Skip to content

fix(ui): show current context tokens instead of cumulative in Control UI#48144

Closed
imwyvern wants to merge 3 commits intoopenclaw:mainfrom
imwyvern:fix/control-ui-token-display
Closed

fix(ui): show current context tokens instead of cumulative in Control UI#48144
imwyvern wants to merge 3 commits intoopenclaw:mainfrom
imwyvern:fix/control-ui-token-display

Conversation

@imwyvern
Copy link
Copy Markdown
Contributor

Summary

Fixes #47885.

The Control UI /usage slash-command displayed cumulative token counts instead of the current context snapshot, making context usage appear much higher than reality (e.g. 353k/200k at 100% vs TUI showing 95k/200k at 47%).

Changes

  • ui/src/ui/chat/slash-command-executor.ts: Use the latest context-window snapshot (lastContextTokens) instead of the cumulative totalTokensIn + totalTokensOut. When no snapshot is available, display n/a instead of a misleading cumulative figure.
  • ui/src/ui/chat/slash-command-executor.node.test.ts: Added regression tests covering both fresh-snapshot and stale/missing-snapshot scenarios.

Verification

PATH=/opt/homebrew/opt/node@22/bin:$PATH npx vitest run --config vitest.node.config.ts src/ui/chat/slash-command-executor.node.test.ts

Type

  • Bug fix (non-breaking change which fixes an issue)

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 16, 2026

Greptile Summary

This PR fixes the /usage slash-command in the Control UI so it displays the current context-window snapshot instead of the misleading cumulative inputTokens + outputTokens total. When no fresh snapshot is available the command now shows n/a rather than an inflated number. Two regression tests are added to cover both scenarios.

Key changes:

  • slash-command-executor.ts: total is now derived from session.totalTokens only when session.totalTokensFresh !== false and the value is a finite number; otherwise falls back to null"n/a". The context-usage percentage is updated to use the total snapshot tokens rather than just input tokens.
  • slash-command-executor.node.test.ts: Existing test expectation updated (30% → 38% to match the new percentage logic); new test added for the stale/missing-snapshot path.

Issue found:

  • totalTokensFresh is referenced in slash-command-executor.ts (line 306) and passed as Partial<GatewaySessionRow> in the test (line 341), but GatewaySessionRow in ui/src/ui/types.ts does not declare this field. TypeScript's excess-property checking will reject both call-sites with a compile error. The fix requires adding totalTokensFresh?: boolean to GatewaySessionRow in ui/src/ui/types.ts.

Confidence Score: 2/5

  • Not safe to merge — missing type definition causes TypeScript compilation to fail.
  • The fix logic is sound and the test coverage is good, but the PR is incomplete: totalTokensFresh is used in both the implementation and the tests without being added to the GatewaySessionRow type. This produces hard TypeScript compilation errors in both files and blocks the build.
  • ui/src/ui/types.ts — must have totalTokensFresh?: boolean added to GatewaySessionRow before this PR can compile.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: ui/src/ui/chat/slash-command-executor.ts
Line: 306

Comment:
**`totalTokensFresh` missing from `GatewaySessionRow` type**

`session.totalTokensFresh` is accessed here, but `totalTokensFresh` is not declared in the `GatewaySessionRow` type in `ui/src/ui/types.ts`. TypeScript will reject this with _"Property 'totalTokensFresh' does not exist on type 'GatewaySessionRow'"_, causing compilation to fail.

The same problem surfaces in the test file at line 341, where `{ totalTokensFresh: false }` is passed as `Partial<GatewaySessionRow>` to the `row()` helper — TypeScript's excess-property check will also reject that.

`GatewaySessionRow` needs `totalTokensFresh?: boolean` added before this code can compile:

