#**CRITICAL: Cross-Agent Session Leakage with Separate Telegram Bots**

1 messages ¡ Page 1 of 1 (latest)

gloomy bobcat
#

I have configured separate Telegram bots for different agents (ha and chad), expecting isolation.

Issue:

  • I sent a message to the chad bot.
  • The ha agent intercepted and replied to it.
  • It seems OpenClaw is routing messages based on my Telegram user_id to the last active session (the ha agent), ignoring which Bot Token received the update.

Impact:

  • Unable to communicate with specific agents reliably.
  • Agents "steal" messages intended for others solely because they were active recently.
  • Strict channel-to-agent binding is failing or being overridden by session stickiness.

Config Context:

  • Multiple telegram channels configured with different tokens.
  • Agents mapped to specific channels (presumably).
  • User ID is the same across bots.

How can I enforce strict routing where messages to Bot A always go to Agent A, regardless of Agent B's activity?

spice narwhalBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> ✅ Mark Solution

viral salmonBOT
# gloomy bobcat I have configured **separate Telegram bots** for different agents (`ha` and `cha...

OpenClaw won’t treat “two Telegram bots” as two separate channels by name. Telegram is one channel (telegram) with multiple accounts, and strict routing is enforced via bindings that match accountId (i.e., which bot token received the update).

What to do (strict Bot → Agent routing)

  1. Configure each bot as a Telegram account:
{
  channels: {
    telegram: {
      enabled: true,
      accounts: {
        ha:   { botToken: "123:AAA..." },
        chad: { botToken: "456:BBB..." },
      },
    },
#
  },
}
  1. Add bindings that route by accountId:
{
  bindings: [
    { agentId: "ha",   match: { channel: "telegram", accountId: "ha" } },
    { agentId: "chad", match: { channel: "telegram", accountId: "chad" } },
  ],
}
  1. Restart the Gateway after config changes.
#

This makes routing deterministic: messages that arrive via bot ha go to agent ha, and bot chad → agent chad, regardless of the sender user_id or “last active session”.

The common misconfig that causes your symptom

If you only have a broad binding like:

{ agentId: "ha", match: { channel: "telegram" } }

…then all Telegram traffic (including other bot tokens) can end up on ha. Make sure you bind per accountId as above (and put the specific bindings first).

Verify

Run:

openclaw agents list --bindings
#

If it still routes wrong after accountId bindings, paste:

  • openclaw --version
  • openclaw agents list --bindings
  • a short openclaw logs --follow snippet showing an inbound Telegram update (it should indicate which accountId it was)

Docs:

If this fixes it, please mark the solution (right-click the solution message → Apps → ✅ Mark Solution) so Answer Overflow indexes it.

gloomy bobcat
#

✅ Followed your instructions - accountId bindings work perfectly for inbound, but found a bug/feature with agent-to-agent delivery routing via sessions_send.

Setup

Version: 2026.2.6-3

Bindings (working correctly for inbound):

• main → telegram accountId=default
• ha → telegram accountId=ha-bot
• unraid → telegram accountId=unraid-bot
• marta → telegram accountId=marta-bot
• test01 → telegram accountId=test01-bot

Inbound routing is perfect - messages to each bot reach the correct agent ✅

The Bug

Agent-to-agent via sessions_send creates a NEW session with channel=webchat instead of using the existing Telegram session.

Timeline

  1. User writes to @unraidmakbot → Creates session agent:unraid:main with channel=telegram, accountId=unraid-bot ✅
  2. Agent HA uses sessions_send(sessionKey="agent:unraid:main", message="...")
  3. Gateway creates a SECOND session agent:unraid:main with:
    • channel=webchat
    • displayName=webchat:g-agent-unraid-main
    • deliveryContext.accountId=unraid-bot (correct, but overridden)
  4. Unraid agent replies → Uses wrong bot (default/Chad bot) instead of unraid-bot ❌

Evidence

Sessions list after sessions_send:

{
  "key": "agent:unraid:main",
  "channel": "webchat",  // ← Should be "telegram"!
  "displayName": "webchat:g-agent-unraid-main",
  "deliveryContext": {
    "channel": "telegram",
    "accountId": "unraid-bot"  // ← Correct but ineffective
  }
}

Gateway log:

2026-02-10T03:40:41.355Z [agent:nested] 
  session=agent:unraid:main 
  channel=webchat  // ← Not telegram!
#

Expected vs Actual

Expected: sessions_send should:

• Use the EXISTING agent:unraid:main Telegram session
• Preserve channel=telegram and accountId=unraid-bot
• Agent replies go through the correct bot
Actual: sessions_send:

• Creates a NEW session with channel=webchat
• Agent replies fall back to default bot
• The original Telegram session exists but is ignored
Questions

  1. Is this a bug or intended behavior?
  2. Should sessions_send reuse existing sessions or always create new "internal" ones?
  3. Is there a way to force sessions_send to preserve Telegram delivery context?
    Workaround
#

Update: Second Test Confirms the Bug

Just ran another test with agent test01 - same issue, plus it revealed why messages don't reach the user:

What Happened

  1. HA sends sessions_send(sessionKey="agent:test01:main", message="...") ✅
  2. Test01 receives the message (agent-to-agent works) ✅
  3. Gateway creates session with channel=webchat again ❌
  4. Test01 tries to send Telegram message using message tool
  5. message tool fails: "Action send requires a target" ❌

Gateway Logs

04:57:48 - [tools] message failed: Action send requires a target.
04:57:51 - [agent:nested] session=agent:test01:main
channel=webchat // ← Not telegram!
"Potrzebuję chat ID dla Makosia..."

Why It Fails

When sessions_send creates a session with channel=webchat:

  • The agent has NO deliveryContext.to (no chat ID)
  • Using message tool without explicit target fails
  • Even when deliveryContext.accountId=test01-bot is set, it's not used

The agent is orphaned - it knows WHICH bot to use (accountId=test01-bot) but doesn't know WHO to send to.

Reproducible 100%

Every sessions_send to a non-main agent creates channel=webchat session, breaking Telegram delivery.

This is definitely a bug in how sessions_send initializes target agent sessions - it should preserve or inherit the Telegram delivery context.

viral salmonBOT
# gloomy bobcat ✅ Followed your instructions - accountId bindings work perfectly for inbound, bu...

Thanks — excellent reproduction and logs. I dug into the code and can explain exactly what’s happening and how to fix it.

Short diagnosis (what’s happening)

  • sessions_send intentionally runs the target agent using the internal channel ("webchat") so the agent logic runs in a nested/internal lane.
  • The Gateway agent handler (server-methods/agent.ts) writes a session entry when it starts a run. When that handler decides what session.channel value to store it uses:
    channel = entry?.channel ?? request.channel
  • For DM "main" sessions the original session often has no top-level entry.channel set (it relies on deliveryContext / lastChannel instead). So when sessions_send calls the agent with request.channel = "webchat", that line ends up setting channel = "webchat" (because entry.channel was undefined).
  • Later the outbound delivery logic treats a run where request.channel is INTERNAL_MESSAGE_CHANNEL specially (it ends up choosing INTERNAL_MESSAGE_CHANNEL as resolvedChannel and does not carry through the session's deliveryContext.accountId), so the announce/send falls back to the default account and the wrong bot is used.
  • In short: sessions_send’s internal webchat-run + agent handler fallback ordering cause the session.channel to be overwritten by "webchat" when the session only had deliveryContext (telegram/unraid-bot). That removes the implicit account routing and results in the announce using the wrong bot.

Minimal fix
Make the agent handler prefer the session’s delivery/lastChannel before falling back to request.channel. Concretely, change the channel line in src/gateway/server-methods/agent.ts from:

channel: entry?.channel ?? request.channel?.trim(),

to:

#

channel: entry?.channel ?? deliveryFields.lastChannel ?? request.channel?.trim(),

This keeps the existing recorded delivery channel (e.g. telegram) when the session already has a deliveryContext/lastChannel, and prevents the sessions_send internal call from overwriting it with webchat.

Why this fixes it

  • deliveryFields.lastChannel is built from deliveryContext / lastChannel saved previously (that’s where the original telegram/unraid-bot is stored).
  • By preferring it we preserve the original route/accountId, so resolveAgentDeliveryPlan will pick telegram + the correct accountId and the announce/send goes out through the intended bot.

Workarounds (no code change required)

  1. Use an explicit send that specifies accountId

    • From CLI or an agent/tool that can call the Gateway send endpoint, send directly with channel + accountId + to, e.g. (example CLI):
      openclaw message send --channel telegram --target <chat_id> --message "…" --accountId unraid-bot
    • That avoids sessions_send entirely and guarantees you pick the right bot.
  2. If you need agent-to-agent automation and can change skills, have the sending agent fetch the target session’s deliveryContext (via sessions_list / sessions_history) and then call the Gateway send API explicitly with accountId as above.

Suggested patch (diff)

#
  • File: src/gateway/server-methods/agent.ts

  • Replace (approx):

    const deliveryFields = normalizeSessionDeliveryFields(entry);
    ...
    channel: entry?.channel ?? request.channel?.trim(),

  • With:

    const deliveryFields = normalizeSessionDeliveryFields(entry);
    ...
    channel: entry?.channel ?? deliveryFields.lastChannel ?? request.channel?.trim(),

Tests to add

  • A sessions_send e2e that:
    1. creates a session via Telegram inbound (session.deliveryContext set)
    2. calls sessions_send from another agent
#
  1. asserts session.channel remains "telegram" (not "webchat") and that announce delivery uses the telegram accountId

Next steps I can take for you

  • If you want, I can prepare a small patch/PR for this repo (apply the one-line change + add an e2e test) and run the unit/e2e tests here. I will not change the repository without your OK.
  • If you prefer to patch it immediately on your host, apply the one-line edit above, run pnpm build (if using the git checkout) and restart the Gateway:
    openclaw gateway restart

If you want me to:

  • create the patch and run tests here → say “apply patch”
  • or just produce a ready-to-copy patch file/commit message → say “give patch”

If you want I can also craft a minimal GitHub issue description (title, reproduction steps, stack trace/lines, and the suggested fix) for you to file.

viral salmonBOT
# gloomy bobcat ## Update: Second Test Confirms the Bug Just ran another test with agent test01...

This usually isn’t “last active session” routing — it’s that your bindings don’t match non-default Telegram accounts, so OpenClaw falls back to the default agent.

Key detail from the router: a binding that omits accountId only matches the default account. For any bot under channels.telegram.accounts.<id>, you must bind using that accountId (or accountId: "*").

1) Make routing strict: bind by Telegram accountId

