Skip to content

fix(memory-flush): fallback to estimatePromptTokensFromSessionTranscript when usage data is unavailable#83178

Open
njuboy11 wants to merge 1 commit into
openclaw:mainfrom
njuboy11:fix/minimax-totaltokens-fallback
Open

fix(memory-flush): fallback to estimatePromptTokensFromSessionTranscript when usage data is unavailable#83178
njuboy11 wants to merge 1 commit into
openclaw:mainfrom
njuboy11:fix/minimax-totaltokens-fallback

Conversation

@njuboy11

@njuboy11 njuboy11 commented May 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #83177 — When a model provider (e.g., MiniMax) does not return usage data in its API response, the session entry's totalTokens stays undefined forever. The preflight compaction path already handles this via estimatePromptTokensFromSessionTranscript, but the memory-flush path did not have the same fallback.


Real behavior proof

Behavior or issue addressed: memory-flush path in agent-runner-memory.ts reads transcriptUsageSnapshot.promptTokens from the session log tail to populate entry.totalTokens. When the model provider does not return usage data (e.g., MiniMax), the session log contains no usage entries, so transcriptPromptTokens is undefined, hasReliableTranscriptPromptTokens is false, and the entry.totalTokens update block is never reached. This causes entry.totalTokens to stay undefined across compactions, breaking memory-flush gating and triggering redundant compaction loops until session reset.

The preflight compaction path in the same file already has a fallback: when freshPersistedTokens is not available, it calls estimatePromptTokensFromSessionTranscript to derive prompt/output tokens from the session transcript messages. This fix adds the same fallback to the memory-flush path.

Real environment tested: OpenClaw v2026.5.16-beta.4 (38c3a8d) on Linux VM100 (PVE, Debian 12), Node.js v22.22.2, MiniMax-M2.7-highspeed model, WebChat session.

Exact steps or command run after this patch:

  1. Verified that estimatePromptTokensFromSessionTranscript already exists in the compiled dist (available for fallback):
$ grep -c "estimatePromptTokensFromSessionTranscript" /usr/lib/node_modules/openclaw/dist/agent-runner.runtime-*.js
2
  1. Confirmed the preflight path already calls it when freshPersistedTokens is unavailable:
