openclaw - ✅(Solved) Fix fix(gateway): emit sessions.changed after startup pending-action recovery [1 pull requests, 2 comments, 2 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#58597Fetched 2026-04-08 02:00:26
View on GitHub
Comments
2
Participants
2
Timeline
3
Reactions
0
Author
Participants
Timeline (top)
commented ×2cross-referenced ×1

Discovered during code review of #52422 (sessions_manage tool). The recovery path was added to handle deferred actions that survive gateway restarts. The gap is cosmetic (no data loss — clients get fresh state on next poll/reconnect) but breaks the sessions.subscribe contract.

🤖 Generated with Claude Code

Root Cause

Discovered during code review of #52422 (sessions_manage tool). The recovery path was added to handle deferred actions that survive gateway restarts. The gap is cosmetic (no data loss — clients get fresh state on next poll/reconnect) but breaks the sessions.subscribe contract.

🤖 Generated with Claude Code

Fix Action

Fixed

PR fix notes

PR #52422: feat(tools): add sessions_manage tool with semantic compaction and deferred self-session support

Description (problem / solution / changelog)

Summary

Add a sessions_manage tool with LLM-based semantic compaction and deferred self-session support, replacing transcript trimming with structured summarization.

What's new

  1. sessions.compactSemantic gateway RPC — calls compactEmbeddedPiSession for real semantic compaction producing structured summaries (Goal/Progress/Decisions/Next Steps). Supports optional instructions parameter for guided compaction.

  2. Deferred self-session operations — both compact and reset use deferred mode when the target session has an active run. The action persists as pendingAction in the session store and executes after the run drains via waitForEmbeddedPiRunEnd. Eliminates the self-reset deadlock entirely.

  3. sessions_manage tool — exposes compact/reset to agents. Always requests deferred mode (gateway only defers when the target is actually running). Cross-session operations on idle sessions execute immediately.

  4. Pending-action recovery — survives gateway restarts. On startup, scans session stores and replays recent pending actions. Active sessions get deferred waiters; idle stale actions (>4h) are cleared.

Deferred action lifecycle

  • scheduledAt matching on all marker operations prevents newer actions from being clobbered
  • Markers only cleared on confirmed success — failures leave the marker for next restart
  • Timeout (4h) leaves marker in place for recovery
  • Reset reason ("new" vs "reset") preserved across restarts
  • Active-run check runs before stale expiry to protect long-running sessions

Known limitations

  • sessions.changed not emitted from startup recoveryemitSessionsChanged requires GatewayRequestContext not available in the recovery module. Tracked in #58597.
  • Failed startup recovery not retried in-process — recovery runs once at boot; transient failures leave the marker for the next restart rather than retrying in a loop.

Files changed (22)

AreaFilesWhat
Toolsessions-manage-tool.tsNew tool implementation
Gateway RPCserver-methods/sessions.tscompactSemantic handler + deferred paths
Recoveryserver-pending-actions.ts + testStartup recovery module
Typesconfig/sessions/types.tspendingAction field on SessionEntry
Wiringopenclaw-tools.ts, tool-catalog.ts, pi-tools.policy.ts, etc.Registration, deny list, scope groups

Test plan

  • Unit tests for pending-action recovery (9 cases: reset, compact, stale cleanup, post-boot skip, active-run deferral, result checking, legacy key cleanup)
  • codex review --base upstream/main — 10 rounds, all actionable findings addressed
  • Rebased on v2026.3.31 (resolved dangerous-tools.ts conflict — upstream replaced ACP dangerous-tool list with semantic approval classes)
  • Manual: self-session compact via tool, verify deferred execution after run ends
  • Manual: cross-session compact of idle session, verify immediate execution
  • Manual: gateway restart with pending action, verify recovery

🤖 AI-assisted (Claude Opus 4.6). Fully tested, all code understood.

Changed files

  • src/agents/openclaw-tools.ts (modified, +7/-0)
  • src/agents/pi-embedded-subscribe.tools.ts (modified, +1/-0)
  • src/agents/pi-tools.policy.ts (modified, +2/-0)
  • src/agents/sandbox/constants.ts (modified, +1/-0)
  • src/agents/system-prompt.ts (modified, +3/-0)
  • src/agents/tool-catalog.ts (modified, +8/-0)
  • src/agents/tool-display-overrides.json (modified, +5/-0)
  • src/agents/tool-mutation.ts (modified, +2/-0)
  • src/agents/tools/sessions-access.ts (modified, +13/-1)
  • src/agents/tools/sessions-manage-tool.ts (added, +189/-0)
  • src/config/sessions/types.ts (modified, +11/-0)
  • src/gateway/method-scopes.ts (modified, +1/-0)
  • src/gateway/protocol/index.ts (modified, +5/-0)
  • src/gateway/protocol/schema/protocol-schemas.ts (modified, +2/-0)
  • src/gateway/protocol/schema/sessions.ts (modified, +13/-0)
  • src/gateway/protocol/schema/types.ts (modified, +1/-0)
  • src/gateway/server-methods-list.ts (modified, +1/-0)
  • src/gateway/server-methods/sessions.ts (modified, +323/-1)
  • src/gateway/server-pending-actions.test.ts (added, +331/-0)
  • src/gateway/server-pending-actions.ts (added, +349/-0)
  • src/gateway/server-startup.ts (modified, +16/-0)
  • src/security/dangerous-tools.ts (modified, +2/-0)
RAW_BUFFERClick to expand / collapse

Problem

When a deferred sessions_manage action (compact or reset) is recovered after a gateway restart, the recovery module (server-pending-actions.ts) mutates session state but never emits sessions.changed. Connected UI clients and subscribers that reconnected during the 2-second startup delay keep showing stale session metadata until they manually refresh.

The normal sessions.reset and sessions.compactSemantic handlers both emit sessions.changed via emitSessionsChanged(context, ...), but the recovery path can't — emitSessionsChanged requires a GatewayRequestContext (for broadcastToConnIds and getSessionEventSubscriberConnIds), and the recovery module only receives a { log } parameter from server-startup.ts.

Proposed fix

Either:

  1. Thread gateway context into recoverPendingActions — change the startup call site to pass the context, allowing recovery to broadcast like the normal handlers
  2. Extract broadcast into a standalone function — decouple sessions.changed emission from request context so any module can trigger it

Option 1 is more straightforward; option 2 is cleaner long-term.

Context

Discovered during code review of #52422 (sessions_manage tool). The recovery path was added to handle deferred actions that survive gateway restarts. The gap is cosmetic (no data loss — clients get fresh state on next poll/reconnect) but breaks the sessions.subscribe contract.

🤖 Generated with Claude Code

extent analysis

TL;DR

Passing the GatewayRequestContext to the recovery module or decoupling the sessions.changed emission from the request context can resolve the issue of stale session metadata.

Guidance

  • Identify the recoverPendingActions function in server-pending-actions.ts and consider modifying it to accept the GatewayRequestContext to enable broadcasting of sessions.changed.
  • Evaluate the feasibility of extracting the broadcast logic into a standalone function that can be triggered by any module, including the recovery module.
  • Verify that the proposed fix does not introduce any unintended side effects on the sessions.subscribe contract or other related functionality.
  • Test the fix by simulating a gateway restart and verifying that connected UI clients and subscribers receive updated session metadata without requiring a manual refresh.

Example

// Example of passing GatewayRequestContext to recoverPendingActions
function recoverPendingActions(context: GatewayRequestContext, log: any) {
  // ...
  emitSessionsChanged(context, ...);
}

// Example of extracting broadcast into a standalone function
function broadcastSessionsChanged(sessionData: any) {
  // Decoupled broadcast logic
}

Notes

The choice between the two proposed fixes depends on the specific requirements and constraints of the project. Passing the GatewayRequestContext is a more straightforward solution, while extracting the broadcast logic into a standalone function is a cleaner and more maintainable approach.

Recommendation

Apply the workaround of passing the GatewayRequestContext to the recovery module, as it is a more straightforward solution that can be implemented quickly to resolve the issue.

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