From 4507fd6eea0759eb16ce29de8062c308dc892cd8 Mon Sep 17 00:00:00 2001 From: Ryan Vogel Date: Sun, 28 Dec 2025 15:20:15 -0500 Subject: [PATCH 1/6] feat: separate context window and session stats in sidebar Split the sidebar Context section to show current context window (tokens, % used) separately from Session stats (total tokens, spent). This allows users to see both the current LLM context size (which resets on compaction) and cumulative session usage at a glance. --- .../src/cli/cmd/tui/routes/session/sidebar.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx index a9ed042d1bb9..193290130418 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx @@ -7,7 +7,6 @@ import path from "path" import type { AssistantMessage } from "@opencode-ai/sdk/v2" import { Global } from "@/global" import { Installation } from "@/installation" -import { useKeybind } from "../../context/keybind" import { useDirectory } from "../../context/directory" import { useKV } from "../../context/kv" import { TodoItem } from "../../component/todo-item" @@ -60,6 +59,14 @@ export function Sidebar(props: { sessionID: string }) { } }) + const sessionTokens = createMemo(() => { + const total = messages().reduce((sum, x) => { + if (x.role !== "assistant") return sum + return sum + x.tokens.input + x.tokens.output + x.tokens.reasoning + x.tokens.cache.read + x.tokens.cache.write + }, 0) + return total.toLocaleString() + }) + const directory = useDirectory() const kv = useKV() @@ -94,6 +101,12 @@ export function Sidebar(props: { sessionID: string }) { {context()?.tokens ?? 0} tokens {context()?.percentage ?? 0}% used + + + + Session + + {sessionTokens()} total tokens {cost()} spent 0}> From 0931e9d216e6b914e413b435f5b72e722764b662 Mon Sep 17 00:00:00 2001 From: Ryan Vogel Date: Sun, 28 Dec 2025 15:26:35 -0500 Subject: [PATCH 2/6] fix: only count input + output tokens for session total --- packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx index 193290130418..938bd375895e 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx @@ -62,7 +62,7 @@ export function Sidebar(props: { sessionID: string }) { const sessionTokens = createMemo(() => { const total = messages().reduce((sum, x) => { if (x.role !== "assistant") return sum - return sum + x.tokens.input + x.tokens.output + x.tokens.reasoning + x.tokens.cache.read + x.tokens.cache.write + return sum + x.tokens.input + x.tokens.output }, 0) return total.toLocaleString() }) From b9c9688c8ed3cf2bb7a9ab1216b67bb6b4e25332 Mon Sep 17 00:00:00 2001 From: Ryan Vogel Date: Sun, 28 Dec 2025 15:30:09 -0500 Subject: [PATCH 3/6] fix: track peak tokens before compactions and show context limit - Session tokens now sums peak context size before each compaction - Shows context limit alongside percentage (e.g. '25% used of 200k') --- .../cli/cmd/tui/routes/session/sidebar.tsx | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx index 938bd375895e..d8c4bf2100e6 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx @@ -53,17 +53,30 @@ export function Sidebar(props: { sessionID: string }) { const total = last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write const model = sync.data.provider.find((x) => x.id === last.providerID)?.models[last.modelID] + const limit = model?.limit.context ?? 0 return { tokens: total.toLocaleString(), - percentage: model?.limit.context ? Math.round((total / model.limit.context) * 100) : null, + percentage: limit ? Math.round((total / limit) * 100) : null, + limit: limit ? (limit >= 1000000 ? `${(limit / 1000000).toFixed(1)}M` : `${Math.round(limit / 1000)}k`) : null, } }) const sessionTokens = createMemo(() => { - const total = messages().reduce((sum, x) => { - if (x.role !== "assistant") return sum - return sum + x.tokens.input + x.tokens.output - }, 0) + const msgs = messages() + let total = 0 + let peakBeforeCompaction = 0 + for (const msg of msgs) { + if (msg.role !== "assistant") continue + const assistant = msg as AssistantMessage + const tokens = assistant.tokens.input + assistant.tokens.output + if (assistant.summary) { + total += peakBeforeCompaction + peakBeforeCompaction = tokens + } else { + peakBeforeCompaction = tokens + } + } + total += peakBeforeCompaction return total.toLocaleString() }) @@ -100,7 +113,9 @@ export function Sidebar(props: { sessionID: string }) { Context {context()?.tokens ?? 0} tokens - {context()?.percentage ?? 0}% used + + {context()?.percentage ?? 0}% used{context()?.limit ? ` of ${context()!.limit}` : ""} + From 031d86b8bbec5f461566aa39bde1cb46e05e2c87 Mon Sep 17 00:00:00 2001 From: Ryan Vogel Date: Sun, 28 Dec 2025 15:31:23 -0500 Subject: [PATCH 4/6] fix: remove session total tokens, keep only spent --- .../cli/cmd/tui/routes/session/sidebar.tsx | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx index d8c4bf2100e6..3c9036f5ad7d 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx @@ -61,25 +61,6 @@ export function Sidebar(props: { sessionID: string }) { } }) - const sessionTokens = createMemo(() => { - const msgs = messages() - let total = 0 - let peakBeforeCompaction = 0 - for (const msg of msgs) { - if (msg.role !== "assistant") continue - const assistant = msg as AssistantMessage - const tokens = assistant.tokens.input + assistant.tokens.output - if (assistant.summary) { - total += peakBeforeCompaction - peakBeforeCompaction = tokens - } else { - peakBeforeCompaction = tokens - } - } - total += peakBeforeCompaction - return total.toLocaleString() - }) - const directory = useDirectory() const kv = useKV() @@ -121,7 +102,6 @@ export function Sidebar(props: { sessionID: string }) { Session - {sessionTokens()} total tokens {cost()} spent 0}> From e21d88c27f38692fad2e5e95c83d1e91be206dda Mon Sep 17 00:00:00 2001 From: Ryan Vogel Date: Sun, 28 Dec 2025 15:35:31 -0500 Subject: [PATCH 5/6] feat: add session total tokens summing all API calls Sums tokens.input + tokens.output across all assistant messages, which represents total tokens billed across all API calls. --- .../src/cli/cmd/tui/routes/session/sidebar.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx index 3c9036f5ad7d..e65da32e294d 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx @@ -61,6 +61,15 @@ export function Sidebar(props: { sessionID: string }) { } }) + const sessionTokens = createMemo(() => { + const total = messages().reduce((sum, msg) => { + if (msg.role !== "assistant") return sum + const assistant = msg as AssistantMessage + return sum + assistant.tokens.input + assistant.tokens.output + }, 0) + return total.toLocaleString() + }) + const directory = useDirectory() const kv = useKV() @@ -102,6 +111,7 @@ export function Sidebar(props: { sessionID: string }) { Session + {sessionTokens()} total tokens {cost()} spent 0}> From b5eab4fd4b87c459ce4710175b799faee62b1381 Mon Sep 17 00:00:00 2001 From: Ryan Vogel Date: Sun, 28 Dec 2025 15:39:13 -0500 Subject: [PATCH 6/6] fix: simplify context calculation to input + output only Remove reasoning and cache tokens from context calculation since: - reasoning tokens are internal to the model, not context window - cache tokens are metadata about caching, not additional tokens --- packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx index e65da32e294d..297467ba5122 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx @@ -50,8 +50,7 @@ export function Sidebar(props: { sessionID: string }) { const context = createMemo(() => { const last = messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage if (!last) return - const total = - last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write + const total = last.tokens.input + last.tokens.output const model = sync.data.provider.find((x) => x.id === last.providerID)?.models[last.modelID] const limit = model?.limit.context ?? 0 return {