openclaw - ✅(Solved) Fix [Feature]: Symmetric Session Eviction Real-Time Realities (WebSocket Heartbeats) [1 pull requests, 1 comments, 1 participants]

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…
GitHub stats
openclaw/openclaw#63374Fetched 2026-04-09 07:54:33
View on GitHub
Comments
1
Participants
1
Timeline
3
Reactions
0
Author
Participants
Timeline (top)
commented ×1cross-referenced ×1labeled ×1

Push deterministic, real-time WebSocket "heartbeats" specifically for tracking system-forced session evictions (deleted or reset lifecycle events) through existing stream connections.

Root Cause

Because this relies entirely on our existing WebSocket payload formatting, this change will be natively 100% backwards compatible. Any existing listener that is unaware of the socket_drain event will safely drop the unrecognized packet string natively without breaking active workflows or causing blocking terminal errors.

Fix Action

Fixed

PR fix notes

PR #63378: feat(websocket-heartbeats) recognize session evictions

Description (problem / solution / changelog)

Summary

🦞 A deterministic way for downstream systems to recognize session evictions without relying on legacy polling mechanics.

  • Problem: Systems traditionally relied on inefficient polling to detect forced session evictions.
  • Why it matters: Connected subsystems require deterministic, real-time WebSocket "heartbeats" pushed from the gateway when specific session events occur, rather than relying on API polling. This reduces unnecessary upstream traffic and latency.
  • What changed:
    • Modified src/gateway/server-broadcast.ts to add the socket.drain event to EVENT_SCOPE_GUARDS under [READ_SCOPE], mirroring how standard transcript updates are streamed.
    • Modified src/gateway/server.impl.ts's onSessionLifecycleEvent to explicitly broadcast the socket.drain event down all WebSocket channels to any clients actively subscribed when a session is invalidated via reason === 'reset' || reason === 'deleted'.
  • What did NOT change (scope boundary): No existing contracts were broken. This is a 100% additive change. No legacy polling endpoints were modified or removed.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #63374
  • Related #
  • This PR fixes a bug or regression

Root Cause (if applicable)

N/A

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/gateway/session-message-events.test.ts
  • Scenario the test should lock in: Ensure socket.drain events are pushed on invalidation without breaking any existing event scopes.
  • Why this is the smallest reliable guardrail: Tests gateway socket mirroring efficiently and completely.
  • Existing test that already covers this (if any):
  • If no new test is added, why not:

User-visible / Behavior Changes

None. Because the implementation simply ferries a net-new event type down the exact same WebSocket channels using existing JSON structures, any legacy consumer or system interacting with OpenClaw that doesn't explicitly listen for 'socket.drain' will organically ignore the new payload.

Diagram (if applicable)

N/A

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (Yes)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation: Extending standard real-time websocket pushes to support a purely additive socket.drain event utilizing the existing, secured [READ_SCOPE] guardrails.

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Base Node
  • Model/provider:
  • Integration/channel (if any):
  • Relevant config (redacted):

Steps

  1. Connect to the gateway standard transcript websocket with [READ_SCOPE].
  2. Manually trigger a session reset or deletion.
  3. Verify socket.drain is received successfully.

Expected

  • socket.drain is broadcast cleanly without disrupting active session.message streams.

Actual

  • socket.drain pushes correctly only when the matching lifecycle event occurs.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: Ran the comprehensive local unit testing suite on gateway integrations to ensure the new payloads pass seamlessly.
  • Edge cases checked: Validated EVENT_SCOPE_GUARDS logic updates correctly limits distribution without leaking scope.
  • What you did not verify:

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps: 100% Backwards Compatible. Legacy clients lacking explicit 'socket.drain' handlers will securely drop the unrecognized event frames from the stream without generating terminal errors.

Risks and Mitigations

None.


Please let me know if you need any adjustments or if I can answer any questions about the approach here. Thank you for reviewing this submission! 👍

Changed files

  • src/gateway/server-broadcast.ts (modified, +1/-0)
  • src/gateway/server-methods-list.ts (modified, +1/-0)
  • src/gateway/server-methods/agent.ts (modified, +5/-0)
  • src/gateway/server-methods/sessions.ts (modified, +6/-5)
  • src/gateway/server.impl.ts (modified, +8/-0)
  • src/gateway/session-message-events.test.ts (modified, +148/-1)
RAW_BUFFERClick to expand / collapse

Summary

Push deterministic, real-time WebSocket "heartbeats" specifically for tracking system-forced session evictions (deleted or reset lifecycle events) through existing stream connections.

Problem to solve

The current OpenClaw architecture relies heavily on API polling to stay aware of system-forced session evictions. While standard transcript updates stream immediately to connected systems, a downstream system has no organic way of knowing if its underlying session has been destroyed externally by the server. This asymmetry forces subsystems and integrations to build inefficient polling loops to continuously check session validity, fundamentally violating the push-first nature of our data structures.

Proposed solution

We need a deterministic methodology to symmetrically push system eviction knowledge downstream through existing WebSocket connections. I propose we modify src/gateway/server.impl.ts's onSessionLifecycleEvent to actively broadcast a purely additive socket_drain event down all channels natively to any subscribed clients using the default [READ_SCOPE] guardrails.

Alternatives considered

  • Aggressive Polling Engine: Standardizing how often clients must ping the API layer to verify session integrity creates unnecessary network overhead and backend computational taxation.
  • REST Webhooks: Forcing an external REST webhook pipeline specifically for eviction necessitates backend port-opening requirements and complex integrations that heavily complicate lightweight downstream consumers or isolated integrations.

Impact

  • Affected users/systems/channels: Any downstream system, orchestrator, or user interface maintaining an active gateway connection tracking session lifecycles.
  • Severity: Workflow Blocker for performance-sensitive integrations.
  • Frequency: Persistent/Constant (affects all integrations trying to perform cleanup upon standard eviction lifecycles).
  • Consequence: Causes unnecessary, heavy upstream network latency and API tax, creating delayed cleanups and leaving orphaned connections open.

Evidence/examples

Currently, if a downstream gateway implementation clears out session data, there are absolutely no packets broadcasted downwards to matching connected sockets; the consumers must actively fire GET /v1/chat/sessions/:id requests continuously to monitor their environment state bounds, which mimics anti-patterns seen in legacy long-polling architectures.

Additional information

Because this relies entirely on our existing WebSocket payload formatting, this change will be natively 100% backwards compatible. Any existing listener that is unaware of the socket_drain event will safely drop the unrecognized packet string natively without breaking active workflows or causing blocking terminal errors.

extent analysis

TL;DR

Modify the onSessionLifecycleEvent function in src/gateway/server.impl.ts to broadcast a socket_drain event to subscribed clients when a session is deleted or reset.

Guidance

  • Identify the specific session lifecycle events (deleted or reset) that should trigger the socket_drain event broadcast.
  • Update the onSessionLifecycleEvent function to handle these events and send the socket_drain event to all connected clients with the default [READ_SCOPE] guardrails.
  • Verify that the socket_drain event is being sent correctly by monitoring the WebSocket connections and checking for the event in the client-side code.
  • Test the backwards compatibility of the change by ensuring that existing listeners that are unaware of the socket_drain event do not break or cause errors.

Example

// src/gateway/server.impl.ts
onSessionLifecycleEvent(event) {
  if (event.type === 'deleted' || event.type === 'reset') {
    // Broadcast socket_drain event to all connected clients
    this.broadcast('socket_drain', { sessionId: event.sessionId });
  }
}

Notes

The proposed solution relies on modifying the existing onSessionLifecycleEvent function, which may have unintended consequences if not thoroughly tested. Additionally, the backwards compatibility of the change should be verified to ensure that existing listeners are not affected.

Recommendation

Apply the proposed workaround by modifying the onSessionLifecycleEvent function to broadcast the socket_drain event, as it provides a deterministic and efficient way to push system eviction knowledge downstream through existing WebSocket connections.

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