11import fs from "node:fs" ;
22import { createTestPluginApi } from "openclaw/plugin-sdk/plugin-test-api" ;
3- import { describe , expect , it , vi } from "vitest" ;
3+ import { beforeEach , describe , expect , it , vi } from "vitest" ;
44import { createCodexAppServerAgentHarness } from "./harness.js" ;
55import plugin from "./index.js" ;
66
77const runCodexAppServerAttemptMock = vi . hoisted ( ( ) => vi . fn ( ) ) ;
88const runCodexAppServerSideQuestionMock = vi . hoisted ( ( ) => vi . fn ( ) ) ;
9+ const getSharedCodexAppServerClientMock = vi . hoisted ( ( ) => vi . fn ( ) ) ;
10+ const readCodexAppServerBindingMock = vi . hoisted ( ( ) => vi . fn ( ) ) ;
11+ const resolveCodexAppServerAuthProfileIdForAgentMock = vi . hoisted ( ( ) =>
12+ vi . fn ( ( params : { authProfileId ?: string } ) => params . authProfileId ) ,
13+ ) ;
14+ const resolveCodexAppServerRuntimeOptionsMock = vi . hoisted ( ( ) =>
15+ vi . fn ( ( ) => ( { start : { command : "codex" , args : [ "app-server" ] } } ) ) ,
16+ ) ;
917
1018vi . mock ( "./src/app-server/run-attempt.js" , ( ) => ( {
1119 runCodexAppServerAttempt : runCodexAppServerAttemptMock ,
1220} ) ) ;
1321vi . mock ( "./src/app-server/side-question.js" , ( ) => ( {
1422 runCodexAppServerSideQuestion : runCodexAppServerSideQuestionMock ,
1523} ) ) ;
24+ vi . mock ( "./src/app-server/shared-client.js" , ( ) => ( {
25+ getSharedCodexAppServerClient : getSharedCodexAppServerClientMock ,
26+ } ) ) ;
27+ vi . mock ( "./src/app-server/config.js" , ( ) => ( {
28+ resolveCodexAppServerRuntimeOptions : resolveCodexAppServerRuntimeOptionsMock ,
29+ } ) ) ;
30+ vi . mock ( "./src/app-server/auth-bridge.js" , ( ) => ( {
31+ resolveCodexAppServerAuthProfileIdForAgent : resolveCodexAppServerAuthProfileIdForAgentMock ,
32+ } ) ) ;
33+ vi . mock ( "./src/app-server/session-binding.js" , ( ) => ( {
34+ readCodexAppServerBinding : readCodexAppServerBindingMock ,
35+ } ) ) ;
1636
1737function mockCall ( mock : { mock : { calls : unknown [ ] [ ] } } , index = 0 ) {
1838 return mock . mock . calls . at ( index ) ;
@@ -23,6 +43,10 @@ function mockCallArg(mock: { mock: { calls: unknown[][] } }, index = 0, argIndex
2343}
2444
2545describe ( "codex plugin" , ( ) => {
46+ beforeEach ( ( ) => {
47+ vi . clearAllMocks ( ) ;
48+ } ) ;
49+
2650 it ( "is opt-in by default" , ( ) => {
2751 const manifest = JSON . parse (
2852 fs . readFileSync ( new URL ( "./openclaw.plugin.json" , import . meta. url ) , "utf8" ) ,
@@ -75,6 +99,7 @@ describe("codex plugin", () => {
7599 expect ( agentHarnessRegistration . deliveryDefaults ) . toEqual ( {
76100 sourceVisibleReplies : "message_tool" ,
77101 } ) ;
102+ expect ( typeof agentHarnessRegistration . prewarm ) . toBe ( "function" ) ;
78103 expect ( typeof agentHarnessRegistration . dispose ) . toBe ( "function" ) ;
79104 expect ( mediaProviderRegistration ?. id ) . toBe ( "codex" ) ;
80105 expect ( mediaProviderRegistration ?. capabilities ) . toEqual ( [ "image" ] ) ;
@@ -96,6 +121,45 @@ describe("codex plugin", () => {
96121 expect ( typeof bindingResolvedRegistration ?. [ 0 ] ) . toBe ( "function" ) ;
97122 } ) ;
98123
124+ it ( "prewarms existing sessions with the bound app-server auth profile" , async ( ) => {
125+ readCodexAppServerBindingMock . mockResolvedValueOnce ( { authProfileId : "openai-codex:work" } ) ;
126+ getSharedCodexAppServerClientMock . mockResolvedValueOnce ( { } ) ;
127+ const harness = createCodexAppServerAgentHarness ( { pluginConfig : { appServer : { } } } ) ;
128+
129+ await expect (
130+ harness . prewarm ?.( {
131+ cfg : { agents : { list : [ ] } } as never ,
132+ agentDir : "/tmp/openclaw-agent" ,
133+ provider : "codex" ,
134+ modelId : "gpt-5.5" ,
135+ sessionKey : "agent:main:main" ,
136+ sessionFile : "/tmp/openclaw-session.jsonl" ,
137+ reason : "tui-startup" ,
138+ } ) ,
139+ ) . resolves . toEqual ( { warmed : true } ) ;
140+
141+ expect ( resolveCodexAppServerRuntimeOptionsMock ) . toHaveBeenCalledWith ( {
142+ pluginConfig : { appServer : { } } ,
143+ } ) ;
144+ expect ( readCodexAppServerBindingMock ) . toHaveBeenCalledWith ( "/tmp/openclaw-session.jsonl" , {
145+ agentDir : "/tmp/openclaw-agent" ,
146+ config : { agents : { list : [ ] } } ,
147+ } ) ;
148+ expect ( resolveCodexAppServerAuthProfileIdForAgentMock ) . toHaveBeenCalledWith ( {
149+ authProfileId : "openai-codex:work" ,
150+ agentDir : "/tmp/openclaw-agent" ,
151+ config : { agents : { list : [ ] } } ,
152+ } ) ;
153+ expect ( getSharedCodexAppServerClientMock ) . toHaveBeenCalledWith ( {
154+ startOptions : { command : "codex" , args : [ "app-server" ] } ,
155+ timeoutMs : 60_000 ,
156+ agentDir : "/tmp/openclaw-agent" ,
157+ authProfileId : "openai-codex:work" ,
158+ config : { agents : { list : [ ] } } ,
159+ } ) ;
160+ expect ( runCodexAppServerAttemptMock ) . not . toHaveBeenCalled ( ) ;
161+ } ) ;
162+
99163 it ( "registers with capture APIs that do not expose conversation binding hooks yet" , ( ) => {
100164 const registerProvider = vi . fn ( ) ;
101165 const api = createTestPluginApi ( {
0 commit comments