diff --git a/CHANGELOG.md b/CHANGELOG.md index c363a6c74..b3fe29620 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Tools: add Firecrawl fallback for `web_fetch` when configured. - Tools: send Chrome-like headers by default for `web_fetch` to improve extraction on bot-sensitive sites. - Tools: Firecrawl fallback now uses bot-circumvention + cache by default; remove basic HTML fallback when extraction fails. +- Status: trim `/status` to current-provider usage only and drop the OAuth/token block. - Directory: unify `clawdbot directory` across channels and plugin channels. - UI: allow deleting sessions from the Control UI. - Skills: add user-invocable skill commands and expanded skill command registration. diff --git a/docs/concepts/usage-tracking.md b/docs/concepts/usage-tracking.md index cff861d6b..77122007b 100644 --- a/docs/concepts/usage-tracking.md +++ b/docs/concepts/usage-tracking.md @@ -11,7 +11,7 @@ read_when: - No estimated costs; only the provider-reported windows. ## Where it shows up -- `/status` in chats: emoji‑rich status card with session tokens + estimated cost (API key only). When OAuth/token profiles exist, the **OAuth/token status block** includes provider usage headers (when available). +- `/status` in chats: emoji‑rich status card with session tokens + estimated cost (API key only). Provider usage shows for the **current model provider** when available. - `/cost on|off` in chats: toggles per‑response usage lines (OAuth shows tokens only). - CLI: `clawdbot status --usage` prints a full per-provider breakdown. - CLI: `clawdbot channels list` prints the same usage snapshot alongside provider config (use `--no-usage` to skip). diff --git a/docs/tools/slash-commands.md b/docs/tools/slash-commands.md index d4c7c6c43..a55d36ac9 100644 --- a/docs/tools/slash-commands.md +++ b/docs/tools/slash-commands.md @@ -58,7 +58,7 @@ They run immediately, are stripped before the model sees the message, and the re Text + native (when enabled): - `/help` - `/commands` -- `/status` (show current status; includes provider usage/quota when available, plus OAuth/token status block when OAuth profiles exist) +- `/status` (show current status; includes provider usage/quota for the current model provider when available) - `/context [list|detail|json]` (explain “context”; `detail` shows per-file + per-tool + per-skill + system prompt size) - `/usage` (alias: `/status`) - `/whoami` (show your sender id; alias: `/id`) @@ -105,7 +105,7 @@ Notes: ## Usage vs cost (what shows where) -- **Provider usage/quota** (example: “Claude 80% left”) shows up in `/status` when provider usage tracking is enabled. +- **Provider usage/quota** (example: “Claude 80% left”) shows up in `/status` for the current model provider when usage tracking is enabled. - **Per-response tokens/cost** is controlled by `/cost on|off` (appended to normal replies). - `/model status` is about **models/auth/endpoints**, not usage. diff --git a/src/auto-reply/reply/commands-status.ts b/src/auto-reply/reply/commands-status.ts index 382b0768e..e69b82037 100644 --- a/src/auto-reply/reply/commands-status.ts +++ b/src/auto-reply/reply/commands-status.ts @@ -8,7 +8,6 @@ import { resolveAuthProfileDisplayLabel, resolveAuthProfileOrder, } from "../../agents/auth-profiles.js"; -import { buildAuthHealthSummary, formatRemainingShort } from "../../agents/auth-health.js"; import { getCustomProviderApiKey, resolveEnvApiKey } from "../../agents/model-auth.js"; import { normalizeProviderId } from "../../agents/model-selection.js"; import type { ClawdbotConfig } from "../../config/config.js"; @@ -16,10 +15,8 @@ import type { SessionEntry, SessionScope } from "../../config/sessions.js"; import { logVerbose } from "../../globals.js"; import { formatUsageSummaryLine, - formatUsageWindowSummary, loadProviderUsageSummary, resolveUsageProviderId, - type UsageProviderId, } from "../../infra/provider-usage.js"; import { normalizeGroupActivation } from "../group-activation.js"; import { buildStatusMessage } from "../status.js"; @@ -134,17 +131,6 @@ export async function buildStatusReply(params: { ? resolveSessionAgentId({ sessionKey, config: cfg }) : resolveDefaultAgentId(cfg); const statusAgentDir = resolveAgentDir(cfg, statusAgentId); - const authStore = ensureAuthProfileStore(statusAgentDir, { allowKeychainPrompt: false }); - const authHealth = buildAuthHealthSummary({ store: authStore, cfg }); - const oauthProfiles = authHealth.profiles.filter( - (profile) => profile.type === "oauth" || profile.type === "token", - ); - - const usageProviders = new Set(); - for (const profile of oauthProfiles) { - const entry = resolveUsageProviderId(profile.provider); - if (entry) usageProviders.add(entry); - } const currentUsageProvider = (() => { try { return resolveUsageProviderId(provider); @@ -152,47 +138,23 @@ export async function buildStatusReply(params: { return undefined; } })(); - if (usageProviders.size === 0 && currentUsageProvider) { - usageProviders.add(currentUsageProvider); - } - const usageByProvider = new Map(); - let usageSummaryCache: Awaited> | null | undefined; - if (usageProviders.size > 0) { + let usageLine: string | null = null; + if (currentUsageProvider) { try { - usageSummaryCache = await loadProviderUsageSummary({ + const usageSummary = await loadProviderUsageSummary({ timeoutMs: 3500, - providers: Array.from(usageProviders), + providers: [currentUsageProvider], agentDir: statusAgentDir, }); - for (const snapshot of usageSummaryCache.providers) { - const formatted = formatUsageWindowSummary(snapshot, { - now: Date.now(), - maxWindows: 2, - includeResets: true, - }); - if (formatted) usageByProvider.set(snapshot.provider, formatted); - } + const summaryLine = formatUsageSummaryLine(usageSummary, { + now: Date.now(), + maxProviders: 1, + }); + if (summaryLine) usageLine = summaryLine; } catch { - // ignore + usageLine = null; } } - - let usageLine: string | null = null; - try { - if (oauthProfiles.length === 0 && currentUsageProvider) { - const summaryLine = usageSummaryCache - ? formatUsageSummaryLine(usageSummaryCache, { now: Date.now(), maxProviders: 1 }) - : null; - if (summaryLine) { - usageLine = summaryLine; - } else { - const usage = usageByProvider.get(currentUsageProvider); - if (usage) usageLine = `📊 Usage: ${usage}`; - } - } - } catch { - usageLine = null; - } const queueSettings = resolveQueueSettings({ cfg, channel: command.channel, @@ -241,44 +203,5 @@ export async function buildStatusReply(params: { includeTranscriptUsage: false, }); - const authStatusLines = (() => { - if (oauthProfiles.length === 0) return []; - const formatStatus = (status: string) => { - if (status === "ok") return "ok"; - if (status === "static") return "static"; - if (status === "expiring") return "expiring"; - if (status === "missing") return "unknown"; - return "expired"; - }; - const profilesByProvider = new Map(); - for (const profile of oauthProfiles) { - const current = profilesByProvider.get(profile.provider); - if (current) current.push(profile); - else profilesByProvider.set(profile.provider, [profile]); - } - const lines: string[] = ["OAuth/token status"]; - for (const [provider, profiles] of profilesByProvider) { - const usageKey = resolveUsageProviderId(provider); - const usage = usageKey ? usageByProvider.get(usageKey) : undefined; - const usageSuffix = usage ? ` — usage: ${usage}` : ""; - lines.push(`- ${provider}${usageSuffix}`); - for (const profile of profiles) { - const labelText = profile.label || profile.profileId; - const status = formatStatus(profile.status); - const expiry = - profile.status === "static" - ? "" - : profile.expiresAt - ? ` expires in ${formatRemainingShort(profile.remainingMs)}` - : " expires unknown"; - const source = profile.source !== "store" ? ` (${profile.source})` : ""; - lines.push(` - ${labelText} ${status}${expiry}${source}`); - } - } - return lines; - })(); - - const fullStatusText = - authStatusLines.length > 0 ? `${statusText}\n\n${authStatusLines.join("\n")}` : statusText; - return { text: fullStatusText }; + return { text: statusText }; }