openclaw - ✅(Solved) Fix Bug: compaction/reset paths can break session invariants (messageProvider + token accounting) [1 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

  1. Start a long-running session that reaches compaction
  2. Let compaction collapse the remaining transcript to an effectively empty/zero-token state
  3. Observe the transcript is cleared but cached accounting still reports old totals
  4. Next turn hits the compaction safeguard again because totalTokens / cost state was not reset consistently

Fix Action

Fix / Workaround

  1. auto compaction hook dispatch via handleAutoCompactionStart/End
  2. engine-owned compaction hook dispatch in compact.queued.ts
  3. command reset hooks via emitResetCommandHooks
  4. gateway sessions.reset via emitGatewayBeforeResetPluginHook
  5. post-compaction session accounting when tokensAfter === 0

PR fix notes

PR #69270: fix(compaction): restore session invariants across compaction and reset

Description (problem / solution / changelog)

Summary

Restore session invariants across compaction and reset instead of patching each symptom in isolation.

This coordinated fix now covers four connected paths:

  • subscribe-time before_compaction / after_compaction hook context
  • engine-owned compaction hook context in compact.queued.ts
  • command /new / /reset hook context in emitResetCommandHooks
  • gateway sessions.reset hook context in emitGatewayBeforeResetPluginHook

It also fixes compaction bookkeeping so zero-token outcomes reset cached usage instead of leaving the session in a repeated safeguard loop.

What changed

  • introduced a shared hook-message-provider resolver so compaction/reset hooks all use the same rules
  • normalized explicit providers before emitting hook context, so values like Telegram become telegram
  • inferred providers from session keys only for real deliverable channel-scoped keys
  • kept non-channel keys such as agent:main:direct:peer_123 unresolved instead of emitting fake providers
  • reset totalTokens / totalTokensFresh / estimatedCostUsd from compaction results even when tokensAfter === 0
  • extended regression coverage for mixed-case reset channels, normalized compaction hook providers, zero-token compactions, and gateway/manual compaction bookkeeping

Why

These reports all describe the same broader failure mode: compaction/reset code paths were not preserving the session invariants downstream systems rely on.

That surfaced as different user-visible bugs:

  • hook consumers could see missing or non-canonical messageProvider values and split one real conversation into multiple logical sessions
  • compaction could clear the transcript but leave stale token/cost accounting behind, causing repeated safeguard loops

Issues

Fixes #69269 Fixes #69286

Also addresses the duplicate token-accounting report in #69287. Related cluster: #69202.

Tests

  • pnpm build
  • pnpm check
  • pnpm exec vitest run src/auto-reply/reply/commands-core.test.ts src/agents/pi-embedded-subscribe.handlers.compaction.test.ts src/auto-reply/reply/reply-state.test.ts src/gateway/server.sessions.gateway-server-sessions-a.test.ts

Changed files

  • src/agents/pi-embedded-helpers/bootstrap.ts (modified, +2/-1)
  • src/agents/pi-embedded-runner/compact.hooks.harness.ts (modified, +3/-1)
  • src/agents/pi-embedded-runner/compact.queued.ts (modified, +5/-1)
  • src/agents/pi-embedded-runner/compact.ts (modified, +1/-1)
  • src/agents/pi-embedded-runner/run/attempt.ts (modified, +2/-1)
  • src/agents/pi-embedded-subscribe.handlers.compaction.test.ts (modified, +66/-0)
  • src/agents/pi-embedded-subscribe.handlers.compaction.ts (modified, +12/-1)
  • src/agents/pi-embedded-subscribe.types.ts (modified, +2/-0)
  • src/agents/pi-settings.test.ts (modified, +5/-1)
  • src/auto-reply/reply/commands-core.test.ts (modified, +38/-5)
  • src/auto-reply/reply/commands-reset-hooks.ts (modified, +5/-0)
  • src/auto-reply/reply/reply-state.test.ts (modified, +33/-0)
  • src/auto-reply/reply/session-updates.ts (modified, +4/-2)
  • src/gateway/server-methods/sessions.ts (modified, +6/-1)
  • src/gateway/server.sessions.gateway-server-sessions-a.test.ts (modified, +163/-0)
  • src/gateway/server/ws-connection/handshake-auth-helpers.ts (modified, +1/-2)
  • src/gateway/session-reset-service.ts (modified, +2/-0)
  • src/utils/hook-message-provider.test.ts (added, +47/-0)
  • src/utils/hook-message-provider.ts (added, +46/-0)
RAW_BUFFERClick to expand / collapse

Problem

Compaction/reset paths can break session invariants in two related ways:

  1. hook contexts can lose or mis-normalize messageProvider
  2. successful compaction can leave stale token/cost accounting behind

In practice this causes downstream consumers to see one real conversation as multiple logical sessions, and can also trap a session in repeated compaction safeguard loops after the transcript has already been cleared.

Concrete affected paths

Today this cluster shows up across several connected paths:

  1. auto compaction hook dispatch via handleAutoCompactionStart/End
  2. engine-owned compaction hook dispatch in compact.queued.ts
  3. command reset hooks via emitResetCommandHooks
  4. gateway sessions.reset via emitGatewayBeforeResetPluginHook
  5. post-compaction session accounting when tokensAfter === 0

Reproduction A: provider invariants

  1. Use a channel-scoped session key like agent:main:feishu:default:direct:ou_xxx
  2. Register a plugin that uses ctx.sessionKey + ctx.messageProvider to build stable session identity
  3. Trigger auto compaction, /new, /reset, or sessions.reset
  4. Observe ctx.messageProvider missing, mixed-case, or inferred from a non-channel key shape
  5. Plugin falls back to unknown or treats Telegram / telegram as different providers
  6. One real conversation is split into multiple logical sessions

Reproduction B: token accounting invariants

  1. Start a long-running session that reaches compaction
  2. Let compaction collapse the remaining transcript to an effectively empty/zero-token state
  3. Observe the transcript is cleared but cached accounting still reports old totals
  4. Next turn hits the compaction safeguard again because totalTokens / cost state was not reset consistently

Expected

Compaction/reset code paths should preserve the same session invariants everywhere:

  • hook contexts should expose a normalized deliverable messageProvider when one is actually known
  • non-channel session keys should not manufacture fake providers
  • post-compaction token/cost accounting should be refreshed from the compaction result, including zero-token outcomes

Actual

Different paths currently preserve different subsets of those invariants, so downstream systems must guess or degrade.

Scope

This is broader than the original single-field hook bug. It covers the coordinated compaction/reset invariant cluster now reported separately in:

  • #69269
  • #69286
  • #69287
  • related cluster: #69202

extent analysis

TL;DR

To fix the issue, ensure that compaction and reset paths preserve session invariants, including normalized messageProvider and accurate token/cost accounting.

Guidance

  • Review the affected paths listed in the issue (e.g., handleAutoCompactionStart/End, compact.queued.ts, emitResetCommandHooks, emitGatewayBeforeResetPluginHook) to identify where session invariants are being broken.
  • Verify that ctx.messageProvider is consistently normalized and not lost or mis-normalized during compaction and reset operations.
  • Update post-compaction session accounting to refresh token/cost state from the compaction result, including zero-token outcomes.
  • Consider implementing a coordinated approach to preserving session invariants across all compaction and reset paths.

Example

No specific code snippet is provided, as the issue requires a broader review of the affected paths and implementation of a consistent approach to preserving session invariants.

Notes

The issue is part of a larger cluster of related problems (#69269, #69286, #69287, #69202), and addressing this issue may require a coordinated effort to ensure that all affected paths are updated to preserve session invariants.

Recommendation

Apply a workaround to ensure that session invariants are preserved during compaction and reset operations, such as implementing a centralized mechanism to manage and update session state. This will help prevent downstream consumers from seeing one real conversation as multiple logical sessions and reduce the risk of repeated compaction safeguard loops.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING