diff --git a/app/src/components/chat/TokenUsagePill.tsx b/app/src/components/chat/TokenUsagePill.tsx
new file mode 100644
index 000000000..50badfbeb
--- /dev/null
+++ b/app/src/components/chat/TokenUsagePill.tsx
@@ -0,0 +1,95 @@
+import { useNavigate } from 'react-router-dom';
+
+import { useUsageState } from '../../hooks/useUsageState';
+import { useAppSelector } from '../../store/hooks';
+
+function formatTokens(n: number): string {
+ if (n < 1000) return String(n);
+ if (n < 1_000_000) return `${(n / 1000).toFixed(n < 10_000 ? 1 : 0)}K`;
+ return `${(n / 1_000_000).toFixed(1)}M`;
+}
+
+interface PillSeverity {
+ bg: string;
+ text: string;
+ ring: string;
+ label: string;
+}
+
+function severityFromPct(pct: number): PillSeverity {
+ if (pct >= 0.9) {
+ return {
+ bg: 'bg-coral-50',
+ text: 'text-coral-700',
+ ring: 'ring-coral-200',
+ label: `${Math.round(pct * 100)}%`,
+ };
+ }
+ if (pct >= 0.7) {
+ return {
+ bg: 'bg-amber-50',
+ text: 'text-amber-700',
+ ring: 'ring-amber-200',
+ label: `${Math.round(pct * 100)}%`,
+ };
+ }
+ return {
+ bg: 'bg-sage-50',
+ text: 'text-sage-700',
+ ring: 'ring-sage-200',
+ label: `${Math.round(pct * 100)}%`,
+ };
+}
+
+const TokenUsagePill = () => {
+ const navigate = useNavigate();
+ const sessionTokens = useAppSelector(state => state.chatRuntime.sessionTokenUsage);
+ const { usagePct10h, usagePct7d, isAtLimit, isNearLimit, currentTier, teamUsage } =
+ useUsageState();
+
+ const totalTokens = sessionTokens.inputTokens + sessionTokens.outputTokens;
+ const showSessionCounter = totalTokens > 0;
+
+ const planPct = Math.max(usagePct10h, usagePct7d);
+ const planSeverity = severityFromPct(planPct);
+ const showPlanPill = teamUsage !== null;
+
+ const planTitle = (() => {
+ if (isAtLimit) return 'Usage limit reached — click to top up';
+ if (isNearLimit) return 'Approaching usage limit';
+ return `${currentTier.toLowerCase()} plan — click for details`;
+ })();
+
+ if (!showSessionCounter && !showPlanPill) return null;
+
+ return (
+
+ {showSessionCounter ? (
+
+
+ {formatTokens(totalTokens)}
+
+ ) : null}
+ {showPlanPill ? (
+
+ ) : null}
+
+ );
+};
+
+export default TokenUsagePill;
diff --git a/app/src/pages/Conversations.tsx b/app/src/pages/Conversations.tsx
index 850c922d4..ef625f896 100644
--- a/app/src/pages/Conversations.tsx
+++ b/app/src/pages/Conversations.tsx
@@ -4,6 +4,7 @@ import Markdown from 'react-markdown';
import { useNavigate } from 'react-router-dom';
import { type ChatSendError, chatSendError } from '../chat/chatSendError';
+import TokenUsagePill from '../components/chat/TokenUsagePill';
import UpsellBanner from '../components/upsell/UpsellBanner';
import { dismissBanner, shouldShowBanner } from '../components/upsell/upsellDismissState';
import UsageLimitModal from '../components/upsell/UsageLimitModal';
@@ -1114,6 +1115,7 @@ const Conversations = ({ variant = 'page' }: ConversationsProps = {}) => {
{threads.find(t => t.id === selectedThreadId)?.title ?? 'Select a thread'}
+