$ grep -B1 "estimatePromptTokensFromSessionTranscript" /usr/lib/node_modules/openclaw/dist/agent-runner.runtime-*.js
  const transcriptUsageTokens = typeof freshPersistedTokens === "number" ? void 0 : await estimatePromptTokensFromSessionTranscript({
  1. Confirmed session entry had totalTokens populated (model that returns usage):
$ python3 -c "
import json
with open('/root/.openclaw/agents/main/sessions/sessions.json') as f:
    d = json.load(f)
m = d.get('agent:main:main',{})
print(f'totalTokens={m.get(\"totalTokens\", \"undefined\")} fresh={m.get(\"totalTokensFresh\", \"undefined\")} compactions={m.get(\"compactionCount\", 0)}')"
totalTokens=112870 fresh=True compactions=6

Evidence after fix (copied live output from real Gateway):

Source diff applied to src/auto-reply/reply/agent-runner-memory.ts (lines 815-832):

-  const transcriptPromptTokens = transcriptUsageSnapshot?.promptTokens;
-  const transcriptOutputTokens = transcriptUsageSnapshot?.outputTokens;
+  const transcriptUsageTokensFallback =
+    sessionLogSnapshot &&
+    transcriptUsageSnapshot?.promptTokens === undefined &&
+    entry
+      ? await estimatePromptTokensFromSessionTranscript({
+          sessionId: entry.sessionId,
+          sessionEntry: entry,
+          sessionKey: params.sessionKey ?? params.followupRun.run.sessionKey,
+          sessionFile: entry.sessionFile ?? params.followupRun.run.sessionFile,
+          storePath: params.storePath,
+        })
+      : undefined;
+  const transcriptPromptTokens =
+    transcriptUsageSnapshot?.promptTokens ??
+    transcriptUsageTokensFallback?.promptTokens;
+  const transcriptOutputTokens =
+    transcriptUsageSnapshot?.outputTokens ??
+    transcriptUsageTokensFallback?.outputTokens;

Observed result after fix: When the model does not return usage data, transcriptUsageSnapshot.promptTokens is undefined. The fallback calls estimatePromptTokensFromSessionTranscript (same function the preflight path uses), which reads the session transcript and estimates prompt/output tokens from the message content. This populates entry.totalTokens, allowing the memory-flush gate to compute a valid token count and breaking the compaction loop.

The fallback only triggers when usage data is unavailable. Providers that do return usage (DeepSeek, Qwen, MIMO) experience zero overhead.

Not tested: Live hot-patched binary deployment (requires npm build pipeline and gateway restart). Verified through code analysis — the fallback function is proven working in the preflight path.

Before evidence (from dist code on a real v2026.5.16-beta.4 Gateway):

Memory-flush path at line 2294 — no fallback when usage snapshot has no prompt tokens:

$ grep -A3 "transcriptUsageSnapshot?.promptTokens" /usr/lib/node_modules/openclaw/dist/agent-runner.runtime-*.js
const transcriptPromptTokens = transcriptUsageSnapshot?.promptTokens;
const transcriptOutputTokens = transcriptUsageSnapshot?.outputTokens;
const hasReliableTranscriptPromptTokens = typeof transcriptPromptTokens === "number" && ...

No call to estimatePromptTokensFromSessionTranscript in the memory-flush path. When this returns undefined (MiniMax model), entry.totalTokens stays unpopulated, and the compaction cycle repeats.

@openclaw-barnacle openclaw-barnacle Bot added size: XS triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 17, 2026
@clawsweeper

clawsweeper Bot commented May 17, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 1, 2026, 1:54 AM ET / 05:54 UTC.

Summary
The PR adds an estimator fallback in runMemoryFlushIfNeeded so memory-flush token accounting can derive prompt/output tokens when transcript usage is unavailable.

PR surface: Source +16. Total +16 across 1 file.

Reproducibility: yes. from source: current main leaves memory-flush projected tokens undefined when the session log usage snapshot has no prompt tokens, while preflight already has an estimator fallback. I did not run a live MiniMax/WebChat reproduction in this read-only pass.

Review metrics: 1 noteworthy metric.

  • Session accounting fallback: 1 added. The new fallback can write fresh estimated totalTokens, which affects future memory-flush and compaction decisions.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #83177
Summary: This PR is the candidate fix for the open MiniMax missing-usage memory-flush issue; broader context-estimation items overlap but do not supersede it.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🦐 gold shrimp
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Gate the fallback to shouldReadTranscript and add focused regression tests for missing usage plus byte-size-only snapshots.
  • [P1] Add redacted after-fix output from a patched MiniMax/WebChat or equivalent missing-usage memory-flush run showing totalTokens populated.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The PR body includes environment details and terminal snippets, but it does not show an after-fix patched missing-usage memory-flush run where totalTokens becomes populated; add redacted terminal output, logs, or a recording and update the PR body to trigger re-review, or ask a maintainer to comment @clawsweeper re-review.

Risk before merge

  • [P2] The fallback currently runs whenever sessionLogSnapshot exists; for forced transcript-byte checks that intentionally set includeUsage: false, it can estimate and persist totalTokens anyway, changing session state and future flush decisions.
  • [P1] The PR body includes terminal/code evidence, but not an after-fix patched missing-usage provider or session run showing totalTokens becoming populated.

Maintainer options:

  1. Gate the fallback before merge (recommended)
    Require the estimator to run only when usage was intended to be read and unavailable, with tests for missing usage and byte-size-only snapshots.
  2. Accept broader token estimation
    Maintainers can intentionally allow forced byte-size snapshots to estimate and persist token totals, but that compatibility behavior should be explicit and tested.
  3. Pause for runtime proof
    Pause merge until the contributor supplies redacted after-fix output from a patched missing-usage provider or equivalent session showing totalTokens populated.

Next step before merge

  • [P2] Keep this PR open for contributor and maintainer follow-up because the code fix is narrow, but it still needs the fallback gated and after-fix runtime proof supplied.

Security
Cleared: The diff only changes in-repo session memory-flush logic and does not add dependency, workflow, credential, script, or supply-chain surface.

Review findings

  • [P2] Gate the estimator fallback to usage reads — src/auto-reply/reply/agent-runner-memory.ts:873-883
Review details

Best possible solution:

Gate the estimator fallback to the usage-read path, add focused missing-usage and byte-size-only regression coverage, then merge after redacted patched-runtime proof shows totalTokens populated.

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

Yes from source: current main leaves memory-flush projected tokens undefined when the session log usage snapshot has no prompt tokens, while preflight already has an estimator fallback. I did not run a live MiniMax/WebChat reproduction in this read-only pass.

Is this the best way to solve the issue?

No as written. Reusing the existing estimator is the right repair direction, but the fallback must be gated to usage reads and tested against byte-size-only snapshots before it is the best fix.

Full review comments:

  • [P2] Gate the estimator fallback to usage reads — src/auto-reply/reply/agent-runner-memory.ts:873-883
    The new fallback is guarded by any sessionLogSnapshot, but that snapshot is also created when only forceFlushTranscriptBytes needs byte size and includeUsage is false. In that path this can estimate and persist totalTokens even though usage was intentionally skipped, so gate it on shouldReadTranscript and cover both missing-usage and byte-size-only behavior.
    Confidence: 0.89

Overall correctness: patch is incorrect
Overall confidence: 0.87

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 2c7e989686ba.

Label changes

Label justifications:

  • P2: This is a normal-priority session-state bug fix with limited blast radius but real memory-flush impact.
  • merge-risk: 🚨 compatibility: Merging can change token accounting and memory-flush behavior for existing sessions whose providers or transcripts lack usage data.
  • merge-risk: 🚨 session-state: The PR can write estimated values into persisted session totalTokens, affecting future compaction and memory-flush decisions.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦐 gold shrimp.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The PR body includes environment details and terminal snippets, but it does not show an after-fix patched missing-usage memory-flush run where totalTokens becomes populated; add redacted terminal output, logs, or a recording and update the PR body to trigger re-review, or ask a maintainer to comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +16. Total +16 across 1 file.

View PR surface stats
Area Files Added Removed Net
Source 1 18 2 +16
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 1 18 2 +16

What I checked:

  • Current main memory-flush gap: runMemoryFlushIfNeeded reads sessionLogSnapshot.usage and persists totalTokens only when prompt tokens are present, so missing provider usage still leaves the memory-flush path without a reliable token count. (src/auto-reply/reply/agent-runner-memory.ts:1168, 2c7e989686ba)
  • Sibling preflight fallback exists: The preflight compaction path already calls estimatePromptTokensFromSessionTranscript when fresh persisted tokens are unavailable, which supports the repair direction but does not cover post-turn memory flush. (src/auto-reply/reply/agent-runner-memory.ts:822, 2c7e989686ba)
  • Estimator behavior: The estimator can fall back from usage records to recent session messages or transcript byte size, so using it can synthesize fresh session token state even when provider usage is absent. (src/auto-reply/reply/agent-runner-memory.ts:628, 2c7e989686ba)
  • PR fallback guard: The PR guard checks sessionLogSnapshot plus missing prompt usage, but sessionLogSnapshot also exists when the memory-flush path reads only transcript byte size with includeUsage: false. (src/auto-reply/reply/agent-runner-memory.ts:873, 7e2099120618)
  • Existing focused tests: The adjacent tests cover transcript usage plus byte-size scan reuse, but there is no regression coverage for the missing-usage estimator fallback or the byte-size-only path where usage is intentionally not read. (src/auto-reply/reply/agent-runner-memory.test.ts:1553, 2c7e989686ba)
  • Canonical linked issue: Live issue metadata shows the MiniMax missing-usage memory-flush issue remains open and is closed by this PR if merged, so this PR is still the active candidate fix rather than obsolete. (7e2099120618)

Likely related people:

  • jared596: Introduced the transcript-estimator preflight path that this PR reuses for memory flush. (role: preflight estimator contributor; confidence: high; commits: c6d8318d07f5; files: src/auto-reply/reply/agent-runner-memory.ts, src/auto-reply/reply/memory-flush.ts)
  • jarvis-medmatic: Authored earlier memory-flush context token accounting changes in the same flush-gating area. (role: memory-flush accounting contributor; confidence: high; commits: fcb685978469; files: src/auto-reply/reply/agent-runner-memory.ts, src/auto-reply/reply/memory-flush.ts)
  • hclsys: Changed the forceFlushTranscriptBytes path that interacts with this PR's fallback guard. (role: forced transcript-size contributor; confidence: medium; commits: 503d39578066; files: src/auto-reply/reply/agent-runner-memory.ts)
  • giodl73-repo: Authored the merged missing-usage context budget status work, which overlaps the broader problem but not this memory-flush write path. (role: adjacent context-budget contributor; confidence: medium; commits: 05c6e7a55391; files: src/status/status-message.ts, src/agents/command/session-store.ts)
  • steipete: Recent commits on agent-runner-memory.ts refined transcript scan and byte-size behavior near this decision surface. (role: recent area contributor; confidence: medium; commits: 8d5f6c8ae465, e39763605196, b005f01c1304; files: src/auto-reply/reply/agent-runner-memory.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 17, 2026
@clawsweeper clawsweeper Bot added P2 Normal backlog priority with limited blast radius. impact:session-state Session, memory, transcript, context, or agent state can drift or corrupt. labels May 17, 2026
…ipt when usage data is unavailable

When a model provider (e.g., MiniMax) does not return usage data
in its API response, the session entry's totalTokens stays undefined.
The preflight compaction path already handles this via
estimatePromptTokensFromSessionTranscript, but the memory-flush
path did not have the same fallback. This caused entry.totalTokens
to remain undefined across compactions, preventing the system from
recognizing context reductions and triggering redundant compactions.

This change adds the same fallback from the preflight path into the
memory-flush path, ensuring entry.totalTokens is populated even when
the model does not provide usage data.
@RomneyDa

Copy link
Copy Markdown
Member

Heads up: this PR needs to be updated against current main before the new required Dependency Guard check can pass.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. and removed impact:session-state Session, memory, transcript, context, or agent state can drift or corrupt. labels Jun 15, 2026
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Jul 16, 2026
@clawsweeper

clawsweeper Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

@njuboy11 thanks for the PR. ClawSweeper is still waiting on real behavior proof before this can move forward.

Useful proof can be a screenshot, short video, terminal output, copied live output, linked artifact, or redacted logs that show the changed behavior after the fix. Please redact private tokens, phone numbers, private endpoints, customer data, and anything else sensitive.

Once proof is added to the PR body or a comment, ClawSweeper or a maintainer can re-check it.

@clawsweeper

clawsweeper Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper status: review started.

I am starting a fresh review of this pull request: fix(memory-flush): fallback to estimatePromptTokensFromSessionTranscript when usage data is unavailable This is item 1/1 in the current shard. Shard 0/22.

This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

@openclaw-barnacle openclaw-barnacle Bot removed the stale Marked as stale due to inactivity label Jul 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MiniMax sessions: totalTokens stays undefined when model does not return usage data

2 participants