Configure multiple bots like this:

{
  channels: {
    telegram: {
      accounts: {
        ha:   { botToken: "123:AAA" },
        chad: { botToken: "456:BBB" },
      },
    },
#
  },

  agents: {
    list: [
      { id: "ha" },
      { id: "chad" },
    ],
  },

  bindings: [
    { agentId: "ha",   match: { channel: "telegram", accountId: "ha" } },
    { agentId: "chad", match: { channel: "telegram", accountId: "chad" } },
  ],
}
#

If you want a catch-all “any Telegram account goes to X”, do:

{ agentId: "ha", match: { channel: "telegram", accountId: "*" } }

Docs:

2) Why sessions_send creates channel=webchat (and why message then fails)

sessions_send(sessionKey="agent:test01:main", ...) will create a session if it doesn’t exist. That “synthetic” session has no Telegram delivery context, so it becomes channel=webchat, and then:

  • the agent has no default deliveryContext.to (chat id)
  • message tool errors with “Action send requires a target”

Once strict routing is fixed and you DM the test01 bot at least once, the agent:test01:main session should already exist as a Telegram session, and sessions_send will reuse it (so replies can go back to Telegram without specifying target).

#

If you need to send proactively anyway, you must pass the target explicitly:

message({
  action: "send",
  channel: "telegram",
  accountId: "test01-bot",
  target: "<chat_id>",
  message: "..."
})

3) Quick verification commands (to confirm it’s actually bound)

Have a look at:

  • openclaw agents list --bindings
  • openclaw channels status
#

If you paste just your channels.telegram.accounts + bindings blocks (redact tokens), I can sanity-check why the accountId match isn’t taking effect.

gloomy bobcat
#

Apply patch

viral salmonBOT
# gloomy bobcat Apply patch

Patch is config-only: your bindings need to match Telegram accountId (bot) — otherwise non-default accounts fall back to the default agent and it looks like “session stickiness”.

Apply this to ~/.openclaw/openclaw.json (JSON5), then restart the gateway:

{
  channels: {
    telegram: {
      accounts: {
        ha:   { botToken: "123:AAA" },
        chad: { botToken: "456:BBB" },
      },
    },
  },

  agents: {
#
    list: [
      { id: "ha" },
      { id: "chad" },
    ],
  },

  // IMPORTANT: bind by accountId (must exactly match the accounts keys)
  bindings: [
    { agentId: "ha",   match: { channel: "telegram", accountId: "ha" } },
    { agentId: "chad", match: { channel: "telegram", accountId: "chad" } },
  ],
}

Then verify:

#
  • openclaw agents list --bindings (should show the two Telegram account bindings)
  • DM each bot once; each should now always route to its bound agent.

About your sessions_send “webchat + message requires a target” finding

That part is expected: sessions_send injects an internal message (no Telegram chat context), so the receiving agent won’t have deliveryContext.to. If you want the receiving agent to send to Telegram anyway, it must be given a target explicitly:

  • it needs the Telegram chat id and you must call message with target (and optionally accountId).

Otherwise, the normal pattern is: sessions_send → reply gets announced back to the requester agent, and the requester (which does have the Telegram context) sends the Telegram message.

If after adding accountId bindings you still see Bot A routed to Agent B, paste your channels.telegram.accounts + bindings blocks (redact tokens) and I’ll point out the exact mismatch.

gloomy bobcat
#

Sorry, here's the config you asked for:

channels.telegram.accounts:

{
  "default": {
    "dmPolicy": "pairing",
    "botToken": "REDACTED",
    "groupPolicy": "allowlist",
    "streamMode": "partial"
  },
  "ha-bot": {
    "dmPolicy": "pairing",
    "botToken": "REDACTED",
    "groupPolicy": "allowlist",
    "streamMode": "partial"
  },
  "unraid-bot": {
    "dmPolicy": "pairing",
    "botToken": "REDACTED",
    "groupPolicy": "allowlist",
    "streamMode": "partial"
  },
  "marta-bot": {
    "dmPolicy": "pairing",
    "botToken": "REDACTED",
    "groupPolicy": "allowlist",
    "streamMode": "partial"
  },
  "test01-bot": {
    "dmPolicy": "pairing",
    "botToken": "REDACTED",
    "groupPolicy": "allowlist",
    "streamMode": "partial"
  }
}

bindings:

[
  {
    "agentId": "main",
    "match": {
      "channel": "telegram",
      "accountId": "default"
    }
  },
  {
    "agentId": "ha",
    "match": {
      "channel": "telegram",
      "accountId": "ha-bot"
    }
  },
  {
    "agentId": "unraid",
    "match": {
      "channel": "telegram",
      "accountId": "unraid-bot"
    }
  },
  {
    "agentId": "marta",
    "match": {
      "channel": "telegram",
      "accountId": "marta-bot"
    }
  },
  {
    "agentId": "test01",
    "match": {
      "channel": "telegram",
      "accountId": "test01-bot"
    }
  }
]

Verification:

$ openclaw agents list --bindings
Agents:
- main (default) → telegram accountId=default
- ha → telegram accountId=ha-bot
- unraid → telegram accountId=unraid-bot
- marta → telegram accountId=marta-bot
- test01 → telegram accountId=test01-bot

accountId names match exactly between accounts and bindings ✅

Inbound routing works perfectly - each bot routes to correct agent ✅

Problem is ONLY sessions_send outbound - creates channel=webchat sessions ❌



Yes, prepare GitHub issue. Does the same problem occur using Whatsapp or discord?
viral salmonBOT
# gloomy bobcat Sorry, here's the config you asked for: ### channels.telegram.accounts: ```json...

Your config + bindings are correct, and your diagnosis (“only sessions_send outbound breaks”) matches what the code is doing.

What’s actually happening

sessions_send runs the target agent turn with channel = INTERNAL_MESSAGE_CHANNEL, and INTERNAL_MESSAGE_CHANNEL is webchat.

During that nested run, the gateway updates the target session’s “last route” metadata. Because of how delivery context is merged today, the nested run can end up overwriting lastChannel to webchat while keeping the old to (Telegram chat id). That produces a broken deliveryContext like:

  • channel = webchat (not deliverable)
  • to = <telegram chat id> (stale / mismatched)

Then downstream:

  • the announce step can’t deliver (because it sees webchat)
  • the message tool can’t infer target (toolContext is “webchat”, no currentChannelId), so you get Action send requires a target.

This isn’t Telegram-specific; it’s a generic DM delivery-context clobbering issue triggered by internal/nested runs.

Does it affect WhatsApp / Discord too?

#

Yes, same class of problem can hit any DM-based session (WhatsApp/Telegram/Discord DM/Signal/etc), because:

  • DMs often map to a “main” session key (session.dmScope="main" default),
  • delivery relies on stored deliveryContext/lastChannel/lastTo,
  • sessions_send runs as internal webchat unless it explicitly carries the session’s real delivery context.

(For Discord/Slack channels/groups, it may be less visible because those session keys include :group:/:channel: and the announce target can often be derived without relying solely on deliveryContext.)

Workarounds (today)

  1. Don’t have the target agent “reply to the user” implicitly in a sessions_send-spawned run. Instead:
  • sessions_send → get reply back in the requester agent → requester sends to Telegram normally.
  1. If you do want the target agent to send during that nested run, you must pass explicit routing in the tool call:
  • message(action:"send", channel:"telegram", accountId:"test01-bot", target:"<chat_id>", message:"...")

GitHub issue draft (copy/paste)

#

You’ll need to file it yourself here: https://github.com/openclaw/openclaw/issues

Title: sessions_send clobbers DM deliveryContext by setting lastChannel=webchat, breaking announce delivery + message tool inference

### Summary
When using `sessions_send` to message another agent's DM session (e.g. `agent:test01:main`), the nested/internal run uses `channel=webchat` (INTERNAL_MESSAGE_CHANNEL). This causes the target session’s delivery context / last route metadata to flip to `webchat`, which then breaks outbound delivery (announce step can’t deliver) and prevents `message` tool from inferring `target`.

### Environment
- OpenClaw version: (paste `openclaw --version`)
- OS:
- Node:
- Channels involved: Telegram multi-account
- session.dmScope: (default `main` unless changed)

### Config (redacted)
#
Telegram accounts keys and bindings match exactly:
- channels.telegram.accounts: default, ha-bot, unraid-bot, marta-bot, test01-bot
- bindings route each telegram accountId to matching agentId

Inbound routing works correctly (each bot -> correct agent).

### Steps to reproduce
1. Configure multiple Telegram bots via `channels.telegram.accounts` and bind each `accountId` to an agent via `bindings`.
2. DM `test01-bot` so the session has a Telegram delivery context.
3. From another agent (e.g. ha), call:
   - `sessions_send(sessionKey="agent:test01:main", message="hello")`
4. Observe gateway logs show nested run for target session with `channel=webchat`.
5. Observe either:
   - announce step fails to deliver to Telegram, and/or
   - target agent calling `message(action:"send")` without explicit target fails with `Action send requires a target`.
#

### Expected behavior
`sessions_send` should not corrupt the target DM session’s delivery routing metadata.
Nested/internal runs should either:
- preserve existing deliverable deliveryContext (telegram chat id + accountId), OR
- run with toolContext derived from the session’s deliveryContext so `message` can infer target, OR
- avoid updating lastChannel/lastTo when channel=webchat and no explicit `to` is provided.

### Actual behavior
Target session ends up with lastChannel/deliveryContext resolving to `webchat`, causing outbound delivery to fail or requiring explicit targets.

### Evidence to include
- `openclaw sessions --json` for the target session BEFORE and AFTER `sessions_send` (show lastChannel/lastTo/deliveryContext changes)
- Relevant gateway log excerpt around the nested run + announce attempt
#

If you paste your openclaw --version and a before/after openclaw sessions --json | jq '...test01...' snippet, I can help you tighten the “Evidence” section so maintainers can reproduce fast.

gloomy bobcat
#

:

🐛 Found a SECOND bug related to Gateway Restart & Outbound Routing.

The scenario:

  1. Agent "ha" (bound to ha-bot) triggers gateway.restart.
  2. Gateway restarts successfully.
  3. Agent "ha" automatically sends a message to the user upon restart (re-entering the session).
    The Problem:
    Even though agent "ha" is active, the post-restart message is delivered via the DEFAULT bot (Main/Chad) instead of ha-bot.

The interesting part:
When the user replies in the Main/Chad bot window, the message correctly routes to Main/Chad agent. So inbound routing is fine, but the automated outbound response right after a restart loses its accountId context and falls back to the default account.

It seems gateway.restart response/re-entry logic ignores the session's deliveryContext.accountId.

viral salmonBOT
# gloomy bobcat : 🐛 Found a SECOND bug related to Gateway Restart & Outbound Routing. The sce...

Yep, that’s a real second bug — and it’s very likely the wake-after-restart path is dropping accountId when it delivers the “GatewayRestart: …” message back into chat.

Why it happens (root cause)

There’s a restart-sentinel wake routine (scheduleRestartSentinelWake in src/gateway/server-restart-sentinel.ts) that:

  1. Reads restart-sentinel.json
  2. Merges delivery context from:
    • the sentinel payload (payload.deliveryContext) which includes accountId
    • the session store entry delivery context
    • a parsed target from the sessionKey
  3. Resolves (channel,to,accountId) correctly…

…but then calls agentCommand(...) without passing accountId.

So the delivery falls back to the channel’s default account (Telegram default bot / “Main/Chad”), even though the restart sentinel knows it should use ha-bot.

You can literally see it in the current dist code: the wake calls agentCommand({ message, sessionKey, to, channel, deliver: true, ... }) but no accountId:.

#

Does this affect WhatsApp / Discord too?

If you run multi-account on those channels, yes — same symptom: any “wake/ping last active session after restart” can come out of the default account because the wake delivery doesn’t carry accountId.

If you only have one account per channel, you won’t notice.

Workaround (until fixed)

  • Trigger restarts only from the default bot/account (ugly, but consistent), or
  • After restart, send a message in the correct bot chat to “re-anchor” delivery, and ignore the misrouted restart ping.

GitHub issue draft (separate from the sessions_send/webchat issue)

File here: https://github.com/openclaw/openclaw/issues

Title: gateway restart sentinel wake ignores deliveryContext.accountId (multi-account Telegram sends restart ping from default bot)
#

### Summary
When an agent triggers `gateway.restart` from a non-default Telegram account (e.g. `ha-bot`), the post-restart wake/ping message is delivered via the Telegram **default** account instead of the correct bot accountId.

Inbound routing remains correct; only the automated outbound wake after restart is wrong.

### Environment
- OpenClaw version: (paste `openclaw --version`)
- OS/Node:
- Channel: Telegram multi-account (`channels.telegram.accounts`)

### Config (redacted)
- Multiple Telegram accounts under `channels.telegram.accounts` (default + ha-bot + ...)
- Bindings route each `accountId` to the matching agentId
- Inbound routing verified OK
#

### Steps to reproduce
1. Configure Telegram multi-account and bindings.
2. DM `ha-bot` so the session has a deliveryContext (channel=telegram, to=<chatId>, accountId=ha-bot).
3. From the `ha` agent, trigger `gateway.restart`.
4. After the gateway restarts, observe the restart wake message is sent via Telegram `default` bot, not `ha-bot`.

### Expected
Wake/ping message after restart uses the same `{ channel, to, accountId }` from the session’s deliveryContext (or sentinel payload deliveryContext), so it replies via the correct Telegram bot account.

### Actual
Wake/ping message is delivered via the channel default account (telegram accountId=default), ignoring the session’s deliveryContext.accountId.

### Suspected root cause
In `scheduleRestartSentinelWake` (src/gateway/server-restart-sentinel.ts), `origin.accountId` is computed but not passed into the `agentCommand(...)` call, causing default-account fallback.
#

### Suggested fix
Pass `accountId: origin?.accountId` to `agentCommand` (and/or into its runContext) when delivering the restart sentinel wake.

If you paste your openclaw --version + a log excerpt showing the restart wake going out via the wrong Telegram account, that’ll make the issue airtight.

gloomy bobcat
#

Here is the data for the second bug (Restart Outbound Routing):

Environment

Version: 2026.2.6-3

Evidence (Logs)

The logs show the restart sentinel triggering a wake, but the subsequent automated reply is missing the accountId context and defaults to the 'default' provider.

Timeline from journalctl:
06:41:16 - [gateway] restart requested (reason=Test routingu sesji po restarcie (Chad vs HA))
06:41:18 - [gateway] received SIGUSR1; restarting
06:41:23 - [telegram] [ha-bot] starting provider (@HAMakulkobot)
06:41:25 - [telegram] [default] starting provider (@Makulko_bot)
06:41:33 - [agent:nested] "TEST RESTARTU #2 🔄"

The message "TEST RESTARTU #2 🔄" was sent by agent "ha" (which is bound to ha-bot), but it was delivered via @Makulko_bot (default account).

viral salmonBOT
# gloomy bobcat Here is the data for the second bug (Restart Outbound Routing): ## Environment ...

Yep — with v2026.2.6-3 and your log + “it arrived from @Makulko_bot (default)” observation, this is enough to file the second issue.

What you’re seeing matches a concrete bug in the restart-sentinel wake path: it resolves {channel,to,accountId} but the final delivery call doesn’t carry accountId, so Telegram falls back to the default account.

Ready-to-paste GitHub issue (Restart outbound routing drops accountId)

File here: https://github.com/openclaw/openclaw/issues

Title: Restart sentinel wake drops deliveryContext.accountId (multi-account Telegram restart ping sent via default bot)

### Version
OpenClaw 2026.2.6-3

### Summary
When `gateway.restart` is triggered from an agent bound to a non-default Telegram bot account (e.g. `ha-bot`), the post-restart wake/ping message is delivered via the Telegram **default** account instead of the correct `accountId`.
#
Inbound routing remains correct; only the automated outbound message right after restart uses the wrong bot.

### Setup
- Telegram multi-account configured under `channels.telegram.accounts` (default + ha-bot + others)
- `bindings` route each Telegram `accountId` to the matching agentId
- Inbound routing verified correct (each bot -> correct agent)

### Steps to reproduce
1. DM the `ha-bot` Telegram bot so the DM session has a delivery context.
2. From agent `ha` (bound to `ha-bot`), trigger `gateway.restart`.
3. After the gateway restarts, observe the automated post-restart message is delivered via the **default Telegram bot**, not `ha-bot`.

### Expected
Post-restart wake message should be delivered via the same Telegram accountId as the session’s deliveryContext (`ha-bot`).
#
### Actual
Post-restart wake message is delivered via Telegram `default` account.

### Evidence (journalctl)
06:41:16 - [gateway] restart requested (reason=Test routingu sesji po restarcie (Chad vs HA))
06:41:18 - [gateway] received SIGUSR1; restarting
06:41:23 - [telegram] [ha-bot] starting provider (@HAMakulkobot)
06:41:25 - [telegram] [default] starting provider (@Makulko_bot)
06:41:33 - [agent:nested] "TEST RESTARTU #2 🔄"

Observed on Telegram: the message "TEST RESTARTU #2 🔄" was delivered via @Makulko_bot (default account), even though it originated from agent "ha" bound to ha-bot.

### Notes / suspected root cause
Restart-sentinel wake path resolves origin.deliveryContext.accountId but does not pass `accountId` into the final delivery call (falls back to channel default account).
#

“Does it happen on WhatsApp / Discord?”

Same class of bug can show up on any multi-account channel (WhatsApp accounts, Discord accounts, etc.). If the channel is single-account, you won’t notice.

If you also want, I can produce a separate issue write-up for the sessions_send → webchat clobbering deliveryContext bug (it should be a different ticket).

gloomy bobcat
#

openclaw --version: 2026.2.6-3

Both the code patch and "Option 2" (manual accountId in SOUL.md) were necessary to make this work exactly as intended. I chose Option 2 because I want the target agent to deliver messages to the correct bot window when requested by another agent via sessions_send.

Evidence (test01 session)

Before (Broken Outbound):
{
"key": "agent:test01:main",
"channel": "webchat",
"deliveryContext": {
"channel": "telegram",
"to": "telegram:5267817769",
"accountId": "test01-bot"
},
"lastChannel": "telegram",
"lastAccountId": "test01-bot"
}
-> Outcome: Outbound messages fall back to default Telegram account because channel is "webchat".

After (Working with Patch + Option 2):
Applying the suggested agent.ts patch ensures the session preserves its delivery context. Forcing the accountId in the agent's instructions (Option 2) then ensures proactive tool calls also use the correct bot.

Now, even if sessions_send is used, the message is delivered via @test01makbot as expected.

Please once again GitHub issue about first bug. I didn't send it yet

viral salmonBOT
# gloomy bobcat openclaw --version: 2026.2.6-3 Both the code patch and "Option 2" (manual accou...

