Show estimated thread usage in /status (#38281)

## What changed

- Extend `account/usage/read` with an optional `threadId` request and a
  backward-compatible `threadUsage` response containing estimated credits,
  optional USD cost, and model, reasoning, speed, and token breakdowns.
- Fetch thread usage for eligible Business and Enterprise plans when `/status`
  is opened, then update the rendered status card asynchronously.
- Preserve terminal scrollback when replacing a visible status-card tail, and
  append a refreshed card when the original tail can no longer be updated.

## Testing

- Add app-server coverage for authentication, canonical thread IDs,
  unavailable billing routes, and malformed IDs.
- Add TUI coverage for usage formatting, stale responses, retries, history
  updates, terminal reflow, and scrollback preservation.

GitOrigin-RevId: 49300914b3fba917c0f08e8595363e04e5ba6d37
This commit is contained in:
Felipe Coury authored and copyberry committed 2026-08-13 00:30:08 +00:00
1 parent 27a98dde4d
commit f1a1fce26a
55 files changed
+2903 -60

No files matched your search

@@ -2062,7 +2062,14 @@
"type": "string"
},
"params": {
"type": "null"
"anyOf": [
{
"$ref": "#/definitions/v2/GetAccountTokenUsageParams"
},
{
"type": "null"
}
]
}
},
"required": [
@@ -11377,6 +11384,18 @@
"title": "GetAccountResponse",
"type": "object"
},
"GetAccountTokenUsageParams": {
"properties": {
"threadId": {
"description": "When present, read estimated usage for this thread instead of account-wide token activity.",
"type": [
"string",
"null"
]
}
},
"type": "object"
},
"GetAccountTokenUsageResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -11391,6 +11410,18 @@
},
"summary": {
"$ref": "#/definitions/v2/AccountTokenUsageSummary"
},
"threadUsage": {
"anyOf": [
{
"$ref": "#/definitions/v2/ThreadUsage"
},
{
"type": "null"
}
],
"default": null,
"description": "Estimated usage when a thread was requested and its billing route is available."
}
},
"required": [
@@ -14253,6 +14284,18 @@
],
"type": "string"
},
"NullableGetAccountTokenUsageParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"anyOf": [
{
"$ref": "#/definitions/v2/GetAccountTokenUsageParams"
},
{
"type": "null"
}
],
"title": "Nullable_GetAccountTokenUsageParams"
},
"OverriddenMetadata": {
"properties": {
"effectiveValue": true,
@@ -21817,6 +21860,101 @@
],
"type": "string"
},
"ThreadUsage": {
"properties": {
"estimatedUsageCreditsMicros": {
"format": "int64",
"type": "integer"
},
"estimatedUsageUsdMicros": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"groups": {
"items": {
"$ref": "#/definitions/v2/ThreadUsageBreakdownGroup"
},
"type": "array"
},
"threadId": {
"type": "string"
}
},
"required": [
"estimatedUsageCreditsMicros",
"groups",
"threadId"
],
"type": "object"
},
"ThreadUsageBreakdownGroup": {
"properties": {
"cachedInputTokens": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"estimatedUsageCreditsMicros": {
"format": "int64",
"type": "integer"
},
"inputTokens": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"model": {
"type": [
"string",
"null"
]
},
"netNewInputTokens": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"outputTokens": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"reasoningEffort": {
"type": [
"string",
"null"
]
},
"speed": {
"type": [
"string",
"null"
]
},
"totalTokens": {
"format": "int64",
"type": [
"integer",
"null"
]
}
},
"required": [
"estimatedUsageCreditsMicros"
],
"type": "object"
},
"TokenUsageBreakdown": {
"properties": {
"cacheWriteInputTokens": {