Skip to content

fix: respect totalTokensFresh flag to avoid showing stale token counts#47083

Open
zymclaw wants to merge 2 commits intoopenclaw:mainfrom
zymclaw:fix/tui-total-tokens-fresh-check
Open

fix: respect totalTokensFresh flag to avoid showing stale token counts#47083
zymclaw wants to merge 2 commits intoopenclaw:mainfrom
zymclaw:fix/tui-total-tokens-fresh-check

Conversation

@zymclaw
Copy link
Copy Markdown

@zymclaw zymclaw commented Mar 15, 2026

Problem

When a session's totalTokensFresh flag is false, the totalTokens value may be stale/historical (e.g., accumulated from previous compaction) and should not be displayed.

This caused incorrect displays like "100% context used 2.6M / 200k" in both TUI and Web UI when the token count was stale.

Root Cause

The Gateway already correctly filters stale token counts via resolveFreshSessionTotalTokens, but:

  1. TUI: Type definition was missing totalTokensFresh field, so TUI couldn't check it
  2. UI: formatSessionTokens function didn't check the freshness flag

Solution

TUI (commit 368ca03)

  • Add totalTokensFresh to GatewaySessionList type in gateway-chat.ts
  • Add totalTokensFresh to SessionInfoEntry type in tui-session-actions.ts
  • Check totalTokensFresh !== false before updating sessionInfo.totalTokens

UI (commit 446fdd1)

  • Add totalTokensFresh to GatewaySessionRow type in types.ts
  • Check totalTokensFresh === false in formatSessionTokens function, return "n/a" when stale

Testing

  • TypeScript compilation passes for both TUI and UI
  • The fix ensures stale token counts are not displayed in sessions list

Files Changed

  • src/tui/gateway-chat.ts - Add totalTokensFresh to type definition
  • src/tui/tui-session-actions.ts - Add type and check before using totalTokens
  • ui/src/ui/types.ts - Add totalTokensFresh to type definition
  • ui/src/ui/presenter.ts - Check freshness before displaying tokens

…counts

When a session's totalTokensFresh flag is false, the totalTokens value
may be stale/historical (e.g., accumulated from previous compaction) and
should not be displayed in the TUI footer.

Changes:
- Add totalTokensFresh to GatewaySessionList type
- Add totalTokensFresh to SessionInfoEntry type
- Check totalTokensFresh !== false before updating sessionInfo.totalTokens

This prevents showing incorrect token counts like '1.9M / 200k' when
the totalTokens value is stale.
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 15, 2026

Greptile Summary

This PR fixes a TUI bug where stale totalTokens values (marked with totalTokensFresh: false by the Gateway, e.g., after session compaction) were incorrectly applied and displayed in the footer. The fix correctly adds totalTokensFresh to the relevant type definitions and gates the totalTokens update behind a freshness check.

Key changes:

  • gateway-chat.ts: Adds totalTokensFresh?: boolean to GatewaySessionList — a clean, additive type change.
  • tui-session-actions.ts: Extends SessionInfoEntry with totalTokensFresh and checks the flag before updating totalTokens in applySessionInfo.

One issue to address: The new outer guard entry.totalTokens !== null is evaluated before the freshness check. Because totalTokens is typed as number | null, this inadvertently prevents a fresh null value from clearing an existing stale count in state (see inline comment). Removing the null guard from the outer condition and relying solely on the inner freshness check handles all four combinations correctly.

