@@ -107,7 +107,12 @@ test("the free usage limit opens the Go dialog once, leaves a readable notice, a
107107 id : "opencode" ,
108108 name : "OpenCode" ,
109109 models : {
110- "big-pickle" : { id : "big-pickle" , name : "Big Pickle" , cost : { input : 0 , output : 0 } , limit : { context : 200_000 } } ,
110+ "big-pickle" : {
111+ id : "big-pickle" ,
112+ name : "Big Pickle" ,
113+ cost : { input : 0 , output : 0 } ,
114+ limit : { context : 200_000 } ,
115+ } ,
111116 } ,
112117 } ,
113118 ...( state . authorized
@@ -117,7 +122,12 @@ test("the free usage limit opens the Go dialog once, leaves a readable notice, a
117122 integrationID : "opencode-go" ,
118123 name : "OpenCode Go" ,
119124 models : {
120- "glm-5-go" : { id : "glm-5-go" , name : "GLM-5 Go" , cost : { input : 1 , output : 1 } , limit : { context : 200_000 } } ,
125+ "glm-5-go" : {
126+ id : "glm-5-go" ,
127+ name : "GLM-5 Go" ,
128+ cost : { input : 1 , output : 1 } ,
129+ limit : { context : 200_000 } ,
130+ } ,
121131 } ,
122132 } ,
123133 ]
@@ -131,7 +141,9 @@ test("the free usage limit opens the Go dialog once, leaves a readable notice, a
131141 id : "opencode" ,
132142 name : "OpenCode Console" ,
133143 methods : [ { id : "device" , type : "oauth" , label : "OpenCode Console account" } ] ,
134- connections : state . authorized ? [ { type : "credential" , id : "cred_console" , label : "Anomaly" , method : "oauth" } ] : [ ] ,
144+ connections : state . authorized
145+ ? [ { type : "credential" , id : "cred_console" , label : "Anomaly" , method : "oauth" } ]
146+ : [ ] ,
135147 } ,
136148 {
137149 id : "opencode-go" ,
@@ -261,62 +273,116 @@ test("the free usage limit opens the Go dialog once, leaves a readable notice, a
261273 await expect ( view . editor ) . toHaveText ( "" )
262274} )
263275
264- test ( "the Go usage limit restores the prompt and leaves a muted notice without a dialog" , async ( { page } ) => {
265- await page . clock . setFixedTime ( T0 )
266- const sessionID = "ses_go_limit"
267- const model = { id : "glm-5-go" , providerID : "opencode-go" }
268-
269- const view = await openSession ( page , {
270- name : "GoLimit" ,
271- sessions : [ { id : sessionID , agent : "build" , model } ] ,
272- onFileWrite : ( ) => undefined ,
273- pageMessages : ( ) => ( {
274- items : [
275- { id : "msg_go_user" , type : "user" , text : "Refactor the parser" , time : { created : T0 } } ,
276- { id : "msg_go_assistant" , type : "assistant" , agent : "build" , model, content : [ ] , time : { created : T0 + 1 } } ,
277- ] ,
278- } ) ,
279- } )
280-
281- await expect ( page . getByText ( "Refactor the parser" , { exact : true } ) ) . toBeVisible ( )
276+ for ( const action of [ "tray" , "picker" ] as const ) {
277+ test ( `the Go usage limit restores the prompt and dismisses the tray when switching models through the ${ action } ` , async ( {
278+ page,
279+ context,
280+ } ) => {
281+ await page . clock . setFixedTime ( T0 )
282+ await context . route ( "https://opencode.ai/**" , ( route ) => route . fulfill ( { body : "Go" } ) )
283+ const sessionID = "ses_go_limit"
284+ const model = { id : "glm-5-go" , providerID : "opencode-go" }
285+
286+ const view = await openSession ( page , {
287+ name : "GoLimit" ,
288+ sessions : [ { id : sessionID , agent : "build" , model } ] ,
289+ onFileWrite : ( ) => undefined ,
290+ provider : ( ) => ( {
291+ all : [
292+ {
293+ id : "opencode" ,
294+ name : "OpenCode" ,
295+ models : {
296+ "big-pickle" : {
297+ id : "big-pickle" ,
298+ name : "Big Pickle" ,
299+ cost : { input : 0 , output : 0 } ,
300+ limit : { context : 200_000 } ,
301+ } ,
302+ } ,
303+ } ,
304+ {
305+ id : "opencode-go" ,
306+ name : "OpenCode Go" ,
307+ models : {
308+ "glm-5-go" : {
309+ id : "glm-5-go" ,
310+ name : "GLM-5 Go" ,
311+ cost : { input : 1 , output : 1 } ,
312+ limit : { context : 200_000 } ,
313+ } ,
314+ } ,
315+ } ,
316+ ] ,
317+ connected : [ "opencode" , "opencode-go" ] ,
318+ default : { build : "opencode-go/glm-5-go" } ,
319+ } ) ,
320+ pageMessages : ( ) => ( {
321+ items : [
322+ { id : "msg_go_user" , type : "user" , text : "Refactor the parser" , time : { created : T0 } } ,
323+ { id : "msg_go_assistant" , type : "assistant" , agent : "build" , model, content : [ ] , time : { created : T0 + 1 } } ,
324+ ] ,
325+ } ) ,
326+ } )
282327
283- const durable = ( seq : number ) => ( { aggregateID : sessionID , seq, version : 1 as const } )
284- const location = { directory : view . directory }
328+ await expect ( page . getByText ( "Refactor the parser" , { exact : true } ) ) . toBeVisible ( )
285329
286- const body = {
287- type : "error" ,
288- error : { type : "GoUsageLimitError" , message : "Go usage limit exceeded" } ,
289- metadata : { workspace : "wrk_go_limit" , limitName : "5 hour" } ,
290- }
330+ // The refusal OpenCode Console sends a Go subscriber at a limit, as core records it.
331+ const body = {
332+ type : "error" ,
333+ error : { type : "GoUsageLimitError" , message : "Go usage limit exceeded" } ,
334+ metadata : { workspace : "wrk_go_limit" , limitName : "5 hour" } ,
335+ }
291336
292- await view . push ( [
293- {
294- id : "evt_go_limit_1" ,
295- created : T0 + 2 ,
296- type : "session.step.failed" ,
297- location,
298- durable : durable ( 1 ) ,
299- data : {
300- sessionID,
301- assistantMessageID : "msg_go_assistant" ,
302- error : {
303- type : "provider.go-limit" ,
304- message : "OpenCode Go usage limit reached." ,
305- status : 429 ,
306- response : { body : JSON . stringify ( body ) } ,
307- retryAfterMs : 3_600_000 ,
337+ await view . push ( [
338+ {
339+ id : "evt_go_limit_1" ,
340+ created : T0 + 2 ,
341+ type : "session.step.failed" ,
342+ location : { directory : view . directory } ,
343+ durable : { aggregateID : sessionID , seq : 1 , version : 1 } ,
344+ data : {
345+ sessionID,
346+ assistantMessageID : "msg_go_assistant" ,
347+ error : {
348+ type : "provider.go-limit" ,
349+ message : "OpenCode Go 5 hour usage limit reached." ,
350+ status : 429 ,
351+ response : { body : JSON . stringify ( body ) } ,
352+ retryAfterMs : 3_600_000 ,
353+ } ,
308354 } ,
309355 } ,
310- } ,
311- ] )
356+ ] )
312357
313- await expect (
314- page . getByRole ( "status" ) . filter ( { hasText : / ^ Y o u ' v e r e a c h e d y o u r O p e n C o d e G o u s a g e l i m i t \. I t r e s e t s a t / } ) ,
315- ) . toBeVisible ( )
316- await expect ( view . editor ) . toHaveText ( "Refactor the parser" )
317- await expect ( page . getByText ( "OpenCode Go usage limit reached." ) ) . toHaveCount ( 0 )
318- await expect ( page . getByRole ( "dialog" ) ) . toHaveCount ( 0 )
319- } )
358+ const tray = page . locator ( '[data-component="session-go-limit-tray"]' )
359+ await expect ( tray ) . toContainText ( / G o 5 h o u r u s a g e l i m i t r e a c h e d \. I t r e s e t s a t .+ ?\. / )
360+ await expect (
361+ page . getByRole ( "status" ) . filter ( { hasText : / ^ Y o u ' v e r e a c h e d y o u r O p e n C o d e G o u s a g e l i m i t \. I t r e s e t s a t / } ) ,
362+ ) . toBeVisible ( )
363+ await expect ( view . editor ) . toHaveText ( "Refactor the parser" )
364+ await expect ( page . getByText ( "OpenCode Go 5 hour usage limit reached." ) ) . toHaveCount ( 0 )
365+ await expect ( page . getByRole ( "dialog" ) ) . toHaveCount ( 0 )
366+
367+ // Extra Usage is turned on from the workspace's Go page in OpenCode Console.
368+ const popup = page . waitForEvent ( "popup" )
369+ await tray . getByRole ( "button" , { name : "Turn on Extra Usage" , exact : true } ) . click ( )
370+ await expect ( await popup ) . toHaveURL ( "https://opencode.ai/console/wrk_go_limit/go" )
371+
372+ if ( action === "tray" ) await tray . getByRole ( "button" , { name : "Switch to a free model" , exact : true } ) . click ( )
373+
374+ if ( action === "picker" ) {
375+ await page . locator ( '[data-action="composer-model"]' ) . click ( )
376+ const picker = page . getByRole ( "dialog" , { name : "Select model" , exact : true } )
377+ await picker . getByRole ( "combobox" , { name : "Search models" , exact : true } ) . fill ( "Big Pickle" )
378+ await picker . getByRole ( "option" , { name : / B i g P i c k l e / } ) . click ( )
379+ }
380+
381+ await expect ( page . locator ( '[data-action="composer-model"]' ) ) . toContainText ( "Big Pickle" )
382+ await expect ( tray ) . toHaveCount ( 0 )
383+ await expect ( view . editor ) . toHaveText ( "Refactor the parser" )
384+ } )
385+ }
320386
321387test ( "ChatGPT's usage limit opens the ChatGPT usage dialog" , async ( { page } ) => {
322388 await page . clock . setFixedTime ( T0 )
0 commit comments