Skip to content

fix: resolveFreshSessionTotalTokens returns undefined when totalTokens is preserved (post-#82578 regression) #82900

@njuboy11

Description

@njuboy11

Bug: resolveFreshSessionTotalTokens returns undefined when totalTokens is preserved (post-#82578 regression)

Symptom

After every 2-3 conversational turns, the /context command shows stale/unknown context utilization, and the Control UI context meter resets to 0%. The session still works correctly and context IS being sent to the model — this is purely a display/estimation issue.

Root Cause

PR #82578 correctly fixed persistSessionUsageUpdate from overwriting totalTokens=undefined when there is no fresh context snapshot. After the fix, totalTokens is preserved across stale usage updates.

However, resolveFreshSessionTotalTokens() (in src/types.ts) still returns undefined when entry?.totalTokensFresh === false, even though totalTokens is now a valid, preserved number. This function is used by:

  1. /context command handler → returns undefined → "context unavailable"
  2. session-utils.ts transcript estimation → returns undefined → triggers unnecessary transcript re-read

Internal consumers unaffected

The compaction preflight check and memory flush logic check entry.totalTokensFresh directly on the entry object, not through resolveFreshSessionTotalTokens(). So they are unaffected and continue to work correctly.

Real Behavior Data

Current session (after fix):
  totalTokens: 110392
  totalTokensFresh: True (after agent refresh) / False (after new message)
  compactionCount: 0
  cacheRead: 109568

Flow trace:

  1. User sends message → session-updates.ts sets totalTokensFresh = false (incrementBy > 0)
  2. persistSessionUsageUpdate also sets totalTokensFresh = false when !hasFreshContextSnapshot
  3. Control UI queries status → resolveFreshSessionTotalTokens sees totalTokensFresh === false → returns undefined
  4. /context command shows stale/unavailable
  5. Agent starts preflight → entry.totalTokensFresh === false (checked directly on entry) → reads from transcript → sets totalTokensFresh = true
  6. Status becomes available again... until next message

Suggested Fix

In resolveFreshSessionTotalTokens(), return the preserved totalTokens value even when totalTokensFresh === false, since the value is now guaranteed to be valid (not undefined) after PR #82578.

// Before:
if (entry?.totalTokensFresh === false) return;
return total;

// After:
// totalTokensFresh=false only means "possibly stale", not "unavailable".
// The value itself is preserved from compaction/transcript (PR #82578).
return total;

The totalTokensFresh flag continues to serve its intended purpose for compaction preflight and memory flush, which check it directly on entry.totalTokensFresh.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions