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
Sibling of #82812 (closed) and #69118 (open). #82812 fixed per-turn metadata bleeding into extraSystemPromptHash by hashing only the static parts. However one of those "static" parts, directChatContext, embeds the channel/provider label, which is NOT stable when a single CLI session binding serves more than one channel. With session.dmScope:"main" (or any route binding that collapses multiple delivery channels onto one session key), alternating the channel a direct conversation arrives on flips directChatContext between, for example, "Telegram" and "WebChat". That changes extraSystemPromptHash, so resolveCliSessionReuse returns invalidatedReason: "system-prompt" and the agent starts a fresh claude session with no memory of the prior turns.
Net user-visible effect: with claude-cli backend plus dmScope:"main", moving a conversation between the dashboard and Telegram (same agent, same logical DM) makes the agent lose all prior context on the switch turn. Confirmed reproducible on OpenClaw 2026.5.7.
src/auto-reply/reply/groups.tsbuildDirectChatContext emits You are in a ${providerLabel} direct conversation. and resolveProviderLabel (same file) returns "WebChat" for internal channels and the capitalized provider name (for example "Telegram") otherwise. So the string is channel dependent.
src/agents/cli-runner/prepare.ts hashes that block (extraSystemPromptHash = hashCliSessionText(params.extraSystemPromptStatic ...)).
src/agents/cli-session.tsresolveCliSessionReuse: if (storedExtraSystemPromptHash !== currentExtraSystemPromptHash) return { invalidatedReason: "system-prompt" }.
Under session.dmScope:"main", direct conversations from different channels resolve to the same session key (one cliSessionBindings.claude-cli entry, one cached hash). The provider label is "static within a channel" but "variant across channels sharing one key", so the static-parts assumption (one session key implies one stable context) does not hold once channels are collapsed.
The same class as #82812 / #69118 / #64386, but the trigger is cross-channel session collapse rather than per-turn metadata or group intro drift, and it is NOT covered by the static-parts split that closed #82812.
Reproduction (deterministic, no restart, no compaction)
Agent on claude-cli backend. Config session.dmScope: "main". Agent reachable on two channels (for example dashboard WebChat and Telegram DM).
Send a direct message on channel A (Telegram). Note the reply; confirm log shows reuse=reusable.
Within a couple of minutes (inside the idle window, no restart), send a direct message to the same agent on channel B (dashboard).
Observe gateway log on the channel B turn: cli session reset: provider=claude-cli reason=system-prompt cli exec: provider=claude-cli ... useResume=false reuse=invalidated:system-prompt
The channel B reply has no memory of the channel A turn. Switching back to channel A resets again.
Expected: a single logical DM that the operator intentionally consolidated via dmScope:"main" reuses one CLI session across channels; turn N+1 remembers turn N regardless of which channel delivered it.
Actual: every channel switch invalidates the CLI session and wipes conversational context.
Two involuntary resets within 90 seconds, each reuse=invalidated:system-prompt with historyPrompt=none (full context wipe), triggered purely by alternating the delivery channel of one logical DM, after 2+ hours of stable single-channel reuse of the same CLI session. No restart, no config change, no compaction in the window.
Impact
Operators who set dmScope:"main" specifically to get one continuous assistant across channels get the opposite: guaranteed context loss on every channel switch. Severity is amplified by the absence of a raw-message reseed fallback for the claude-cli backend (see #58818): each of these resets is a total wipe, not a graceful re-seed.
Suggested direction (non-prescriptive)
Any one of: (a) exclude the channel/provider label from directChatContext when it feeds extraSystemPromptStaticParts (normalize the provider token out of the reuse-hash input, the same spirit as the #82812 metadata exclusion); (b) keep the channel context in the prompt but not in the reuse identity; (c) scope the cliSessionBindings entry per delivery channel even when the logical session key is collapsed by dmScope:"main".
Environment
OpenClaw 2026.5.7, claude-cli backend, session.dmScope: "main", agent reachable on dashboard WebChat and Telegram DM. Self-hosted Docker gateway.
Related: #82812 (closed, per-turn metadata variant), #69118 (open, group intro variant), #64386 (mcpConfigHash sibling), #58818 (missing raw-tail fallback that makes this catastrophic).
Summary
Sibling of #82812 (closed) and #69118 (open). #82812 fixed per-turn metadata bleeding into
extraSystemPromptHashby hashing only the static parts. However one of those "static" parts,directChatContext, embeds the channel/provider label, which is NOT stable when a single CLI session binding serves more than one channel. Withsession.dmScope:"main"(or any route binding that collapses multiple delivery channels onto one session key), alternating the channel a direct conversation arrives on flipsdirectChatContextbetween, for example, "Telegram" and "WebChat". That changesextraSystemPromptHash, soresolveCliSessionReusereturnsinvalidatedReason: "system-prompt"and the agent starts a freshclaudesession with no memory of the prior turns.Net user-visible effect: with
claude-clibackend plusdmScope:"main", moving a conversation between the dashboard and Telegram (same agent, same logical DM) makes the agent lose all prior context on the switch turn. Confirmed reproducible on OpenClaw 2026.5.7.Root cause (source, 2026.5.7)
src/auto-reply/reply/get-reply-run.tsbuilds the reuse-hash input:extraSystemPromptStaticParts = [directChatContext, groupChatContext, groupIntro, groupSystemPrompt, execOverrideHint](comment: "Static parts only (no per-message inbound metadata) for CLI session reuse hashing"). This is the bug: per-turn dynamic context bleeds into extraSystemPromptHash, triggering false cli session reset + A-type empty output #82812 mitigation, and it correctly excludesinboundMetaPrompt.src/auto-reply/reply/groups.tsbuildDirectChatContextemitsYou are in a ${providerLabel} direct conversation.andresolveProviderLabel(same file) returns"WebChat"for internal channels and the capitalized provider name (for example"Telegram") otherwise. So the string is channel dependent.src/agents/cli-runner/prepare.tshashes that block (extraSystemPromptHash = hashCliSessionText(params.extraSystemPromptStatic ...)).src/agents/cli-session.tsresolveCliSessionReuse:if (storedExtraSystemPromptHash !== currentExtraSystemPromptHash) return { invalidatedReason: "system-prompt" }.session.dmScope:"main", direct conversations from different channels resolve to the same session key (onecliSessionBindings.claude-clientry, one cached hash). The provider label is "static within a channel" but "variant across channels sharing one key", so the static-parts assumption (one session key implies one stable context) does not hold once channels are collapsed.The same class as #82812 / #69118 / #64386, but the trigger is cross-channel session collapse rather than per-turn metadata or group intro drift, and it is NOT covered by the static-parts split that closed #82812.
Reproduction (deterministic, no restart, no compaction)
claude-clibackend. Configsession.dmScope: "main". Agent reachable on two channels (for example dashboard WebChat and Telegram DM).reuse=reusable.cli session reset: provider=claude-cli reason=system-promptcli exec: provider=claude-cli ... useResume=false reuse=invalidated:system-promptExpected: a single logical DM that the operator intentionally consolidated via
dmScope:"main"reuses one CLI session across channels; turn N+1 remembers turn N regardless of which channel delivered it.Actual: every channel switch invalidates the CLI session and wipes conversational context.
Confirmed reproduction (operator capture, OpenClaw 2026.5.7, logs scrubbed)
Before the test: 2+ hours of continuous single-channel (Telegram DM) turns, every one reusing one CLI session:
Channel switch (Telegram to dashboard WebChat):
Switch back (WebChat to Telegram):
Two involuntary resets within 90 seconds, each
reuse=invalidated:system-promptwithhistoryPrompt=none(full context wipe), triggered purely by alternating the delivery channel of one logical DM, after 2+ hours of stable single-channel reuse of the same CLI session. No restart, no config change, no compaction in the window.Impact
Operators who set
dmScope:"main"specifically to get one continuous assistant across channels get the opposite: guaranteed context loss on every channel switch. Severity is amplified by the absence of a raw-message reseed fallback for the claude-cli backend (see #58818): each of these resets is a total wipe, not a graceful re-seed.Suggested direction (non-prescriptive)
Any one of: (a) exclude the channel/provider label from
directChatContextwhen it feedsextraSystemPromptStaticParts(normalize the provider token out of the reuse-hash input, the same spirit as the #82812 metadata exclusion); (b) keep the channel context in the prompt but not in the reuse identity; (c) scope thecliSessionBindingsentry per delivery channel even when the logical session key is collapsed bydmScope:"main".Environment
OpenClaw 2026.5.7,
claude-clibackend,session.dmScope: "main", agent reachable on dashboard WebChat and Telegram DM. Self-hosted Docker gateway.Related: #82812 (closed, per-turn metadata variant), #69118 (open, group intro variant), #64386 (mcpConfigHash sibling), #58818 (missing raw-tail fallback that makes this catastrophic).