Confidence Score: 3/5

  • Safe to merge with a minor fix — the freshness check is correct, but the placement of the null guard introduces a subtle regression for fresh-null resets.
  • The core logic (don't apply totalTokens when totalTokensFresh === false) is sound and addresses the stated bug. However, the entry.totalTokens !== null check placed in the outer condition silently drops null even when totalTokensFresh is true, which changes the pre-existing behaviour of clearing the token count. This is a real edge-case regression worth fixing before merge.
  • src/tui/tui-session-actions.ts — lines 194–198, the null guard ordering
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/tui/tui-session-actions.ts
Line: 194-198

Comment:
**`null` guard blocks legitimate fresh-null resets**

`totalTokens` is typed as `number | null` in `SessionInfo`. When the gateway sends `totalTokens: null` (to clear/reset the displayed count) together with `totalTokensFresh: true` (or `totalTokensFresh` absent), the outer `entry.totalTokens !== null` guard silently drops the update — leaving a stale numeric value on-screen. The `null` check is needed only when the value is stale, but currently it runs unconditionally before the freshness check is ever evaluated.

Restructuring the condition handles all four combinations correctly:

```suggestion
    if (entry?.totalTokens !== undefined) {
      if (entry.totalTokensFresh !== false) {
        next.totalTokens = entry.totalTokens;
      }
    }
```

This way:
- `totalTokens: 5000, fresh: true` → updated ✓  
- `totalTokens: 5000, fresh: false` → skipped (stale) ✓  
- `totalTokens: null, fresh: true` → clears display ✓  
- `totalTokens: null, fresh: false` → skipped (stale null) ✓

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

Last reviewed commit: 368ca03

Comment on lines +194 to 198
if (entry?.totalTokens !== undefined && entry.totalTokens !== null) {
if (entry.totalTokensFresh !== false) {
next.totalTokens = entry.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.

null guard blocks legitimate fresh-null resets

totalTokens is typed as number | null in SessionInfo. When the gateway sends totalTokens: null (to clear/reset the displayed count) together with totalTokensFresh: true (or totalTokensFresh absent), the outer entry.totalTokens !== null guard silently drops the update — leaving a stale numeric value on-screen. The null check is needed only when the value is stale, but currently it runs unconditionally before the freshness check is ever evaluated.

Restructuring the condition handles all four combinations correctly:

Suggested change
if (entry?.totalTokens !== undefined && entry.totalTokens !== null) {
if (entry.totalTokensFresh !== false) {
next.totalTokens = entry.totalTokens;
}
}
if (entry?.totalTokens !== undefined) {
if (entry.totalTokensFresh !== false) {
next.totalTokens = entry.totalTokens;
}
}

This way:

  • totalTokens: 5000, fresh: true → updated ✓
  • totalTokens: 5000, fresh: false → skipped (stale) ✓
  • totalTokens: null, fresh: true → clears display ✓
  • totalTokens: null, fresh: false → skipped (stale null) ✓
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/tui/tui-session-actions.ts
Line: 194-198

Comment:
**`null` guard blocks legitimate fresh-null resets**

`totalTokens` is typed as `number | null` in `SessionInfo`. When the gateway sends `totalTokens: null` (to clear/reset the displayed count) together with `totalTokensFresh: true` (or `totalTokensFresh` absent), the outer `entry.totalTokens !== null` guard silently drops the update — leaving a stale numeric value on-screen. The `null` check is needed only when the value is stale, but currently it runs unconditionally before the freshness check is ever evaluated.

Restructuring the condition handles all four combinations correctly:

```suggestion
    if (entry?.totalTokens !== undefined) {
      if (entry.totalTokensFresh !== false) {
        next.totalTokens = entry.totalTokens;
      }
    }
```

This way:
- `totalTokens: 5000, fresh: true` → updated ✓  
- `totalTokens: 5000, fresh: false` → skipped (stale) ✓  
- `totalTokens: null, fresh: true` → clears display ✓  
- `totalTokens: null, fresh: false` → skipped (stale null) ✓

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

…ounts in sessions list

When a session's totalTokensFresh flag is false, the totalTokens value
may be stale/historical (e.g., accumulated from previous compaction) and
should not be displayed in the UI sessions list.

Changes:
- Add totalTokensFresh to GatewaySessionRow type
- Check totalTokensFresh !== false in formatSessionTokens before displaying

This prevents showing incorrect token counts like '2.6M / 200k' when
the totalTokens value is stale.

Complements TUI fix in commit 368ca03.
@openclaw-barnacle openclaw-barnacle Bot added the app: web-ui App: web-ui label Mar 15, 2026
@zymclaw zymclaw changed the title fix(tui): respect totalTokensFresh flag to avoid showing stale token counts fix: respect totalTokensFresh flag to avoid showing stale token counts Mar 15, 2026
@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 28, 2026

Codex review: needs changes before merge.

Summary
The PR adds totalTokensFresh handling to TUI and Control UI session-token display paths so stale token totals are hidden.

Reproducibility: yes. Source inspection gives a high-confidence path: a TUI refresh with totalTokensFresh: false and no fresh totalTokens leaves an old sessionInfo.totalTokens, which the footer still formats; a direct Control UI row with totalTokensFresh: false and a numeric total also still formats as a count.

Next step before merge
This is a concrete display-state repair suitable for automation, but the worker should update the contributor branch if possible or create a focused replacement against current main.

Security
Cleared: The diff only changes TUI and Control UI token-count type/display handling and does not touch dependencies, workflows, permissions, secrets, package metadata, install scripts, or code execution paths.

Review findings

  • [P2] Handle freshness before token value guards — src/tui/tui-session-actions.ts:194-198
Review details

Best possible solution:

Land a narrow repaired fix that carries totalTokensFresh through the current TUI session-info path, clears or suppresses stale footer totals including flag-only and fresh-null cases, keeps Control UI formatting defensive, and includes focused tests plus a changelog entry.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection gives a high-confidence path: a TUI refresh with totalTokensFresh: false and no fresh totalTokens leaves an old sessionInfo.totalTokens, which the footer still formats; a direct Control UI row with totalTokensFresh: false and a numeric total also still formats as a count.

Is this the best way to solve the issue?

No, not as submitted. The direction is the narrow maintainable fix, but the TUI logic must check freshness before value/null guards and be ported to the current TuiSessionList/SessionInfo type surface.

Full review comments:

  • [P2] Handle freshness before token value guards — src/tui/tui-session-actions.ts:194-198
    The new condition only runs when totalTokens is present and non-null. A stale flag-only update can leave an old footer total visible, and a fresh totalTokens: null update cannot clear a previous count, so the TUI can still show stale context usage.
    Confidence: 0.94

Overall correctness: patch is incorrect
Overall confidence: 0.9

Acceptance criteria:

  • pnpm test src/tui/tui-session-actions.test.ts ui/src/ui/controllers/sessions.test.ts ui/src/ui/views/sessions.test.ts
  • pnpm exec oxfmt --check --threads=1 src/tui/tui-types.ts src/tui/tui-backend.ts src/tui/tui-session-actions.ts src/tui/tui-session-actions.test.ts ui/src/ui/presenter.ts ui/src/ui/types.ts ui/src/ui/views/sessions.test.ts CHANGELOG.md
  • pnpm check:changed in Testbox before handoff if code changes are promoted

What I checked:

  • Current TUI state lacks freshness metadata: SessionInfo carries totalTokens but no totalTokensFresh, so TUI session state cannot distinguish a stale/missing refreshed total from a fresh value. (src/tui/tui-types.ts:48, 8e79392dccf4)
  • Current TUI list type drops freshness: TuiSessionList picks token fields from SessionInfo and includes totalTokens, but not totalTokensFresh; GatewaySessionList is now only an alias to this type. (src/tui/tui-backend.ts:34, 8e79392dccf4)
  • Current TUI action keeps stale totals: applySessionInfo assigns entry.totalTokens when present but has no freshness check or flag-only stale clear, so an existing footer total can survive a stale refresh. (src/tui/tui-session-actions.ts:198, 8e79392dccf4)
  • Current TUI footer displays retained totals: The footer formats sessionInfo.totalTokens directly, so a retained stale value remains user-visible as context usage. (src/tui/tui.ts:905, 8e79392dccf4)
  • Gateway contract treats stale totals as unknown: resolveFreshSessionTotalTokens returns undefined when totalTokensFresh === false, matching the expected display-layer behavior of suppressing stale context totals. (src/config/sessions/types.ts:502, 8e79392dccf4)
  • Current session listing already emits flag-only stale rows: The gateway session-list test asserts a stale stored total becomes totalTokens: undefined and totalTokensFresh: false, which is the exact flag-only update the submitted TUI guard would miss. (src/gateway/session-utils.search.test.ts:250, 8e79392dccf4)

Likely related people:

  • steipete: Recent history touches the active TUI session action/backend files and adjacent session freshness helpers involved in this display-state path. (role: recent TUI/session maintainer; confidence: medium; commits: db40ec404a91, 32ebaa37574e, 4cd68fafbb2a; files: src/tui/tui-session-actions.ts, src/tui/tui-backend.ts, src/config/sessions/types.ts)
  • BunsDev: Merged the related Control UI context freshness work in Fix Control UI context freshness and compaction CTA #71297 and has recent current-main commits in the sessions controller/type surfaces that carry totalTokensFresh. (role: Control UI freshness maintainer; confidence: high; commits: da773175f2eb, 05c9492bff0f, 4532e5d85803; files: ui/src/ui/controllers/sessions.ts, ui/src/ui/types.ts, ui/src/ui/chat/context-notice.ts)
  • vincentkoc: Recent file history for the Control UI presenter and type seams includes formatter/localization and UI type-splitting changes near the affected display helper. (role: adjacent Control UI formatter/type maintainer; confidence: low; commits: 790129615327, e8f0f91d2907, 01058162bebb; files: ui/src/ui/presenter.ts, ui/src/ui/types.ts)

Remaining risk / open question:

  • The PR head was written before the current GatewaySessionList = TuiSessionList shape, so the repair likely needs a rebase or replacement branch rather than applying the old type edit literally.
  • No tests were run during this read-only review; the repaired branch needs focused TUI and Control UI regression coverage before merge.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 8e79392dccf4.

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.

1 participant