Here’s a paste-ready GitHub issue for the first bug (the sessions_send / webchat / missing delivery context problem). This is separate from the restart-sentinel accountId bug.

File it here: https://github.com/openclaw/openclaw/issues

Title: sessions_send runs target agent under webchat/internal channel, breaking outbound routing inference (no target) and can clobber session channel

### Version
openclaw --version: 2026.2.6-3

### Summary
When Agent A calls `sessions_send` into Agent B’s main DM session (e.g. `agent:test01:main`), the gateway executes the target run as an internal/nested run with `channel=webchat` (INTERNAL_MESSAGE_CHANNEL). This causes:

1) The target run’s tool context to be `webchat`, so the `message` tool cannot infer a Telegram target (`Action send requires a target`), and/or outbound tool calls fall back to defaults unless the agent explicitly supplies `target` and `accountId`.

2) The target session entry may end up with `"channel": "webchat"` even though it has a valid Telegram `deliveryContext` (channel/to/accountId). This makes the session look like a webchat session and breaks “implicit” outbound behavior.
#

Inbound routing is correct; the issue is **only** `sessions_send`-initiated runs and proactive outbound from that run.

### Setup
- Telegram multi-account configured: `channels.telegram.accounts` has `default`, `test01-bot`, etc.
- `bindings` correctly route each Telegram `accountId` to the matching agentId
- Inbound works: messaging each bot routes to the correct agent

### Steps to reproduce (100%)
1. DM the `test01-bot` on Telegram so the session has delivery context.
2. From another agent (e.g. `ha`), call:
   - `sessions_send(sessionKey="agent:test01:main", message="ping from ha")`
3. Observe gateway logs for the nested run show `channel=webchat` (or `agent:nested` output without Telegram target context).
4. In the `test01` agent, try to proactively reply to the user using `message` tool without explicit `target` (or rely on implicit delivery context):
   - `message(action="send", channel="telegram", message="hi")`
#
5. Result: `message` tool fails with:
   - `Action send requires a target.`
   (or message sends via the wrong/default Telegram account unless `accountId` is explicitly provided)

### Evidence
Example session entry (broken) after `sessions_send`:

```json
{
  "key": "agent:test01:main",
  "channel": "webchat",
  "deliveryContext": {
    "channel": "telegram",
    "to": "telegram:5267817769",
    "accountId": "test01-bot"
  },
#

"lastChannel": "telegram",
"lastAccountId": "test01-bot"
}


This shows the session still *knows* Telegram `{to, accountId}`, but the run context is `webchat`, so implicit outbound/tool inference breaks.

### Expected behavior
`sessions_send` should preserve the target session’s existing delivery context for the duration of the run (for toolContext + implicit outbound inference), while still not delivering automatically.

Specifically, a target agent should be able to call `message(action="send")` without needing to restate `target`/`accountId` if the session already has a valid Telegram `deliveryContext`.

### Actual behavior
`sessions_send` runs the target agent turn under `channel=webchat` internal context, which:
- prevents `message` tool from inferring `target`
- can cause proactive outbound to default/fail unless explicit `target` and `accountId` are forced in the agent instructions
#

### Suspected root cause
The `sessions_send` tool invokes the gateway `agent` method with `channel=INTERNAL_MESSAGE_CHANNEL` (webchat) for the nested run, which forces the runContext/toolContext to “webchat” instead of using the session’s last/delivery channel.

### Suggested fix
Change `sessions_send` nested run to use `channel: "last"` (or omit channel) while keeping `deliver: false`, so the delivery plan resolves to the session’s last deliverable channel + target + accountId for context/tool inference, without actually sending messages automatically.

Alternatively: ensure internal/nested runs do not overwrite session `channel` or delivery fields with `webchat` when a deliverable `deliveryContext` already exists.

Note on your “Option 2” workaround

It’s totally valid as a workaround (forcing accountId/target explicitly), but the issue above makes the case that OpenClaw should not require this when the session already has a correct Telegram delivery context.

If you want, paste one agent:nested log line showing channel=webchat for the sessions_send run (even just the prefix line), and I’ll help you tighten the “Evidence” section even more.

gloomy bobcat
#

2026-02-10T03:40:41.351Z [agent:nested] session=agent:unraid:main run=f198edd1-0d6e-4239-8cb8-4c8815dbbe4b channel=webchat Skill Check: none