You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Behavior bug (incorrect output/state without crash)
Beta release blocker
Yes
Summary
Direct Telegram DMs can resolve a per-peer runtimePolicySessionKey and then use that policy key as the Lossless/LCM context-engine sessionKey. When the configured/default user-facing session is the canonical main lane (agent:main:main), this breaks session continuity, can select stale per-DM LCM conversations, and can produce both a full-context failure fallback and a later normal reply for one inbound message.
This is distinct from #84885 (partial preview spam), #84886 (Telegram polling dispatch idempotency), and #84887 (runtime LLM allowlist diagnostic double-prefixing).
Steps to reproduce
The local repro was an installed 2026.5.19 gateway with Telegram polling and the default/direct DM route bound to agent main.
Use a Telegram DM route where the canonical conversation should stay attached to the agent main session.
Have an older active Lossless conversation row for a per-DM key such as agent:main:telegram:default:direct:<peer-id>.
Send a Telegram DM to the bot.
Observe that OpenClaw can assemble/projection-bind the per-DM Lossless conversation before returning to the canonical main conversation.
Expected behavior
The runtime policy/session key should only scope policy/sandbox/tool decisions. It should not replace the canonical context-engine session key unless the user/config explicitly selected a per-DM conversation scope.
For a direct Telegram DM whose configured/default conversation is agent:main:main, the Lossless/LCM bootstrap and assemble calls should use:
sessionKey=agent:main:main
A generated per-peer policy key may still be used for tool/sandbox isolation, but it should be passed as a separate policy/sandbox key, not as the context history identity.
Actual behavior
A single inbound Telegram DM at 2026-05-21T18:28:02+07:00 first assembled the stale per-DM LCM conversation, hit the model context limit, sent the generic failure fallback, then assembled the canonical main conversation and sent a second visible message.
Relevant local log evidence from /tmp/openclaw/openclaw-2026-05-21.log:
2026-05-21T18:28:02.266+07:00 Inbound message telegram:<redacted-peer> -> @EvaLabsBot (direct, 4 chars)
2026-05-21T18:28:03.428+07:00 [lcm] bootstrap: session file shrank past checkpoint conversation=1876 session=boot-2026-05-21_07-53-21-505-412ec8bc sessionKey=agent:main:telegram:default:direct:<redacted-peer> checkpointOffset=1897994 currentSize=179112
2026-05-21T18:28:50.843+07:00 [lcm] assemble: done conversation=1876 session=boot-2026-05-21_07-53-21-505-412ec8bc sessionKey=agent:main:telegram:default:direct:<redacted-peer> contextItems=416 summaryContextItems=17 inputMessages=124 outputMessages=417 tokenBudget=258000 estimatedTokens=227240
2026-05-21T18:28:50.888+07:00 codex app-server context-engine projection decision sessionKey=agent:main:telegram:default:direct:<redacted-peer> previousEpoch=summary-prefix-v1:1872:... reason=context-engine-binding-mismatch assembledMessages=417 projectedPromptChars=817231
2026-05-21T18:29:12.721+07:00 embedded run failover decision provider=openai-codex model=gpt-5.5 rawErrorPreview="Codex ran out of room in the model's context window. Start a new thread or clear earlier history before retrying."
2026-05-21T18:29:24.073+07:00 telegram outbound send ok messageId=24348 operation=sendMessage deliveryKind=text
2026-05-21T18:29:22.697+07:00 [lcm] assemble: done conversation=1872 session=boot-2026-05-21_11-15-48-922-1e2fa501 sessionKey=agent:main:main contextItems=165 summaryContextItems=2 inputMessages=125 outputMessages=137 tokenBudget=258000 estimatedTokens=61135
2026-05-21T18:29:22.718+07:00 codex app-server context-engine projection decision sessionKey=agent:main:main previousEpoch=summary-prefix-v1:1876:... reason=context-engine-binding-mismatch assembledMessages=137 projectedPromptChars=166128
2026-05-21T18:29:33.958+07:00 telegram outbound send ok messageId=24349 operation=sendMessage deliveryKind=text
2026-05-21T18:29:33.965+07:00 res ok message.action channel=telegram
The Codex app-server state DB also shows the failed per-DM projection consumed the full window:
openclaw sessions cleanup --dry-run --fix-dm-scope --active-key agent:main:main --json identified one stale session-store row, but that dry run does not retire stale active LCM conversations:
flowchart TD
A[Telegram direct DM] --> B[Canonical route/session should be agent:main:main]
B --> C[Runtime policy resolver derives per-peer policy key]
C --> D[Policy key passed as sandboxSessionKey]
D --> E[Context engine bootstrap/assemble uses sandboxSessionKey]
E --> F[LCM selects stale per-DM conversation 1876]
F --> G[Prompt projection grows to ~817k chars / full context]
G --> H[Model fails before reply]
H --> I[Generic Telegram error fallback]
B --> J[Next projection uses main conversation 1872]
J --> K[Agent sends visible reply via message.action]
classDef bad fill:#ffd6d6,stroke:#cc3333,color:#111;
class E,F,G,H,I bad;
Loading
Impact and severity
Affected: Telegram users where DMs are expected to share the agent main session and stale per-DM LCM/session rows exist from prior runs or prior scoping behavior.
Severity: High / beta blocker. It can cause:
one inbound Telegram message to produce both a failure fallback and an agent-authored reply,
full-context gpt-5.5 requests from stale history selection,
sudden API/token burn,
broken expectation that the agent-name main session remains the single continuity lane,
confusing LCM compaction behavior because the wrong active conversation is selected.
Additional information
Suggested OpenClaw fix shape:
Keep sessionKey and runtimePolicySessionKey separate in the embedded/Codex app-server run path.
Use the canonical conversation sessionKey for bootstrapHarnessContextEngine(), assembleHarnessContextEngine(), context-engine thread bindings, and workspace/bootstrap memory selection.
Use runtimePolicySessionKey only for policy/sandbox/tool scoping fields.
Add a regression for direct Telegram context where session.dmScope is unset/default-main and ctx.ChatType="direct": context-engine calls should receive agent:main:main, while policy hooks may receive the per-peer policy key.
Consider a companion LCM/OpenClaw cleanup guard for stale active same-file/same-path-shrink conversations whose checkpoint offset is beyond EOF; LCM detected this condition but did not deactivate the stale active row.
Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
Yes
Summary
Direct Telegram DMs can resolve a per-peer
runtimePolicySessionKeyand then use that policy key as the Lossless/LCM context-enginesessionKey. When the configured/default user-facing session is the canonical main lane (agent:main:main), this breaks session continuity, can select stale per-DM LCM conversations, and can produce both a full-context failure fallback and a later normal reply for one inbound message.This is distinct from #84885 (partial preview spam), #84886 (Telegram polling dispatch idempotency), and #84887 (runtime LLM allowlist diagnostic double-prefixing).
Steps to reproduce
The local repro was an installed
2026.5.19gateway with Telegram polling and the default/direct DM route bound to agentmain.agent:main:telegram:default:direct:<peer-id>.Expected behavior
The runtime policy/session key should only scope policy/sandbox/tool decisions. It should not replace the canonical context-engine session key unless the user/config explicitly selected a per-DM conversation scope.
For a direct Telegram DM whose configured/default conversation is
agent:main:main, the Lossless/LCM bootstrap and assemble calls should use:A generated per-peer policy key may still be used for tool/sandbox isolation, but it should be passed as a separate policy/sandbox key, not as the context history identity.
Actual behavior
A single inbound Telegram DM at
2026-05-21T18:28:02+07:00first assembled the stale per-DM LCM conversation, hit the model context limit, sent the generic failure fallback, then assembled the canonical main conversation and sent a second visible message.Relevant local log evidence from
/tmp/openclaw/openclaw-2026-05-21.log:The Codex app-server state DB also shows the failed per-DM projection consumed the full window:
OpenClaw version
2026.5.19
Operating system
macOS 26 / Darwin arm64
Install method
Global OpenClaw gateway managed by launchd (
ai.openclaw.gateway)Model
gpt-5.5viaopenai-codexProvider / routing chain
Telegram polling -> OpenClaw gateway -> embedded agent / Codex app-server -> Lossless context engine -> OpenAI Codex provider
Additional provider/model setup details
Lossless-Claw was verified as the vanilla latest npm package, not a local checkout or branched patch:
Gateway status at diagnosis:
No local config path explicitly selected per-DM scoping:
Logs, screenshots, and evidence
Source-level paths in the installed release build:
The active LCM conversation rows at diagnosis included both the canonical main lane and stale active per-DM lanes:
openclaw sessions cleanup --dry-run --fix-dm-scope --active-key agent:main:main --jsonidentified one stale session-store row, but that dry run does not retire stale active LCM conversations:{ "dryRun": true, "beforeCount": 8, "afterCount": 7, "dmScopeRetired": 1, "wouldMutate": true }Impact and severity
Affected: Telegram users where DMs are expected to share the agent main session and stale per-DM LCM/session rows exist from prior runs or prior scoping behavior.
Severity: High / beta blocker. It can cause:
gpt-5.5requests from stale history selection,Additional information
Suggested OpenClaw fix shape:
sessionKeyandruntimePolicySessionKeyseparate in the embedded/Codex app-server run path.sessionKeyforbootstrapHarnessContextEngine(),assembleHarnessContextEngine(), context-engine thread bindings, and workspace/bootstrap memory selection.runtimePolicySessionKeyonly for policy/sandbox/tool scoping fields.session.dmScopeis unset/default-main andctx.ChatType="direct": context-engine calls should receiveagent:main:main, while policy hooks may receive the per-peer policy key.