```
// ui/src/ui/types.ts  (GatewaySessionRow)
  inputTokens?: number;
  outputTokens?: number;
  totalTokens?: number;
+ totalTokensFresh?: boolean;
  model?: string;
  modelProvider?: string;
  contextTokens?: number;
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 5800b59

const output = session.outputTokens ?? 0;
const total = session.totalTokens ?? input + output;
const total =
session.totalTokensFresh === false || !Number.isFinite(session.totalTokens)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

totalTokensFresh missing from GatewaySessionRow type

session.totalTokensFresh is accessed here, but totalTokensFresh is not declared in the GatewaySessionRow type in ui/src/ui/types.ts. TypeScript will reject this with "Property 'totalTokensFresh' does not exist on type 'GatewaySessionRow'", causing compilation to fail.

The same problem surfaces in the test file at line 341, where { totalTokensFresh: false } is passed as Partial<GatewaySessionRow> to the row() helper — TypeScript's excess-property check will also reject that.

GatewaySessionRow needs totalTokensFresh?: boolean added before this code can compile:

// ui/src/ui/types.ts  (GatewaySessionRow)
  inputTokens?: number;
  outputTokens?: number;
  totalTokens?: number;
+ totalTokensFresh?: boolean;
  model?: string;
  modelProvider?: string;
  contextTokens?: number;
Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/ui/chat/slash-command-executor.ts
Line: 306

Comment:
**`totalTokensFresh` missing from `GatewaySessionRow` type**

`session.totalTokensFresh` is accessed here, but `totalTokensFresh` is not declared in the `GatewaySessionRow` type in `ui/src/ui/types.ts`. TypeScript will reject this with _"Property 'totalTokensFresh' does not exist on type 'GatewaySessionRow'"_, causing compilation to fail.

The same problem surfaces in the test file at line 341, where `{ totalTokensFresh: false }` is passed as `Partial<GatewaySessionRow>` to the `row()` helper — TypeScript's excess-property check will also reject that.

`GatewaySessionRow` needs `totalTokensFresh?: boolean` added before this code can compile:

```
// ui/src/ui/types.ts  (GatewaySessionRow)
  inputTokens?: number;
  outputTokens?: number;
  totalTokens?: number;
+ totalTokensFresh?: boolean;
  model?: string;
  modelProvider?: string;
  contextTokens?: number;
```

How can I resolve this? If you propose a fix, please make it concise.

@imwyvern imwyvern force-pushed the fix/control-ui-token-display branch 5 times, most recently from 5d54bd3 to 11bb635 Compare March 18, 2026 12:06
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 11bb635411

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +305 to +308
const total =
session.totalTokensFresh === false || !Number.isFinite(session.totalTokens)
? null
: (session.totalTokens ?? null);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep reporting total usage when freshness is unknown

When totalTokensFresh is false we only know the context snapshot is stale, not that token usage is unavailable. persistSessionUsageUpdate still writes inputTokens/outputTokens in that case (src/auto-reply/reply/session-usage.ts:93-105), so providers or runs without lastCallUsage/promptTokens will now make /usage show Total: n/a even though the command can still compute the total-usage line from input + output as before. Hiding the context percentage makes sense here, but dropping the total-usage fallback regresses /usage for sessions that do not emit fresh prompt snapshots.

Useful? React with 👍 / 👎.

@imwyvern imwyvern force-pushed the fix/control-ui-token-display branch 4 times, most recently from d68c0f2 to 57e4411 Compare March 22, 2026 18:34
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 57e4411514

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +305 to +306
const total = Number.isFinite(session.totalTokens) ? (session.totalTokens ?? null) : null;
const totalTokensFresh = session.totalTokensFresh !== false;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore /usage fallback when stale totals are omitted

Fresh evidence: sessions.list strips stale or missing context snapshots before the Control UI sees them—listSessionsFromStore only exposes totalTokens when resolveFreshSessionTotalTokens(entry) succeeds, and src/gateway/session-utils.test.ts:800-838 asserts stale rows arrive as { totalTokens: undefined, totalTokensFresh: false }. In that normal provider path, this change still renders Total: n/a even though inputTokens and outputTokens are present, so /usage regresses for runs without a fresh snapshot. The old input + output fallback is still needed for the total line; only the context percentage should depend on totalTokensFresh.

Useful? React with 👍 / 👎.

@imwyvern imwyvern force-pushed the fix/control-ui-token-display branch from 57e4411 to 2c72a3f Compare March 23, 2026 01:49
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2c72a3f494

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -302,15 +302,18 @@ async function executeUsage(
}
const input = session.inputTokens ?? 0;
const output = session.outputTokens ?? 0;
const total = session.totalTokens ?? input + output;
const total = Number.isFinite(session.totalTokens) ? (session.totalTokens ?? null) : null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the /usage total line on cumulative tokens

session.totalTokens is not the session’s total usage; deriveSessionTotalTokens defines it as the prompt/context snapshot and explicitly excludes completion/output tokens (src/agents/usage.ts:173-180). Reusing it for the Total line means any session with non-zero output now underreports usage—for example, the transcript snapshot exercised in src/gateway/session-utils.fs.test.ts:693-697 has inputTokens: 1200, outputTokens: 300, but totalTokens: 1250, so this command would print Total: 1.25k even though the actual cumulative usage is 1.5k. We still need the old input + output total for the Total line and should reserve totalTokens for the context snapshot/percentage only.

Useful? React with 👍 / 👎.

@imwyvern imwyvern force-pushed the fix/control-ui-token-display branch from 2c72a3f to 48624b6 Compare March 23, 2026 02:05
@openclaw-barnacle openclaw-barnacle Bot added channel: mattermost Channel integration: mattermost channel: msteams Channel integration: msteams channel: nextcloud-talk Channel integration: nextcloud-talk channel: irc size: S and removed size: XS labels Mar 23, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 48624b6b54

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/config/schema.base.generated.ts Outdated
@@ -1,3 +1,4 @@
 WARN  Unsupported engine: wanted: {"node":">=22.16.0"} (current: {"node":"v20.11.0","pnpm":"10.23.0"})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove the pnpm warning from the generated schema artifact

src/config/schema.ts:3 imports this file directly, so checking in the raw WARN Unsupported engine... line at the top corrupts the generated module and makes config-schema loads fail before any UI code runs. In practice this breaks any command or test path that imports the schema, because the file is no longer valid TypeScript/ESM source.

Useful? React with 👍 / 👎.

@imwyvern imwyvern force-pushed the fix/control-ui-token-display branch 2 times, most recently from aedee3c to 159182a Compare March 28, 2026 09:38
@openclaw-barnacle openclaw-barnacle Bot removed channel: mattermost Channel integration: mattermost channel: msteams Channel integration: msteams channel: irc labels Mar 28, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 159182a45b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +318 to +319
const total = Number.isFinite(session.totalTokens) ? (session.totalTokens ?? null) : null;
const totalTokensFresh = session.totalTokensFresh !== false;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore total-usage fallback when snapshot tokens are missing

sessions.list drops totalTokens whenever the prompt snapshot is stale or absent (resolveFreshSessionTotalTokens returns undefined when totalTokensFresh is false in src/config/sessions/types.ts), so this branch now sets total to null and /usage renders Total: n/a even when inputTokens and outputTokens are present. That regresses normal provider paths without a fresh context snapshot by hiding total usage entirely; the total line should still fall back to input + output (optionally marked approximate), while only the context-percentage line depends on fresh snapshot tokens.

Useful? React with 👍 / 👎.

@imwyvern imwyvern force-pushed the fix/control-ui-token-display branch from 159182a to 27bc496 Compare March 28, 2026 15:35
@openclaw-barnacle openclaw-barnacle Bot added size: XS and removed channel: nextcloud-talk Channel integration: nextcloud-talk size: S labels Mar 28, 2026
@imwyvern imwyvern force-pushed the fix/control-ui-token-display branch from 27bc496 to 1838d20 Compare March 28, 2026 16:06
@openclaw-barnacle openclaw-barnacle Bot added the docs Improvements or additions to documentation label Mar 28, 2026
@imwyvern imwyvern force-pushed the fix/control-ui-token-display branch from 1838d20 to 26e5115 Compare March 30, 2026 08:41
@openclaw-barnacle openclaw-barnacle Bot removed the docs Improvements or additions to documentation label Mar 30, 2026
@imwyvern imwyvern force-pushed the fix/control-ui-token-display branch from 26e5115 to 389ef43 Compare April 1, 2026 06:34
@imwyvern imwyvern force-pushed the fix/control-ui-token-display branch 3 times, most recently from 2be88c7 to 592312a Compare April 1, 2026 12:35
@imwyvern imwyvern force-pushed the fix/control-ui-token-display branch from 592312a to 303a548 Compare April 11, 2026 17:39
@imwyvern
Copy link
Copy Markdown
Contributor Author

Follow-up complete on this branch:

  • rebuilt from upstream/main and cherry-picked f159d4770cc9a73a783345019c0fdc1358480ee8 + 592312a5c0373b7155f65d23b80fe625ee25bc09
  • kept /usage total on cumulative input + output tokens while still using totalTokens only for the fresh context snapshot percentage
  • added a regression test covering the case where cumulative usage differs from the context snapshot
  • verification: NODE_OPTIONS='--max-old-space-size=8192' npx tsc --noEmit

@imwyvern imwyvern force-pushed the fix/control-ui-token-display branch 2 times, most recently from 92ff2b5 to 7e07932 Compare April 16, 2026 03:44
@imwyvern imwyvern force-pushed the fix/control-ui-token-display branch from 7e07932 to bceec12 Compare April 20, 2026 18:08
@steipete
Copy link
Copy Markdown
Contributor

Closing this as implemented after Codex review.

Current main already implements the requested Control UI /usage behavior: it keeps the total line cumulative, uses the current context snapshot only for the context percentage, handles stale snapshots via totalTokensFresh, and has regression tests covering the reported mismatch. This PR is superseded by the implementation already on main and by later closed replacements referenced in the PR timeline.

What I checked:

  • Current /usage logic matches requested behavior: executeUsage now computes cumulative total from inputTokens + outputTokens, computes the context percentage from totalTokens only when totalTokensFresh !== false, and hides the context line when the snapshot is stale. (ui/src/ui/chat/slash-command-executor.ts:380, 151befb90b90)
  • Regression tests cover fresh, stale, and differing snapshot cases: Tests assert 38% for a fresh snapshot, ~1.5k with no context line for stale snapshots, and 31% context while preserving 1.5k cumulative total when snapshot and cumulative usage differ. (ui/src/ui/chat/slash-command-executor.node.test.ts:490, 151befb90b90)
  • Earlier PR review blocker is already resolved on main: GatewaySessionRow now includes totalTokensFresh?: boolean, so the type error noted in PR review no longer applies. (ui/src/ui/types.ts:433, 151befb90b90)
  • Core contract distinguishes context snapshot from cumulative usage: deriveSessionTotalTokens documents that totalTokens is a prompt/context snapshot and intentionally excludes completion/output tokens, which is exactly why current main uses it for context percentage but not for the total line. (src/agents/usage.ts:242, 151befb90b90)
  • Session freshness contract supports the UI behavior: resolveFreshSessionTotalTokens drops stale totals when totalTokensFresh === false, and session usage persistence comments say lastCallUsage is stored so context-window utilization reflects the current context rather than accumulated run totals. (src/config/sessions/types.ts:426, 151befb90b90)
  • PR timeline already points to later closed replacements: The timeline cross-references closed PRs #71297 (Fix Control UI context freshness and compaction CTA) and #71462 (fix(ui): use current context usage in Control UI), which is consistent with this PR being superseded by work already present on main.

So I’m closing this as already implemented rather than keeping a duplicate issue open.

Review notes: reviewed against 151befb90b90.

@steipete steipete closed this Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Control UI token display shows incorrect values - shows cumulative tokens instead of current context

2 participants