summaryrefslogtreecommitdiffhomepage
path: root/.dispatch
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-10-14 17:04:35 +0900
committerAdam Malczewski <[email protected]>2026-10-14 17:04:35 +0900
commit10e78065201c8387494b8aafabb987da87183b3a (patch)
treef038f368459acbd3b817974100fb4bd4817ecbff /.dispatch
parent551a7a050e8cd25e795026a1060c9d5b5ea028bf (diff)
downloaddispatch-web-10e78065201c8387494b8aafabb987da87183b3a.tar.gz
dispatch-web-10e78065201c8387494b8aafabb987da87183b3a.zip
feat(account-usage): sidebar Usage view with Claude/Codex quota bars + on-track pace markers
Consumes GET /usage (transport-contract 0.25.0). DaisyUI progress bars per quota window with a dot at the elapsed fraction of the window (the 'on track' level), ahead/under-pace labels, reset countdowns, stale/unavailable/not-configured states.
Diffstat (limited to '.dispatch')
-rw-r--r--.dispatch/transport-contract.reference.md117
1 files changed, 117 insertions, 0 deletions
diff --git a/.dispatch/transport-contract.reference.md b/.dispatch/transport-contract.reference.md
index 0a3a347..043c95b 100644
--- a/.dispatch/transport-contract.reference.md
+++ b/.dispatch/transport-contract.reference.md
@@ -5,6 +5,9 @@
> permission prompt). Your CODE still imports `@dispatch/transport-contract` normally — this file is for
> READING only.
>
+> **2026-10-14 delta (account-usage — ADDITIVE, `0.25.0`):** `GET /usage` + `GET /usage/:provider` and the
+> `AccountUsage*` types (verbatim at the end of this file).
+>
> **Orchestrator:** SNAPSHOT of `[email protected]` (MCP status + computers + provider concurrency + cancel queued message). Regenerate whenever
> it changes.
>
@@ -1253,3 +1256,117 @@ ALWAYS route to the self-hosted Firecrawl client; the tool's `map` mode is REMOV
(service not loaded). The umans entry appears in `available` only when the backend has
`UMANS_API_KEY`; an unconfigured backend is listed but `configured: false` — the UI disables it,
it is never an error.
+
+## Account usage (`[email protected]`, verbatim)
+
+```ts
+// ─── Account usage (Claude / Codex subscription quotas) ───────────────────────
+//
+// Subscription quota usage polled from each provider's first-party account
+// API (Claude `GET /api/oauth/usage`, Codex `GET /backend-api/wham/usage`),
+// cached server-side. Distinct from per-turn token `Usage`: these are the
+// account's plan allowance windows, never estimated from token counts.
+
+/** The subscription accounts the usage service reports on. */
+export type AccountUsageProviderId = "claude" | "codex";
+
+/**
+ * Freshness/availability of one provider's report:
+ * - `fresh` — the last refresh succeeded and is within the cache TTL.
+ * - `stale` — the latest refresh FAILED; `limits` are the last good
+ * report (≤ 24h old), see `fetchedAt` / `error`.
+ * - `unavailable` — refresh failed and there is no last-good report.
+ * - `unsupported` — a credential exists but cannot query subscription usage
+ * (e.g. a plain Anthropic API key).
+ * - `not-configured` — no credential / the auth extension is not loaded.
+ */
+export type AccountUsageState =
+ | "fresh"
+ | "stale"
+ | "unavailable"
+ | "unsupported"
+ | "not-configured";
+
+/** Severity of a single limit: `warning` ≥ 90% used, `exhausted` ≥ 100%. */
+export type AccountUsageLimitStatus = "ok" | "warning" | "exhausted" | "unknown";
+
+/**
+ * One quota meter. Percent meters have `unit: "percent"`, `limit: 100`.
+ * Spend meters (`kind: "spend"`, `unit: "usd"`) may have `limit: null` when the
+ * cap is unlimited/unset — never treat that as a zero-dollar cap.
+ */
+export interface AccountUsageLimit {
+ /** Stable id, e.g. `claude:5h`, `claude:7d:opus`, `codex:primary`, `codex:spark:secondary`. */
+ readonly id: string;
+ /** Human label, e.g. "5 Hour", "7 Day (Opus)", "Spark · 5 hours". */
+ readonly label: string;
+ readonly kind: "window" | "spend";
+ /** Window id, e.g. `5h`, `7d`, `30d`, `extra`. */
+ readonly windowId: string;
+ /** True for account-wide windows that gate every request. */
+ readonly shared: boolean;
+ /** Model family / metered-feature slug for scoped meters, else null. */
+ readonly tier: string | null;
+ /** Window length in ms (null when unknown, e.g. spend meters). */
+ readonly durationMs: number | null;
+ /** Epoch-ms when the window resets (null when unknown). */
+ readonly resetsAt: number | null;
+ readonly unit: "percent" | "usd";
+ readonly used: number;
+ readonly limit: number | null;
+ readonly remaining: number | null;
+ /** 0..1 (null when there is no cap). */
+ readonly usedFraction: number | null;
+ readonly remainingFraction: number | null;
+ readonly status: AccountUsageLimitStatus;
+}
+
+/** Codex paid-credit details (from `/wham/usage` `credits` + `spend_control`). */
+export interface AccountUsageCredits {
+ readonly hasCredits: boolean | null;
+ readonly unlimited: boolean | null;
+ readonly overageLimitReached: boolean | null;
+ /** Remaining credit balance as reported (string or number upstream; normalized to number). */
+ readonly balance: number | null;
+ readonly spendControlReached: boolean | null;
+}
+
+/** One provider's usage report as served to clients. Never carries credentials. */
+export interface AccountUsageReport {
+ readonly provider: AccountUsageProviderId;
+ /** Display name: "Claude" / "Codex". */
+ readonly label: string;
+ readonly state: AccountUsageState;
+ readonly account: {
+ readonly accountId: string | null;
+ readonly email: string | null;
+ readonly planType: string | null;
+ } | null;
+ readonly limits: readonly AccountUsageLimit[];
+ /**
+ * Plan-level verdict (Codex): `limitReached` = plan allowance consumed;
+ * `canServe` = the account can still serve requests (credits may fund
+ * overage). Null when unknown (Claude reports per-window only).
+ */
+ readonly limitReached: boolean | null;
+ readonly canServe: boolean | null;
+ /** True when Codex credits fund requests past an exhausted plan window. */
+ readonly creditOverage: boolean;
+ readonly credits: AccountUsageCredits | null;
+ /** Epoch-ms of the report's data (last successful fetch), null if never. */
+ readonly fetchedAt: number | null;
+ /** Epoch-ms of the last refresh attempt (success or failure), null if never. */
+ readonly lastAttemptAt: number | null;
+ /** Epoch-ms after which the server will refresh on the next request. */
+ readonly nextRefreshAt: number | null;
+ /** Last refresh failure message (sanitized, no secrets), null on success. */
+ readonly error: string | null;
+}
+
+/** Response of `GET /usage` — one report per provider, always both, in fixed order (claude, codex). */
+export interface AccountUsageResponse {
+ readonly providers: readonly AccountUsageReport[];
+ /** Server clock (epoch-ms) when this response was built — use it to compute window progress. */
+ readonly servedAt: number;
+}
+```