From d519dc6976defefa8e841bcd85e58462921d126f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 27 Apr 2026 22:37:57 +0100 Subject: [PATCH] docs(channels): add channel docking concept --- docs/concepts/channel-docking.md | 145 +++++++++++++++++++++++++++++++ docs/concepts/session.md | 24 +---- docs/docs.json | 1 + docs/gateway/config-agents.md | 2 +- docs/tools/slash-commands.md | 4 + 5 files changed, 154 insertions(+), 22 deletions(-) create mode 100644 docs/concepts/channel-docking.md diff --git a/docs/concepts/channel-docking.md b/docs/concepts/channel-docking.md new file mode 100644 index 00000000000..da1dcabde64 --- /dev/null +++ b/docs/concepts/channel-docking.md @@ -0,0 +1,145 @@ +--- +summary: "Move one OpenClaw session's reply route between linked chat channels" +title: "Channel docking" +read_when: + - You want replies for one active session to move from Telegram to Discord, Slack, Mattermost, or another linked channel + - You are configuring session.identityLinks for cross-channel direct messages + - A /dock command says the sender is not linked or no active session exists +--- + +Channel docking is call forwarding for one OpenClaw session. + +It keeps the same conversation context, but changes where future replies for +that session are delivered. + +## Example + +Alice can message OpenClaw on Telegram and Discord: + +```json5 +{ + session: { + identityLinks: { + alice: ["telegram:123", "discord:456"], + }, + }, +} +``` + +If Alice sends this from Telegram: + +```text +/dock_discord +``` + +OpenClaw keeps the current session context and changes the reply route: + +| Before docking | After `/dock_discord` | +| ---------------------------- | --------------------------- | +| Replies go to Telegram `123` | Replies go to Discord `456` | + +The session is not recreated. The transcript history stays attached to the +same session. + +## Why use it + +Use docking when a task starts in one chat app but the next replies should land +somewhere else. + +Common flow: + +1. Start an agent task from Telegram. +2. Move to Discord where you are coordinating work. +3. Send `/dock_discord` from the Telegram session. +4. Keep the same OpenClaw session, but receive future replies in Discord. + +## Required config + +Docking requires `session.identityLinks`. The source sender and target peer +must be in the same identity group: + +```json5 +{ + session: { + identityLinks: { + alice: ["telegram:123", "discord:456", "slack:U123"], + }, + }, +} +``` + +The values are channel-prefixed peer ids: + +| Value | Meaning | +| -------------- | ---------------------------- | +| `telegram:123` | Telegram sender id `123` | +| `discord:456` | Discord direct peer id `456` | +| `slack:U123` | Slack user id `U123` | + +The canonical key (`alice` above) is only the shared identity group name. Dock +commands use the channel-prefixed values to prove that the source sender and +target peer are the same person. + +## Commands + +Dock commands are generated from loaded channel plugins that support native +commands. Current bundled commands: + +| Target channel | Command | Alias | +| -------------- | ------------------ | ------------------ | +| Discord | `/dock-discord` | `/dock_discord` | +| Mattermost | `/dock-mattermost` | `/dock_mattermost` | +| Slack | `/dock-slack` | `/dock_slack` | +| Telegram | `/dock-telegram` | `/dock_telegram` | + +The underscore aliases are useful on native command surfaces such as Telegram. + +## What changes + +Docking updates the active session delivery fields: + +| Session field | Example after `/dock_discord` | +| --------------- | ---------------------------------------- | +| `lastChannel` | `discord` | +| `lastTo` | `456` | +| `lastAccountId` | the target channel account, or `default` | + +Those fields are persisted in the session store and used by later reply +delivery for that session. + +## What does not change + +Docking does not: + +- create channel accounts +- connect a new Discord, Telegram, Slack, or Mattermost bot +- grant access to a user +- bypass channel allowlists or DM policies +- move transcript history to another session +- make unrelated users share a session + +It only changes the delivery route for the current session. + +## Troubleshooting + +**The command says the sender is not linked.** + +Add both the current sender and the target peer to the same +`session.identityLinks` group. For example, if Telegram sender `123` should dock +to Discord peer `456`, include both `telegram:123` and `discord:456`. + +**The command says no active session exists.** + +Dock from an existing direct-chat session. The command needs an active session +entry so it can persist the new route. + +**Replies still go to the old channel.** + +Check that the command replied with a success message, and confirm the target +peer id matches the id used by that channel. Docking only changes the active +session route; another session may still route elsewhere. + +**I need to switch back.** + +Send the matching command for the original channel, such as `/dock_telegram` or +`/dock-telegram`, from a linked sender. diff --git a/docs/concepts/session.md b/docs/concepts/session.md index 5ff9a284ff5..bc1e7acd851 100644 --- a/docs/concepts/session.md +++ b/docs/concepts/session.md @@ -56,27 +56,9 @@ If the same person contacts you from multiple channels, use ### Dock linked channels Dock commands let a user move the current direct-chat session's reply route to -another linked channel without starting a new session. For example, with: - -```json5 -{ - session: { - identityLinks: { - alice: ["telegram:123", "discord:456"], - }, - }, -} -``` - -if Telegram sender `123` sends `/dock_discord`, OpenClaw keeps the current -session context and stores Discord as the route for future replies from that -session. The command updates the session delivery fields (`lastChannel`, -`lastTo`, and `lastAccountId`) and persists them in the session store. - -Docking requires the source sender and target peer to appear in the same -`session.identityLinks` group. If no linked target exists, the command replies -with a setup hint and does not fall through to normal chat. Docking does not -grant channel access or bypass channel allowlists. +another linked channel without starting a new session. See +[Channel docking](/concepts/channel-docking) for examples, config, and +troubleshooting. Verify your setup with `openclaw security audit`. diff --git a/docs/docs.json b/docs/docs.json index 1dfdd1746fc..c3f116fcf45 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -1129,6 +1129,7 @@ "group": "Sessions and memory", "pages": [ "concepts/session", + "concepts/channel-docking", "concepts/session-pruning", "concepts/session-tool", { diff --git a/docs/gateway/config-agents.md b/docs/gateway/config-agents.md index b96851dfac0..6c155249a44 100644 --- a/docs/gateway/config-agents.md +++ b/docs/gateway/config-agents.md @@ -1182,7 +1182,7 @@ See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for preceden - `per-peer`: isolate by sender id across channels. - `per-channel-peer`: isolate per channel + sender (recommended for multi-user inboxes). - `per-account-channel-peer`: isolate per account + channel + sender (recommended for multi-account). -- **`identityLinks`**: map canonical ids to provider-prefixed peers for cross-channel session sharing. Dock commands such as `/dock_discord` use the same map to switch the active session's reply route to another linked channel peer. +- **`identityLinks`**: map canonical ids to provider-prefixed peers for cross-channel session sharing. Dock commands such as `/dock_discord` use the same map to switch the active session's reply route to another linked channel peer; see [Channel docking](/concepts/channel-docking). - **`reset`**: primary reset policy. `daily` resets at `atHour` local time; `idle` resets after `idleMinutes`. When both configured, whichever expires first wins. Daily reset freshness uses the session row's `sessionStartedAt`; idle reset freshness uses `lastInteractionAt`. Background/system-event writes such as heartbeat, cron wakeups, exec notifications, and gateway bookkeeping can update `updatedAt`, but they do not keep daily/idle sessions fresh. - **`resetByType`**: per-type overrides (`direct`, `group`, `thread`). Legacy `dm` accepted as alias for `direct`. - **`parentForkMaxTokens`**: max parent-session `totalTokens` allowed when creating a forked thread session (default `100000`). diff --git a/docs/tools/slash-commands.md b/docs/tools/slash-commands.md index 02d5e7c6072..25b786591a2 100644 --- a/docs/tools/slash-commands.md +++ b/docs/tools/slash-commands.md @@ -188,6 +188,10 @@ Current source-of-truth: ### Generated dock commands +Dock commands switch the current session's reply route to another linked +channel. See [Channel docking](/concepts/channel-docking) for setup, +examples, and troubleshooting. + Dock commands are generated from channel plugins with native-command support. Current bundled set: - `/dock-discord` (alias: `/dock_discord`)