Skip to content

fix(session): tighten direct-session webchat routing matching#37867

Merged
vincentkoc merged 2 commits intomainfrom
vincentkoc-code/fix-strict-direct-session-routing
Mar 6, 2026
Merged

fix(session): tighten direct-session webchat routing matching#37867
vincentkoc merged 2 commits intomainfrom
vincentkoc-code/fix-strict-direct-session-routing

Conversation

@vincentkoc
Copy link
Copy Markdown
Member

Summary

  • Problem: session-delivery.ts treated any session key containing :direct: or :dm: as eligible for WebChat route override, even when the key shape was malformed.
  • Why it matters: malformed or non-chat session keys could poison persisted lastChannel / lastTo routing state during internal WebChat turns.
  • What changed: the routing override now accepts only strict supported direct-session shapes, including legacy :dm: keys and direct thread/topic descendants.
  • What did NOT change (scope boundary): deriveSessionChatType() remains best-effort for broader classification; this PR only hardens the routing-affecting override path.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • 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

User-visible / Behavior Changes

WebChat route overrides now apply only to canonical direct-session keys instead of any key that happens to contain a direct / dm token.

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node 22 + pnpm workspace
  • Model/provider: N/A
  • Integration/channel (if any): WebChat / session routing
  • Relevant config (redacted): default session store, dmScope coverage via tests

Steps

  1. Persist an external lastChannel / lastTo for a session.
  2. Send a WebChat-originated turn with a malformed session key like agent:main:cron:job-1:dm or agent:main:main:direct.
  3. Confirm the persisted external route is preserved.
  4. Repeat with valid direct-session keys such as agent:main:telegram:direct:123456 and confirm WebChat still overrides routing.

Expected

  • Valid direct-session keys continue to let WebChat own direct-session UI routing.
  • Malformed direct-like keys do not override persisted external routing.

Actual

  • Verified by targeted unit coverage and existing session routing tests.

Evidence

  • 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: strict direct keys, legacy :dm: keys, per-account direct keys, direct thread/topic keys, and malformed direct-like keys.
  • Edge cases checked: main, cron, subagent, missing peer ids, and unsupported suffixes no longer qualify for WebChat override.
  • What you did not verify: live Gateway/WebSocket repro against a running external deployment.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert this PR.
  • Files/config to restore: src/auto-reply/reply/session-delivery.ts
  • Known bad symptoms reviewers should watch for: valid direct-session WebChat turns unexpectedly preserving stale external routes.

Risks and Mitigations

  • Risk: strict matching could reject a valid direct-session variant that was previously accepted implicitly.
    • Mitigation: test coverage includes supported canonical, legacy, per-account, and thread/topic direct shapes.

@vincentkoc vincentkoc self-assigned this Mar 6, 2026
@openclaw-barnacle openclaw-barnacle bot added size: S maintainer Maintainer-authored PR labels Mar 6, 2026
@vincentkoc vincentkoc marked this pull request as ready for review March 6, 2026 13:53
@vincentkoc vincentkoc merged commit 9fed9f1 into main Mar 6, 2026
30 checks passed
@vincentkoc vincentkoc deleted the vincentkoc-code/fix-strict-direct-session-routing branch March 6, 2026 13:53
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 6, 2026

Greptile Summary

This PR hardens the WebChat routing override in session-delivery.ts by replacing a loose substring-presence check with a strict, position-aware isDirectSessionKey function that validates the channel segment, requires a non-empty peer ID, and permits only a well-formed thread/topic tail. The change prevents malformed session keys (e.g. agent:main:cron:job-1:dm, agent:main:main:direct) from poisoning lastChannel/lastTo routing state during internal WebChat turns.

Key changes:

  • New isDirectSessionKey: Parses the scoped rest of the session key structurally — confirms the channel segment maps to a deliverable integration, finds the direct/dm marker at an expected position, requires a non-empty peer ID, and only accepts an optional thread/topic two-token tail.
  • Targeted test coverage: Covers canonical, per-account, legacy :dm:, and thread/topic positive cases, plus cron, subagent, missing-peer-ID, and unsupported-suffix negative cases. One minor gap: the short dm:peerId form (e.g. agent:main:dm:user-1) is supported but absent from the positive test matrix.

Confidence Score: 4/5

  • Safe to merge; the stricter matching is correct and well-tested for all documented direct-session shapes, with one minor test gap.
  • The implementation logic is sound and produces the expected result for all test cases. The removal of the broad deriveSessionChatType call is clean. The new structural parser correctly gates on deliverable channel identity, peer ID presence, and an allowlisted tail. The only gap is a missing test for the short dm:peerId form, which does not affect the correctness of the implementation but should be added to make the legacy coverage exhaustive as the PR description states.
  • src/auto-reply/reply/session-delivery.test.ts — add the missing "agent:main:dm:user-1" test case to the positive matrix.

Last reviewed commit: 02e7525

Comment on lines +5 to +12
it.each([
"agent:main:direct:user-1",
"agent:main:telegram:direct:123456",
"agent:main:telegram:account-a:direct:123456",
"agent:main:telegram:dm:123456",
"agent:main:telegram:direct:123456:thread:99",
"agent:main:telegram:account-a:direct:123456:topic:ops",
])("lets webchat override persisted routes for strict direct key %s", (sessionKey) => {
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.

The positive-case matrix includes "agent:main:direct:user-1" (short direct: form without a channel prefix), but doesn't include the analogous short dm: form — e.g. "agent:main:dm:user-1". The implementation at line 66 of session-delivery.ts hits DIRECT_SESSION_MARKERS.has(parts[0]) for both "direct" and "dm", so the short dm form is fully supported; it is just absent from the test matrix.

Given the PR description explicitly states "test coverage includes... legacy :dm: keys", adding this case would make the legacy coverage exhaustive.

Suggested change
it.each([
"agent:main:direct:user-1",
"agent:main:telegram:direct:123456",
"agent:main:telegram:account-a:direct:123456",
"agent:main:telegram:dm:123456",
"agent:main:telegram:direct:123456:thread:99",
"agent:main:telegram:account-a:direct:123456:topic:ops",
])("lets webchat override persisted routes for strict direct key %s", (sessionKey) => {
it.each([
"agent:main:direct:user-1",
"agent:main:dm:user-1",
"agent:main:telegram:direct:123456",
"agent:main:telegram:account-a:direct:123456",
"agent:main:telegram:dm:123456",
"agent:main:telegram:direct:123456:thread:99",
"agent:main:telegram:account-a:direct:123456:topic:ops",
])("lets webchat override persisted routes for strict direct key %s", (sessionKey) => {
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/auto-reply/reply/session-delivery.test.ts
Line: 5-12

Comment:
The positive-case matrix includes `"agent:main:direct:user-1"` (short `direct:` form without a channel prefix), but doesn't include the analogous short `dm:` form — e.g. `"agent:main:dm:user-1"`. The implementation at line 66 of `session-delivery.ts` hits `DIRECT_SESSION_MARKERS.has(parts[0])` for both `"direct"` and `"dm"`, so the short `dm` form is fully supported; it is just absent from the test matrix.

Given the PR description explicitly states "test coverage includes... legacy `:dm:` keys", adding this case would make the legacy coverage exhaustive.

```suggestion
  it.each([
    "agent:main:direct:user-1",
    "agent:main:dm:user-1",
    "agent:main:telegram:direct:123456",
    "agent:main:telegram:account-a:direct:123456",
    "agent:main:telegram:dm:123456",
    "agent:main:telegram:direct:123456:thread:99",
    "agent:main:telegram:account-a:direct:123456:topic:ops",
  ])("lets webchat override persisted routes for strict direct key %s", (sessionKey) => {
```

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

Copy link
Copy Markdown

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

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: 02e7525d04

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return false;
}
const scoped = parseAgentSessionKey(raw)?.rest ?? raw;
const parts = scoped.split(":").filter(Boolean);
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 Handle direct-session peer IDs that include colons

The new strict parser assumes : only separates structural tokens, but valid Matrix DM session keys embed MXIDs like @alice:example.org as the peer ID (extensions/matrix/src/matrix/monitor/handler.ts passes senderId into route key generation, and extensions/matrix/src/matrix/monitor/index.ts treats @...:... as a Matrix user ID). With const parts = scoped.split(":"), a key such as agent:main:matrix:direct:@alice:example.org is split so hasStrictDirectSessionTail sees an extra tail token and returns false, meaning resolveLastChannelRaw/resolveLastToRaw stop applying the WebChat direct-session override and can keep stale external routing for legitimate Matrix direct sessions.

Useful? React with 👍 / 👎.

handsdiff pushed a commit to handsdiff/activeclaw that referenced this pull request Mar 6, 2026
…aw#37867)

* fix(session): require strict direct key routing shapes

* test(session): cover direct route poisoning cases
jerrycaimin pushed a commit to jerrycaimin/openclaw that referenced this pull request Mar 6, 2026
…aw#37867)

* fix(session): require strict direct key routing shapes

* test(session): cover direct route poisoning cases
mrosmarin added a commit to mrosmarin/openclaw that referenced this pull request Mar 6, 2026
* main:
  Mattermost: harden interaction callback binding (openclaw#38057)
  WhatsApp: honor outbound mediaMaxMb (openclaw#38097)
  openai-image-gen: validate --background and --style options (openclaw#36762)
  Docs: align BlueBubbles media cap wording
  Telegram/Discord: honor outbound mediaMaxMb uploads (openclaw#38065)
  CI: run changed-scope on main pushes
  Skills/nano-banana-pro: clarify MEDIA token comment (openclaw#38063)
  nano-banana-pro: respect explicit --resolution when editing images (openclaw#36880)
  CI: drop unused install-smoke bootstrap
  fix(nano-banana-pro): remove space after MEDIA: token in generate_image.py (openclaw#18706)
  docs: context engine
  docs(config): list the context engine plugin slot
  docs(plugins): add context-engine manifest kind example
  docs(plugins): document context engine slots and registration
  docs(protocol): document slash-delimited schema lookup plugin ids
  docs(tools): document slash-delimited config schema lookup paths
  fix(session): tighten direct-session webchat routing matching (openclaw#37867)
  feature(context): extend plugin system to support custom context management (openclaw#22201)
  Gateway: allow slash-delimited schema lookup paths
Saitop pushed a commit to NomiciAI/openclaw that referenced this pull request Mar 8, 2026
…aw#37867)

* fix(session): require strict direct key routing shapes

* test(session): cover direct route poisoning cases
jenawant pushed a commit to jenawant/openclaw that referenced this pull request Mar 10, 2026
…aw#37867)

* fix(session): require strict direct key routing shapes

* test(session): cover direct route poisoning cases
dhoman pushed a commit to dhoman/chrono-claw that referenced this pull request Mar 11, 2026
…aw#37867)

* fix(session): require strict direct key routing shapes

* test(session): cover direct route poisoning cases
senw-developers pushed a commit to senw-developers/va-openclaw that referenced this pull request Mar 17, 2026
…aw#37867)

* fix(session): require strict direct key routing shapes

* test(session): cover direct route poisoning cases
V-Gutierrez pushed a commit to V-Gutierrez/openclaw-vendor that referenced this pull request Mar 17, 2026
…aw#37867)

* fix(session): require strict direct key routing shapes

* test(session): cover direct route poisoning cases
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 20, 2026
…aw#37867)

* fix(session): require strict direct key routing shapes

* test(session): cover direct route poisoning cases

(cherry picked from commit 9fed9f1)
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 20, 2026
…aw#37867)

* fix(session): require strict direct key routing shapes

* test(session): cover direct route poisoning cases

(cherry picked from commit 9fed9f1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant