Skip to content

fix(routing): include accountId in group/channel session keys#27750

Closed
scz2011 wants to merge 1 commit into
openclaw:mainfrom
scz2011:fix/discord-multi-account-routing-27731
Closed

fix(routing): include accountId in group/channel session keys#27750
scz2011 wants to merge 1 commit into
openclaw:mainfrom
scz2011:fix/discord-multi-account-routing-27731

Conversation

@scz2011

@scz2011 scz2011 commented Feb 26, 2026

Copy link
Copy Markdown

Fixes #27731

When using multiple Discord bot accounts with agent bindings, all messages were routed to the main agent regardless of binding configuration. The root cause was that session keys for group/channel messages did not include the accountId, causing messages from different accounts to the same channel to share the same session key.

This led to routing conflicts where whichever agent was last matched would capture all subsequent messages to that channel, regardless of which Discord account received the message.

Root Cause

The buildAgentPeerSessionKey() function in src/routing/session-key.ts was generating session keys for group/channel messages without including the accountId:

Old format: agent:{agentId}:{channel}:{peerKind}:{peerId}

This meant messages from different Discord accounts (e.g., designer, erp) to the same channel would get identical session keys, causing routing conflicts.

Changes

  • Modified buildAgentPeerSessionKey() to include accountId in group/channel session key format
  • New format: agent:{agentId}:{channel}:{accountId}:{peerKind}:{peerId}
  • Added comprehensive tests to verify accountId inclusion and multi-account isolation

Impact

  • Each Discord account now gets its own session namespace per channel
  • Agent bindings with accountId matching now work correctly
  • Messages route to the correct agent based on both channel and account
  • Resolves "discord channels unresolved" routing fallback to main agent

Testing

All existing tests pass, plus new tests added:

  • Verifies accountId is included in group/channel session keys
  • Verifies default accountId is used when not provided
  • Verifies different accounts to same channel get different session keys

This fix applies to all channels (Discord, Telegram, WhatsApp, etc.) that use group/channel routing with multiple accounts.

Fixes openclaw#27731

When using multiple Discord bot accounts with agent bindings, all
messages were routed to the main agent regardless of binding
configuration. The root cause was that session keys for group/channel
messages did not include the accountId, causing messages from different
accounts to the same channel to share the same session key.

This led to routing conflicts where whichever agent was last matched
would capture all subsequent messages to that channel, regardless of
which Discord account received the message.

Changes:
- Modified buildAgentPeerSessionKey() to include accountId in
  group/channel session key format
- New format: agent:{agentId}:{channel}:{accountId}:{peerKind}:{peerId}
- Added tests to verify accountId inclusion and multi-account isolation

Impact:
- Each Discord account now gets its own session namespace per channel
- Agent bindings with accountId matching now work correctly
- Messages route to the correct agent based on both channel and account
@greptile-apps

greptile-apps Bot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds accountId to group/channel session keys to fix multi-account routing conflicts. Previously, messages from different Discord bot accounts (e.g., designer, erp) to the same channel would share identical session keys, causing all messages to route to whichever agent was matched last. With this change, each account now maintains its own session namespace per channel.

Key changes:

  • Modified buildAgentPeerSessionKey() to include accountId in the session key format for group/channel messages
  • New format: agent:{agentId}:{channel}:{accountId}:{peerKind}:{peerId}
  • Added comprehensive test coverage verifying accountId inclusion and multi-account isolation
  • The function already existed with accountId parameter, it just wasn't being used in the key generation

Compatibility:

  • This is a breaking change for existing group/channel sessions (old sessions won't match new message keys), but this is necessary to fix the routing bug
  • Direct message sessions are unaffected unless using dmScope: "per-account-channel-peer"
  • Session key parsing code (e.g., in cron-tool.ts) remains compatible as it dynamically searches for markers rather than assuming fixed positions

Confidence Score: 4/5

  • This PR is safe to merge with low risk - it fixes a critical routing bug with minimal code changes
  • The change is focused, well-tested, and addresses a real bug. Score is 4 (not 5) because this is a breaking change that will invalidate existing group/channel sessions, though this is necessary to fix the routing issue. The implementation is clean with only 2 lines changed in the core logic, and comprehensive test coverage validates the fix works correctly.
  • No files require special attention

Last reviewed commit: fa21e62

@greptile-apps greptile-apps Bot left a comment

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.

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@greptile-apps

greptile-apps Bot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor
Additional Comments (1)

src/agents/tools/cron-tool.ts
Update comment to reflect new session key format with accountId

  // - <channel>:<accountId>:group:<peerId>
  // - <channel>:<accountId>:channel:<peerId>
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/tools/cron-tool.ts
Line: 176-177

Comment:
Update comment to reflect new session key format with `accountId`

```suggestion
  // - <channel>:<accountId>:group:<peerId>
  // - <channel>:<accountId>:channel:<peerId>
```

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
Contributor

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: fa21e6296c

ℹ️ 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".

const peerId = ((params.peerId ?? "").trim() || "unknown").toLowerCase();
return `agent:${normalizeAgentId(params.agentId)}:${channel}:${peerKind}:${peerId}`;
const accountId = normalizeAccountId(params.accountId);
return `agent:${normalizeAgentId(params.agentId)}:${channel}:${accountId}:${peerKind}:${peerId}`;

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.

P1 Badge Preserve Discord channel key compatibility

Including accountId in every group/channel key changes the shape to agent:<agent>:<channel>:<accountId>:<kind>:<id>, but Discord exec approvals still extract channel IDs with /discord:(?:channel|group):(\d+)/ (src/discord/monitor/exec-approvals.ts), which no longer matches these new keys. In handleApprovalRequested, channel-targeted approvals then cannot resolve channelId and silently fall back to DM-only behavior or miss channel delivery, so channel approval workflows break for routed sessions created with the new format.

Useful? React with 👍 / 👎.

const peerId = ((params.peerId ?? "").trim() || "unknown").toLowerCase();
return `agent:${normalizeAgentId(params.agentId)}:${channel}:${peerKind}:${peerId}`;
const accountId = normalizeAccountId(params.accountId);
return `agent:${normalizeAgentId(params.agentId)}:${channel}:${accountId}:${peerKind}:${peerId}`;

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.

P1 Badge Keep send-policy channel matching for new session keys

This new key layout also breaks channel derivation in resolveSendPolicy: deriveChannelFromKey expects stripped keys like <channel>:<group|channel>:... (src/sessions/send-policy.ts), so agent:...:discord:<accountId>:channel:... now yields no channel. Gateway paths such as src/gateway/server-methods/agent.ts call resolveSendPolicy with channel: entry?.channel, and on first-use sessions entry can be absent, causing channel-scoped deny rules to be skipped for these group/channel keys.

Useful? React with 👍 / 👎.

@bmendonca3

Copy link
Copy Markdown
Contributor

The account-scoped session-key change makes sense, but this PR is currently red across node/bun/windows tests with many session-key expectation mismatches.

Example from node test log:

  • src/telegram/bot.create-telegram-bot.test.ts:1504 expected key segment telegram:group:-1001234567890:topic:99, but got the new shape with account slot (agent:main:telegram:default:group:...).

I also see related failures in src/telegram/bot.test.ts, src/routing/resolve-route.test.ts, src/infra/outbound/outbound.test.ts, and thread-session tests. Updating downstream key expectations/consumers to include accountId should make this mergeable.

scz2011 added a commit to scz2011/openclaw that referenced this pull request Feb 26, 2026
…ord exec approvals

PR openclaw#27750 changed session key format for group/channel keys to include accountId:
- Old: agent:<agentId>:<channel>:<peerKind>:<peerId>
- New: agent:<agentId>:<channel>:<accountId>:<peerKind>:<peerId>

This broke two critical features:

1. deriveChannelFromKey in send-policy.ts expected parts[1] to be peerKind,
   but it's now accountId. This caused channel-scoped deny rules to be
   skipped for group/channel keys, especially on first-use sessions.

2. extractDiscordChannelId regex /discord:(?:channel|group):(\d+)/ no longer
   matched the new format discord:<accountId>:channel:<id>. This broke
   channel-targeted approval workflows.

Fixes:
- Updated deriveChannelFromKey to check both parts[1] and parts[2] for
  peerKind, supporting both old and new formats
- Updated extractDiscordChannelId regex to optionally match accountId segment
- Added comprehensive tests for both old and new session key formats
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Mar 4, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the stale Marked as stale due to inactivity label Mar 26, 2026
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Apr 1, 2026
@openclaw-barnacle

Copy link
Copy Markdown

Closing due to inactivity.
If you believe this PR should be revived, post in #pr-thunderdome-dangerzone on Discord to talk to a maintainer.
That channel is the escape hatch for high-quality PRs that get auto-closed.

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

Labels

size: S stale Marked as stale due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Discord multi-account binding routes all messages to main agent (channels unresolved)

2 participants