-
-
Notifications
You must be signed in to change notification settings - Fork 80.7k
Conversation context assembled per-transport, not per-session: cross-channel reply binds to a stale proposal and executes it #95378
Copy link
Copy link
Closed
Labels
P1High-priority user-facing bug, regression, or broken workflow.High-priority user-facing bug, regression, or broken workflow.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.ClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.ClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.ClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.ClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.Channel message delivery can be lost, duplicated, or misrouted.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.Session, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.Very strong issue quality with high-confidence source-level or clear reproduction.
Description
Metadata
Metadata
Assignees
Labels
P1High-priority user-facing bug, regression, or broken workflow.High-priority user-facing bug, regression, or broken workflow.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.ClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.ClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.ClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.ClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.Channel message delivery can be lost, duplicated, or misrouted.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.Session, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.Very strong issue quality with high-confidence source-level or clear reproduction.
Type
Fields
Priority
None yet
Version: OpenClaw 2026.6.8 (844f405)
Severity: High — causes the agent to execute a wrong/unrelated action with confidence
Title
Conversation context is assembled per-transport, not per-session: a reply arriving on one channel cannot see the immediately-preceding turn that arrived on another channel (same shared agent session) → agent binds the reply to a stale, unrelated proposal and executes it.
Summary
When a single user interacts with one shared agent session (
agent:main:main) through two different ingresses — the iOS app (gateway-protocol client, sender idopenclaw-ios) and Telegram (channel plugin) — each turn's prompt context is built from only the originating ingress's own message store. The shared session contains all turns, but the per-turn context does not unify them. Result: the agent answers correctly for the corrupted context it received, but that context is missing the actual turn the user is replying to.Impact
The agent executed a state-mutating tool call (cron job edit) that the user never requested, because it bound an ambiguous confirmation to the last proposal visible in the Telegram context window instead of the trading-analysis turn the user was actually replying to (which came in via the iOS app). The same mechanism would fire on any tool (incl. higher-stakes ones) and is not an authorization/gating issue — the context itself is wrong.
Reproduction
Expected
The reply on ingress B is interpreted against the genuinely most-recent turn of the shared session, regardless of which ingress produced it.
Actual
The reply is interpreted against the most-recent message of ingress B's own store only. The cross-ingress turn is invisible.
Root cause (traced in dist 2026.6.8)
Two independent gaps stack so the cross-ingress turn is invisible from both context paths:
Per-channel recency block (Telegram).
bot-B3dz_0yx.jsbuilds the "Conversation context (selected for current message)" viamConversationContext({ cache: messageCache, chatId: msg.chat.id, recentLimit: 10, replyTargetWindowSize: 2 }).messageCache = createTelegramMessageCache({ scope: resolveTelegramMessageCacheScope(...) }), andresolveTelegramMessageCacheScope(storePath)returns${storePath}.telegram-messages.json— a Telegram-only message cache.recentLimit: 10andreplyTargetWindowSize: 2are hardcoded. Messages from the gateway-protocol/iOS ingress are never written to this cache, so they cannot appear in the block.Cached stable prefix (
messages[]). The compiled prompt'smessages[]is an oldest-anchored cached prefix (for prompt-cache reuse; observedcacheRead ≈ 149k). By design it excludes the freshest turns — those are expected to arrive via the per-channel recency block in (1). So a turn that is both too new for the cached prefix and from a different ingress (so absent from the Telegram block) is invisible from both paths.No config knob. There is no setting to unify context across ingresses:
channels.telegramhas no session/store/context config (telegram.session = null,agents.defaults.session = null); nopromptContext/conversationContext/historyScope/recentLimit/crossChannelkey exists in config. The behavior is hardcoded in dist.This is the classic multichannel (siloed) vs omnichannel (unified) distinction: OpenClaw routes multiple ingresses to one shared session (good), but assembles the model's recent-conversation context per-ingress (the defect).
Suggested fix
Assemble the per-turn recent-conversation context from the shared session transcript (which already contains every ingress's turns), not from a per-ingress cache — i.e. a session-scoped (not chat-scoped) recency window, or merge per-ingress caches by the unified session/actor id before building the prompt. Reference pattern: a single canonical conversation store keyed by user/session identity, with all ingresses normalized into it and context assembled from it (e.g. AWS Bedrock AgentCore Memory uses an identical
actor_idacross channels).Notes
recentLimit: 10andreplyTargetWindowSize: 2being hardcoded also limits any short-term mitigation via config.Filed from production logs (OpenClaw 2026.6.8). Personal data redacted.