@@ -34,6 +34,7 @@ import type {
3434 SessionInboxCompaction ,
3535 ShellInfo ,
3636 SkillInfo ,
37+ UsageLimitsInfo ,
3738 VcsInfo ,
3839 OpenCodeEvent ,
3940 OpenCodeClient ,
@@ -103,6 +104,9 @@ type LocationData = {
103104 provider ?: ProviderInfo [ ]
104105 reference ?: ReferenceInfo [ ]
105106 websearch ?: WebSearchProvider [ ]
107+ // The Go allowance and Console budgets of the OpenCode Console workspace the location is signed in to; null when it
108+ // isn't signed in.
109+ usageLimits ?: UsageLimitsInfo | null
106110 // Currently running shell commands for this location, keyed by shell id. Entries are removed
107111 // once the command exits or is deleted, so this only ever holds in-flight shells.
108112 shell ?: Record < string , ShellWithLocation >
@@ -942,6 +946,12 @@ export function createData(config: CreateDataInput) {
942946 return
943947 }
944948 case "session.step.failed" :
949+ // A budget refusal means the cached usage limits are out of date, which is what the refusal sends people to.
950+ if (
951+ event . location &&
952+ ( event . data . error . type === "provider.budget-limit" || event . data . error . type === "provider.model-budget-limit" )
953+ )
954+ refreshUsageLimits ( event . location )
945955 message . editAssistant ( event . data . sessionID , event . data . assistantMessageID , ( assistant ) => {
946956 assistant . time . completed = event . created
947957 assistant . finish = event . data . finish ?? "error"
@@ -1227,6 +1237,7 @@ export function createData(config: CreateDataInput) {
12271237 Object . keys ( store . location ) . forEach ( ( key ) => {
12281238 const ref = JSON . parse ( key ) as [ string , string | null ]
12291239 const location = { directory : ref [ 0 ] , workspaceID : ref [ 1 ] ?? undefined }
1240+ refreshUsageLimits ( location )
12301241 if ( event . type === "credential.updated" ) {
12311242 result . location . integration . invalidate ( location )
12321243 refresh ( ( ) => result . location . integration . sync ( location ) )
@@ -1311,6 +1322,7 @@ export function createData(config: CreateDataInput) {
13111322 refresh ( ( ) => result . location . reference . sync ( location ) )
13121323 break
13131324 case "integration.updated" :
1325+ refreshUsageLimits ( location )
13141326 result . location . integration . invalidate ( location )
13151327 result . location . model . invalidate ( location )
13161328 result . location . provider . invalidate ( location )
@@ -1348,6 +1360,15 @@ export function createData(config: CreateDataInput) {
13481360 // A cached per-location catalog. `sync` loads once per invalidation, keyed by the
13491361 // effective location, and publishes under the server's canonical location; `alias`
13501362 // also publishes under the requested key when the two differ.
1363+ // Reloads usage limits only where something has read them, since each load asks OpenCode Console.
1364+ function refreshUsageLimits ( location : LocationRef ) {
1365+ const loaded =
1366+ store . location [ locationKey ( location ) ] ?. usageLimits !== undefined ||
1367+ sync . has ( `location.usageLimits:${ locationKey ( location ) } ` )
1368+ usageLimits . invalidate ( location )
1369+ if ( loaded ) refresh ( ( ) => usageLimits . sync ( location ) )
1370+ }
1371+
13511372 function locationResource < Field extends keyof LocationData > (
13521373 field : Field ,
13531374 load : ( location : ReturnType < typeof locationQuery > ) => Promise < { location : LocationRef ; data : LocationData [ Field ] } > ,
@@ -1371,6 +1392,7 @@ export function createData(config: CreateDataInput) {
13711392 }
13721393
13731394 const vcs = locationResource ( "vcs" , ( location ) => api ( ) . vcs . get ( { location } ) )
1395+ const usageLimits = locationResource ( "usageLimits" , ( location ) => api ( ) . usage . limits ( { location } ) )
13741396 const shells = locationResource ( "shell" , async ( location ) => {
13751397 const response = await api ( ) . shell . list ( { location } )
13761398 const ref = { directory : response . location . directory }
@@ -1982,6 +2004,7 @@ export function createData(config: CreateDataInput) {
19822004 result . session . form . invalidate ( "global" , location )
19832005 } ,
19842006 vcs : { info : vcs . list , sync : vcs . sync , invalidate : vcs . invalidate } ,
2007+ usage : { limits : usageLimits . list , sync : usageLimits . sync , invalidate : usageLimits . invalidate } ,
19852008 agent : locationResource ( "agent" , ( location ) => api ( ) . agent . list ( { location } ) ) ,
19862009 command : locationResource ( "command" , ( location ) => api ( ) . command . list ( { location } ) ) ,
19872010 config : locationResource ( "config" , async ( location ) => ( {
0 commit comments