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 45824208bfe..38d7558dc7f 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx
@@ -11,6 +11,9 @@ import { useKeybind } from "../../context/keybind"
import { useDirectory } from "../../context/directory"
import { useKV } from "../../context/kv"
import { TodoItem } from "../../component/todo-item"
+// kilocode_change start
+import { formatCount, getUsage } from "./usage"
+// kilocode_change end
export function Sidebar(props: { sessionID: string; overlay?: boolean }) {
const sync = useSync()
@@ -60,6 +63,17 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) {
}
})
+ // kilocode_change start
+ const usage = createMemo(() => {
+ const total = getUsage(messages())
+ return {
+ input: formatCount(total.input),
+ output: formatCount(total.output),
+ cached: formatCount(total.cached),
+ }
+ })
+ // kilocode_change end
+
const directory = useDirectory()
const kv = useKV()
@@ -109,6 +123,25 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) {
{context()?.percentage ?? 0}% used
{cost()} spent
+ {/* kilocode_change start */}
+
+
+ Token Usage
+
+
+ Input
+ {usage().input}
+
+
+ Output
+ {usage().output}
+
+
+ Cached
+ {usage().cached}
+
+
+ {/* kilocode_change end */}
0}>
{
+ if (item.role !== "assistant") return sum
+ return {
+ input: sum.input + item.tokens.input,
+ output: sum.output + item.tokens.output,
+ cached: sum.cached + item.tokens.cache.read,
+ }
+ },
+ {
+ input: 0,
+ output: 0,
+ cached: 0,
+ },
+ )
+}
+
+export function formatCount(input: number) {
+ return fmt.format(input)
+}
diff --git a/packages/opencode/test/cli/tui/usage.test.ts b/packages/opencode/test/cli/tui/usage.test.ts
new file mode 100644
index 00000000000..b5f7c74d19e
--- /dev/null
+++ b/packages/opencode/test/cli/tui/usage.test.ts
@@ -0,0 +1,61 @@
+// kilocode_change - new file
+import { describe, expect, test } from "bun:test"
+import type { AssistantMessage, Message, UserMessage } from "@kilocode/sdk/v2"
+import { formatCount, getUsage } from "../../../src/cli/cmd/tui/routes/session/usage"
+
+function assistant(id: string, input: number, output: number, read: number): AssistantMessage {
+ return {
+ id,
+ sessionID: "ses_1",
+ role: "assistant",
+ time: { created: 1 },
+ parentID: "msg_parent",
+ modelID: "claude-sonnet",
+ providerID: "anthropic",
+ mode: "code",
+ agent: "code",
+ path: { cwd: "/tmp", root: "/tmp" },
+ cost: 0,
+ tokens: {
+ input,
+ output,
+ reasoning: 99,
+ cache: {
+ read,
+ write: 7,
+ },
+ },
+ }
+}
+
+function user(): UserMessage {
+ return {
+ id: "msg_user",
+ sessionID: "ses_1",
+ role: "user",
+ time: { created: 0 },
+ agent: "code",
+ model: {
+ providerID: "anthropic",
+ modelID: "claude-sonnet",
+ },
+ }
+}
+
+describe("session usage", () => {
+ test("sums input, output, and cache read across assistant messages only", () => {
+ const msg: Message[] = [user(), assistant("a", 1200, 45, 300), assistant("b", 800, 55, 700)]
+
+ expect(getUsage(msg)).toEqual({
+ input: 2000,
+ output: 100,
+ cached: 1000,
+ })
+ })
+
+ test("formats full counts with thousands separators", () => {
+ expect(formatCount(0)).toBe("0")
+ expect(formatCount(12345)).toBe("12,345")
+ expect(formatCount(9876543)).toBe("9,876,543")
+ })
+})