Skip to content

feat: add --session-key support to system wake/event#35125

Closed
BrennerSpear wants to merge 1 commit into
openclaw:mainfrom
BrennerSpear:feat/session-key-wake-v5
Closed

feat: add --session-key support to system wake/event#35125
BrennerSpear wants to merge 1 commit into
openclaw:mainfrom
BrennerSpear:feat/session-key-wake-v5

Conversation

@BrennerSpear

Copy link
Copy Markdown
Contributor

What

Add optional --session-key parameter to all wake/system-event entry points so callers can deliver events to a specific agent session instead of default fan-out.

Supersedes #34944 with fixes for review feedback.

Entry Points

  • CLI: openclaw system event "text" --session-key <key>
  • WS wake method: { method: "wake", params: { text, mode, sessionKey } }
  • WS system-event method: { method: "system-event", params: { text, sessionKey, ... } }
  • HTTP /hooks/wake: POST { text, mode, sessionKey } — gated by hooks.allowRequestSessionKey policy
  • HTTP hook mappings: { action: "wake", sessionKey: "hook:{{payload.id}}" }

Core Behavior

  • With sessionKey: Canonicalize, validate, enqueue event to that specific session, heartbeat-wake that session
  • Without sessionKey: Preserve existing fan-out behavior — all agents get nudged (no behavioral change)

Safety

  • Cross-agent session keys throw errors (never silently reroute)
  • Malformed agent-scoped keys (< 3 segments like agent:ops) are rejected
  • HTTP hook sessionKey gated by hooks.allowRequestSessionKey policy + prefix allowlist
  • dispatchWakeHook returns result object (errors propagated, not swallowed)
  • State mutation only after validation succeeds

Changes since v4 (#34944)

  • Fixed: next-heartbeat mode with non-main sessionKey now always triggers requestHeartbeatNow — previously events would silently stall because the heartbeat runner doesn't scan arbitrary session keys
  • Fixed: Same stall in hooks wake dispatch path
  • Added: Defensive comments for intentionally unreachable !wakeResult.ok guards in no-sessionKey branches

Testing

  • 23 files changed, ~1287 insertions
  • 101 tests pass across 8 unit test files + integration tests
  • All 9 anti-patterns from prior failed attempts verified avoided
  • Codex review: 9/9 pass, SHIP verdict

Closes #34944

@openclaw-barnacle openclaw-barnacle Bot added app: macos App: macos app: web-ui App: web-ui gateway Gateway runtime cli CLI command changes size: XL labels Mar 5, 2026
@greptile-apps

greptile-apps Bot commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds an optional --session-key parameter to all wake/system-event entry points (CLI, WebSocket wake, WebSocket system-event, HTTP /hooks/wake, and hook mappings), allowing callers to deliver events to a specific agent session rather than the default fan-out to all agents. The implementation includes canonicalization of raw session keys, cross-agent safety checks (throwing errors instead of silently rerouting), and policy enforcement for HTTP hook paths via allowRequestSessionKey and allowedSessionKeyPrefixes config. Test coverage is extensive (101 tests across 8 unit files plus integration tests).

Key changes:

  • New canonicalizeWakeSessionKey utility in session-utils.ts that mirrors resolveHeartbeatSession two-step logic (global scope early-out, agent prefix normalization, malformed key rejection, cross-agent guard)
  • dispatchWakeHook in server/hooks.ts correctly calls requestHeartbeatNow for targeted sessions even in next-heartbeat mode, fixing the stall bug
  • cron.ts wake handler correctly calls requestHeartbeatNow for targeted sessions

One functional bug found: the system-event WS handler in system.ts enqueues events to the targeted session key but does NOT call requestHeartbeatNow, meaning events sent to non-main session keys via system-event will silently stall — the heartbeat runner does not scan arbitrary session keys, so the session will never wake to consume the event. This is the exact same stall bug the PR fixes for the wake and hooks.wake paths, but the fix was not applied here.

Confidence Score: 3/5

  • One functional logic bug in new functionality: system-event WS handler doesn't trigger heartbeat for targeted sessions, causing silent stalls. Other paths (wake, hooks) correctly implemented.
  • The PR introduces a medium-severity functional bug for the system-event targeted dispatch path — events to non-main session keys will silently stall — but all other paths (wake, hooks wake, CLI) are correctly implemented and safe. The overall architecture is sound and the core canonicalization logic is correct and well-tested. However, the system-event WS handler is missing the requestHeartbeatNow call for targeted sessions, which means a user-facing entry point explicitly listed in the PR description will silently drop events to non-main session keys. This is the same bug the PR set out to fix in other paths, but the fix was not applied here. Existing fan-out behavior is unaffected and the security policy enforcement is correct.
  • src/gateway/server-methods/system.ts — the system-event handler enqueues to targetSessionKey but never triggers requestHeartbeatNow for that session, silently stalling events to non-main sessions.

Last reviewed commit: c6c50be

Comment on lines 141 to 143
} else {
enqueueSystemEvent(text, { sessionKey });
enqueueSystemEvent(text, { sessionKey: targetSessionKey ?? mainSessionKey });
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Targeted system-event silently stalls — missing requestHeartbeatNow

When targetSessionKey is set (the caller passed a sessionKey pointing to a non-main session), the event is enqueued to that session but requestHeartbeatNow is never called with that session key.

The PR description explicitly identifies this as the root-cause bug that was fixed in the other two paths: "Fixed: next-heartbeat mode with non-main sessionKey now always triggers requestHeartbeatNow — previously events would silently stall because the heartbeat runner doesn't scan arbitrary session keys."

Both cron.ts (line 67) and dispatchWakeHook in server/hooks.ts (line 51) were updated to call requestHeartbeatNow for targeted sessions. This path was missed.

Without the heartbeat nudge, events enqueued to a non-main session will never be consumed — the heartbeat runner doesn't poll arbitrary session keys, so the targeted session stays asleep indefinitely.

The fix should mirror what was done in cron.ts: after the enqueueSystemEvent call on line 142, add a guard that checks if (targetSessionKey) and then calls requestHeartbeatNow with that session key. Also add the import for requestHeartbeatNow from ../../infra/heartbeat-wake.js at the top of the file.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/server-methods/system.ts
Line: 141-143

Comment:
**Targeted `system-event` silently stalls — missing `requestHeartbeatNow`**

When `targetSessionKey` is set (the caller passed a `sessionKey` pointing to a non-main session), the event is enqueued to that session but `requestHeartbeatNow` is never called with that session key.

The PR description explicitly identifies this as the root-cause bug that was fixed in the other two paths: "Fixed: next-heartbeat mode with non-main sessionKey now always triggers requestHeartbeatNow — previously events would silently stall because the heartbeat runner doesn't scan arbitrary session keys."

Both `cron.ts` (line 67) and `dispatchWakeHook` in `server/hooks.ts` (line 51) were updated to call `requestHeartbeatNow` for targeted sessions. This path was missed.

Without the heartbeat nudge, events enqueued to a non-main session will never be consumed — the heartbeat runner doesn't poll arbitrary session keys, so the targeted session stays asleep indefinitely.

The fix should mirror what was done in `cron.ts`: after the `enqueueSystemEvent` call on line 142, add a guard that checks `if (targetSessionKey)` and then calls `requestHeartbeatNow` with that session key. Also add the import for `requestHeartbeatNow` from `../../infra/heartbeat-wake.js` at the top of the file.

How can I resolve this? If you propose a fix, please make it concise.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c6c50beff8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

}
} else {
enqueueSystemEvent(text, { sessionKey });
enqueueSystemEvent(text, { sessionKey: targetSessionKey ?? mainSessionKey });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Wake targeted system-event sessions after enqueueing

When system-event is called with a non-main sessionKey, this branch enqueues into that specific session but never triggers requestHeartbeatNow for it, so the event can remain undelivered indefinitely unless another unrelated wake targets the same session. This regression affects the new sessionKey path specifically: unlike the updated wake/hook flows, non-main session queues are not polled by default heartbeat fan-out, so targeted system events silently stall.

Useful? React with 👍 / 👎.

@BrennerSpear

Copy link
Copy Markdown
Contributor Author

Superseded by #35231

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: macos App: macos app: web-ui App: web-ui cli CLI command changes gateway Gateway runtime size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant