@@ -3,6 +3,7 @@ import { describe, expect } from "bun:test"
33import type { LanguageModelV3 } from "@ai-sdk/provider"
44import { Effect } from "effect"
55import { Catalog } from "@opencode-ai/core/catalog"
6+ import { Credential } from "@opencode-ai/core/credential"
67import { Integration } from "@opencode-ai/core/integration"
78import { ModelV2 } from "@opencode-ai/core/model"
89import { PluginV2 } from "@opencode-ai/core/plugin"
@@ -27,6 +28,20 @@ function required<T>(value: T | undefined): T {
2728 return value
2829}
2930
31+ function eventually < A > (
32+ effect : Effect . Effect < A > ,
33+ predicate : ( value : A ) => boolean ,
34+ remaining = 1000 ,
35+ ) : Effect . Effect < A , Error > {
36+ return Effect . gen ( function * ( ) {
37+ const value = yield * effect
38+ if ( predicate ( value ) ) return value
39+ if ( remaining === 0 ) return yield * Effect . fail ( new Error ( "Timed out waiting for value" ) )
40+ yield * Effect . promise ( ( ) => Bun . sleep ( 1 ) )
41+ return yield * eventually ( effect , predicate , remaining - 1 )
42+ } )
43+ }
44+
3045function fakeSelectorSdk ( calls : string [ ] ) {
3146 const make = ( method : string ) => ( id : string ) => {
3247 calls . push ( `${ method } :${ id } ` )
@@ -153,6 +168,80 @@ describe("OpenAIPlugin", () => {
153168 } ) ,
154169 )
155170
171+ it . effect ( "filters the OpenAI catalog to codex-eligible models under a ChatGPT connection" , ( ) =>
172+ Effect . gen ( function * ( ) {
173+ const catalog = yield * Catalog . Service
174+ const credentials = yield * Credential . Service
175+ yield * catalog . transform ( ( catalog ) => {
176+ const item = ProviderV2 . Info . make ( {
177+ ...ProviderV2 . Info . empty ( ProviderV2 . ID . openai ) ,
178+ api : { type : "aisdk" , package : "@ai-sdk/openai" } ,
179+ } )
180+ catalog . provider . update ( item . id , ( draft ) => {
181+ draft . api = item . api
182+ } )
183+ catalog . model . update ( item . id , ModelV2 . ID . make ( "gpt-5.5" ) , ( model ) => {
184+ model . cost = [ { input : 1 , output : 2 , cache : { read : 0.1 , write : 0 } } ]
185+ } )
186+ catalog . model . update ( item . id , ModelV2 . ID . make ( "gpt-5.5-pro" ) , ( ) => { } )
187+ catalog . model . update ( item . id , ModelV2 . ID . make ( "gpt-4.1" ) , ( ) => { } )
188+ } )
189+ yield * credentials . create ( {
190+ integrationID : Integration . ID . make ( "openai" ) ,
191+ value : Credential . OAuth . make ( {
192+ type : "oauth" ,
193+ methodID : Integration . MethodID . make ( "chatgpt-browser" ) ,
194+ access : "chatgpt-token" ,
195+ refresh : "refresh" ,
196+ expires : Date . now ( ) + 60_000 ,
197+ metadata : { accountID : "acct_123" } ,
198+ } ) ,
199+ } )
200+ yield * addPlugin ( )
201+
202+ const eligible = required (
203+ yield * eventually (
204+ catalog . model . get ( ProviderV2 . ID . openai , ModelV2 . ID . make ( "gpt-5.5" ) ) ,
205+ ( model ) => model ?. cost . length === 0 ,
206+ ) ,
207+ )
208+ expect ( eligible . enabled ) . toBe ( true )
209+ expect ( required ( yield * catalog . model . get ( ProviderV2 . ID . openai , ModelV2 . ID . make ( "gpt-5.5-pro" ) ) ) . enabled ) . toBe (
210+ false ,
211+ )
212+ expect ( required ( yield * catalog . model . get ( ProviderV2 . ID . openai , ModelV2 . ID . make ( "gpt-4.1" ) ) ) . enabled ) . toBe ( false )
213+ } ) ,
214+ )
215+
216+ it . effect ( "keeps the full OpenAI catalog under an API key connection" , ( ) =>
217+ Effect . gen ( function * ( ) {
218+ const catalog = yield * Catalog . Service
219+ const credentials = yield * Credential . Service
220+ yield * catalog . transform ( ( catalog ) => {
221+ const item = ProviderV2 . Info . make ( {
222+ ...ProviderV2 . Info . empty ( ProviderV2 . ID . openai ) ,
223+ api : { type : "aisdk" , package : "@ai-sdk/openai" } ,
224+ } )
225+ catalog . provider . update ( item . id , ( draft ) => {
226+ draft . api = item . api
227+ } )
228+ catalog . model . update ( item . id , ModelV2 . ID . make ( "gpt-5.5" ) , ( ) => { } )
229+ catalog . model . update ( item . id , ModelV2 . ID . make ( "gpt-4.1" ) , ( ) => { } )
230+ } )
231+ yield * credentials . create ( {
232+ integrationID : Integration . ID . make ( "openai" ) ,
233+ value : Credential . Key . make ( { type : "key" , key : "sk-test" } ) ,
234+ } )
235+ yield * addPlugin ( )
236+ // The connection refresh is asynchronous; give it time to settle before
237+ // asserting nothing was filtered.
238+ yield * Effect . promise ( ( ) => Bun . sleep ( 25 ) )
239+
240+ expect ( required ( yield * catalog . model . get ( ProviderV2 . ID . openai , ModelV2 . ID . make ( "gpt-5.5" ) ) ) . enabled ) . toBe ( true )
241+ expect ( required ( yield * catalog . model . get ( ProviderV2 . ID . openai , ModelV2 . ID . make ( "gpt-4.1" ) ) ) . enabled ) . toBe ( true )
242+ } ) ,
243+ )
244+
156245 it . effect ( "does not disable gpt-5-chat-latest for non-OpenAI providers" , ( ) =>
157246 Effect . gen ( function * ( ) {
158247 const catalog = yield * Catalog . Service
0 commit comments