File tree Expand file tree Collapse file tree
apps/code/src/renderer/features
message-editor/components
packages/agent/src/adapters/claude Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1+ import { Text , Tooltip } from "@radix-ui/themes" ;
2+ import { useContextUsageForTask } from "@renderer/features/sessions/hooks/useSession" ;
3+
4+ const CONTEXT_WARNING_THRESHOLD_PCT = 40 ;
5+
6+ interface ContextUsageIndicatorProps {
7+ taskId ?: string ;
8+ }
9+
10+ export function ContextUsageIndicator ( { taskId } : ContextUsageIndicatorProps ) {
11+ const contextUsage = useContextUsageForTask ( taskId ) ;
12+ if ( ! contextUsage || contextUsage . size <= 0 ) return null ;
13+
14+ const percent = Math . round ( ( contextUsage . used / contextUsage . size ) * 100 ) ;
15+
16+ if ( percent < CONTEXT_WARNING_THRESHOLD_PCT ) return null ;
17+
18+ return (
19+ < Tooltip
20+ content = { `Context: ${ percent } % used (${ Math . round ( contextUsage . used / 1000 ) } k / ${ Math . round ( contextUsage . size / 1000 ) } k tokens)` }
21+ >
22+ < Text
23+ size = "1"
24+ style = { {
25+ color : getContextColor ( percent ) ,
26+ fontFamily : "var(--font-mono)" ,
27+ padding : "4px 10px" ,
28+ cursor : "default" ,
29+ } }
30+ >
31+ { percent } %
32+ </ Text >
33+ </ Tooltip >
34+ ) ;
35+ }
36+
37+ function getContextColor ( percent : number ) : string {
38+ if ( percent >= 80 ) return "var(--red-9)" ;
39+ if ( percent >= 50 ) return "var(--yellow-11)" ;
40+ return "var(--green-9)" ;
41+ }
Original file line number Diff line number Diff line change @@ -3,6 +3,7 @@ import { Paperclip } from "@phosphor-icons/react";
33import { Flex , IconButton , Tooltip } from "@radix-ui/themes" ;
44import { useRef } from "react" ;
55import type { FileAttachment } from "../utils/content" ;
6+ import { ContextUsageIndicator } from "./ContextUsageIndicator" ;
67
78interface EditorToolbarProps {
89 disabled ?: boolean ;
@@ -69,6 +70,7 @@ export function EditorToolbar({
6970 { ! hideSelectors && (
7071 < ModelSelector taskId = { taskId } adapter = { adapter } disabled = { disabled } />
7172 ) }
73+ < ContextUsageIndicator taskId = { taskId } />
7274 </ Flex >
7375 ) ;
7476}
Original file line number Diff line number Diff line change @@ -147,6 +147,25 @@ export const useThoughtLevelConfigOptionForTask = (
147147 return useConfigOptionForTask ( taskId , "thought_level" ) ;
148148} ;
149149
150+ /** Get context window usage for a task (used / size) */
151+ export const useContextUsageForTask = (
152+ taskId : string | undefined ,
153+ ) : { used : number ; size : number } | undefined => {
154+ return useSessionStore ( ( s ) => {
155+ if ( ! taskId ) return undefined ;
156+ const taskRunId = s . taskIdIndex [ taskId ] ;
157+ if ( ! taskRunId ) return undefined ;
158+ const session = s . sessions [ taskRunId ] ;
159+ if (
160+ session ?. contextUsed === undefined ||
161+ session ?. contextSize === undefined
162+ ) {
163+ return undefined ;
164+ }
165+ return { used : session . contextUsed , size : session . contextSize } ;
166+ } ) ;
167+ } ;
168+
150169/** Get the adapter type for a task */
151170export const useAdapterForTask = (
152171 taskId : string | undefined ,
Original file line number Diff line number Diff line change @@ -911,6 +911,23 @@ export class SessionService {
911911 setPersistedConfigOptions ( taskRunId , configOptions ) ;
912912 log . info ( "Session config options updated" , { taskRunId } ) ;
913913 }
914+
915+ // Handle context usage updates
916+ if ( params ?. update ?. sessionUpdate === "usage_update" ) {
917+ const update = params . update as {
918+ used ?: number ;
919+ size ?: number ;
920+ } ;
921+ if (
922+ typeof update . used === "number" &&
923+ typeof update . size === "number"
924+ ) {
925+ sessionStoreSetters . updateSession ( taskRunId , {
926+ contextUsed : update . used ,
927+ contextSize : update . size ,
928+ } ) ;
929+ }
930+ }
914931 }
915932
916933 // Handle _posthog/sdk_session notifications for adapter info
Original file line number Diff line number Diff line change @@ -73,6 +73,10 @@ export interface AgentSession {
7373 /** Number of session/prompt events to skip from polled logs (set during resume) */
7474 skipPolledPromptCount ?: number ;
7575 optimisticItems : OptimisticItem [ ] ;
76+ /** Context window tokens used (from usage_update) */
77+ contextUsed ?: number ;
78+ /** Context window total size in tokens (from usage_update) */
79+ contextSize ?: number ;
7680}
7781
7882// --- Config Option Helpers ---
Original file line number Diff line number Diff line change @@ -346,6 +346,11 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
346346 ? Math . min ( ...contextWindows )
347347 : getDefaultContextWindow ( this . session . modelId ?? "" ) ;
348348
349+ this . session . contextSize = contextWindowSize ;
350+ if ( lastAssistantTotalUsage !== null ) {
351+ this . session . contextUsed = lastAssistantTotalUsage ;
352+ }
353+
349354 // Send usage_update notification
350355 if ( lastAssistantTotalUsage !== null ) {
351356 await this . client . sessionUpdate ( {
Original file line number Diff line number Diff line change @@ -53,6 +53,10 @@ export type Session = BaseSession & {
5353 effort ?: EffortLevel ;
5454 configOptions : SessionConfigOption [ ] ;
5555 accumulatedUsage : AccumulatedUsage ;
56+ /** Latest context window usage (total tokens from last assistant message) */
57+ contextUsed ?: number ;
58+ /** Context window size in tokens */
59+ contextSize ?: number ;
5660 promptRunning : boolean ;
5761 pendingMessages : Map < string , PendingMessage > ;
5862 nextPendingOrder : number ;
You can’t perform that action at this time.
0 commit comments