Skip to content

Bot bridge: mobile session management for all IM channels (conversation continuity, chat commands, per-conversation working directory) #2325

Description

@Astro-Han

Problem

Users of the bot bridge (WeChat, Telegram, Feishu, WeCom, Discord, DingTalk, QQ, Slack) cannot tell — let alone control — where a conversation works. A common first impression on WeChat: "I can't figure out how to point a conversation at a folder. Can it work like a local session — start inside a folder and understand its context?"

Looking deeper, the gap is structural, not just the folder. The bridge today is a dumb pipe: a message goes in, a reply comes out. Everything around it is missing:

  • No conversation continuity: the conversation → session mapping in bot-incoming-main.ts (${platform}:${chatId} → sessionId) is an in-memory Map. Restart Maka and every bot conversation silently starts over in a fresh session.
  • No per-conversation working directory: bot sessions are created with resolveBotCreateTarget() → { cwd: await projectRoot.current() } — the project last selected on the desktop, shared by every conversation on every channel. There is no way to bind a conversation to a folder, or even to see which folder it works in.
  • No management commands: only a bare restart/help keyword. No way to list conversations, start a new one, stop a running turn, or check status. (/stop doesn't exist at all.)
  • Hardcoded explore permissions: bot sessions are forced to permissionMode: 'explore', so the agent cannot take write actions from any IM channel.

The issue is not "how to pick a folder" — it's that IM conversations are not a session-management surface. Local sessions get a project picker, a sidebar, model selection, and permission prompts; a bot conversation gets none of that. Compare OpenClaw's Control UI and Hermes' chat commands: both expose conversation management (resume, new, stop, status, working directory, approvals) over IM.

Desired outcome

Turn the bot bridge into a mobile session-management surface, uniform across all connected channels, with the local session machinery as the single implementation:

  • Conversation continuity (P0) — persist the conversationKey → sessionId mapping (Hermes sessions.json / PawWork SessionPointers / OpenClaw SQLite are the reference shapes). A conversation resumes its session across restarts, and any directory/model attributes survive with it.
  • Chat command framework (P0) — /new (fresh session), /sessions (list/resume), /stop (interrupt the running turn), /status (current session, working directory, model). Implemented once in the platform-agnostic bot layer (bot-incoming-main.ts), so every channel — WeChat, Telegram, Feishu, WeCom, Discord, DingTalk, QQ, Slack — inherits it.
  • Per-conversation working directory (P0) — the directory is a session attribute, exactly like a local session: chosen at creation (defaulting to the configurable default working directory, see Add a configurable default working directory for sessions without a specific project #2322), changeable from a desktop-side conversation management entry (folder picker), visible in /status and in the sidebar grouping. The bridge only passes the directory through at session creation — it does not implement any folder logic of its own.
  • Permission fix + in-chat approval (P0) — bot sessions follow the global default permission mode (auto/bypass) instead of being forced to explore. In auto mode, permission requests render as chat text and are answered by keywords (PawWork's blocker pattern: 是/好/允许/同意). Native buttons (Hermes send_exec_approval) are a later iteration, on channels that support them.
  • Later directions — inbound media enrichment (image → vision, voice → STT; both Hermes and OpenClaw have reference implementations). Model selection is deliberately out of scope (covered by subagent capability).

Alternatives or workarounds

No workaround today. We compared three open-source bridges:

Dimension PawWork remote-bridge Hermes gateway OpenClaw channels
Layering Platform (protocol) → Engine (routing) → Gateway (supervision); thin adapters BasePlatformAdapter normalizes to MessageEvent; Runner owns everything else ChannelPlugin contract; core is channel-agnostic
Conversation identity <platform>:<channel>:<user> key, persisted SessionPointers (atomic, root-conflict check) Deterministic key agent:main:<platform>:dm:<chat_id>, persisted sessions.json, resume_pending recovery Deterministic agent:<id>:<channel>:<scope>:<peerId>, per-agent SQLite
Working directory createSession() has no directory param → all IM sessions land in hardcoded ~/PawWork (lesson: the seam must accept a directory from day one) Channels share one global cwd; per-session cwd machinery exists (contextvar, explicit_cwd, only explicit dirs persist) Bind at session creation, persist on the session (spawnedCwd), reused every turn
Commands / approvals /new /sessions /stop /help; blockers as chat text answered by keywords /new /reset /stop /sessions; button-based model picker, clarify, exec approval /new + customCommands; native approval buttons where the channel supports them

Conclusions:

  1. Keep the bridge thin — adapters normalize I/O only; sessions, directories, commands, and permissions live in the core (all three projects agree).
  2. Persist the conversation mapping with a deterministic key — restart recovery becomes free.
  3. Bind the directory at session creation and persist it as a session attribute — never resolve it per message, and never build a separate bot-side directory model. Maka's existing seam (resolveBotCreateTarget) is the single place to extend.
  4. Caveats are per-channel: PawWork's missing directory param silently parked every IM session in ~/PawWork. WeChat iLink has no server push (replies echo context_token) and one credential = one poller (409) — commands must work as replies to inbound messages; other channels bring their own constraints (e.g. Telegram's single-getUpdates consumer).
中文对照(简体中文)

问题

bot 桥的用户(微信、Telegram、飞书、企业微信、Discord、钉钉、QQ、Slack)既说不清、也控制不了对话到底在哪个文件夹里干活。微信上常见的第一印象是:"我没弄懂怎么指定对话所在的文件夹。能不能像本地对话一样,直接在某个文件夹下对话,理解文件夹的上下文?"

往深看,缺口是结构性的,不只是文件夹的问题。现在的桥接器只是一根"哑管道":消息进、回复出,周围该有的东西全都没有:

  • 对话没有连续性:bot-incoming-main.ts 里的对话 → 会话映射(${platform}:${chatId} → sessionId)只是内存里的 Map。重启 Maka 后,每个 bot 对话都会悄悄地在全新会话里重新开始。
  • 没有按对话区分的工作目录:bot 会话创建时 resolveBotCreateTarget() → { cwd: await projectRoot.current() }——用的是桌面端最后选中的项目,所有渠道的所有对话共享。既不能把某个对话绑定到某个文件夹,甚至看不到它在哪个文件夹干活。
  • 没有管理命令:只有裸的 restart/help 关键词。不能列出会话、不能开新会话、不能中断进行中的回合、不能查状态。(/stop 压根不存在。)
  • 权限被写死成 explore:bot 会话被强制 permissionMode: 'explore',agent 在任意 IM 渠道里完全不能执行写操作。

问题不在于"怎么选文件夹",而在于IM 对话不是一个会话管理面。本地会话有项目选择器、侧栏、模型选择、权限提示;bot 对话这些全都没有。反观 OpenClaw 的 Control UI 和 Hermes 的聊天命令:两者都把会话管理能力(续聊、新建、停止、状态、工作目录、审批)暴露到了 IM 里。

期望结果

把 bot 桥变成手机端的会话管理面,对所有已接入渠道统一生效,并且以本地会话机制为唯一实现:

  • 对话连续性(P0)——持久化 conversationKey → sessionId 映射(参考形态:Hermes sessions.json / PawWork SessionPointers / OpenClaw SQLite)。对话跨重启续上原会话,绑定的目录、模型等属性随之保留。
  • 聊天命令框架(P0)——/new(开新会话)、/sessions(列出/续聊)、/stop(中断当前回合)、/status(当前会话、工作目录、模型)。在平台无关的 bot 层(bot-incoming-main.ts)实现一次,所有渠道——微信、Telegram、飞书、企业微信、Discord、钉钉、QQ、Slack——自然继承。
  • 按对话的工作目录(P0)——目录是会话属性,和本地会话完全一致:创建时选定(默认落到可配置的默认工作目录,见 Add a configurable default working directory for sessions without a specific project #2322),可在桌面端的对话管理入口(文件夹选择器)修改,在 /status 和侧栏分组里可见。桥接器只在建会话时把目录传进去,自身不实现任何文件夹逻辑。
  • 权限修正 + 聊天内审批(P0)——bot 会话跟随全局默认权限模式(auto/bypass),不再强制 explore。auto 模式下,权限请求渲染成聊天文本,用关键词回复(PawWork 的 blocker 模式:是/好/允许/同意)。原生按钮(Hermes send_exec_approval)留到后续迭代,且只用于支持的渠道。
  • 后续方向——入站媒体富化(图片 → vision、语音 → STT;Hermes 和 OpenClaw 都有现成实现可参考)。模型选择有意不做(由 subagent 能力覆盖)。

替代方案或变通

今天没有任何变通。我们对比了三个开源桥接实现:

维度 PawWork remote-bridge Hermes gateway OpenClaw channels
分层 Platform(协议)→ Engine(路由)→ Gateway(监督);适配器很薄 BasePlatformAdapter 归一化为 MessageEvent;其余全归 Runner ChannelPlugin 契约;核心与渠道无关
会话身份 <platform>:<channel>:<user> key,SessionPointers 持久化(原子写、根冲突检查) 确定性 key agent:main:<platform>:dm:<chat_id>,sessions.json 持久化,resume_pending 恢复 确定性 agent:<id>:<channel>:<scope>:<peerId>,按 agent 存 SQLite
工作目录 createSession() 没有目录参数 → 所有 IM 会话落进硬编码 ~/PawWork(教训:接缝第一天就要接受目录) 渠道共享一个全局 cwd;per-session cwd 机制已存在(contextvar、explicit_cwd,只有显式选择的目录才落库) 建会话时绑定、持久化在会话上(spawnedCwd),每轮沿用
命令/审批 /new /sessions /stop /help;blocker 以聊天文本呈现、关键词回复 /new /reset /stop /sessions;按钮化模型选择、clarify、exec 审批 /new + customCommands;渠道支持时用原生审批按钮

结论:

  1. 桥接器保持薄——适配器只做消息收发归一化;会话、目录、命令、权限都放核心层(三家一致)。
  2. 用确定性 key 持久化对话映射——重启恢复零成本。
  3. 建会话时绑定目录、作为会话属性持久化——绝不在消息级别临时解析,也绝不做一套 bot 侧的目录模型。Maka 现有的接缝(resolveBotCreateTarget)就是唯一需要扩展的地方。
  4. 踩坑按渠道各有各的:PawWork 因为少了目录参数,所有 IM 会话静默落进 ~/PawWork。微信 iLink 没有服务端推送(回复必须回显 context_token)、一份凭据只能一个轮询者(409)——命令必须作为入站消息的回复来工作;其他渠道也有各自的约束(比如 Telegram 的单一 getUpdates consumer)。

Activity

  1. changed the title [-]WeChat bridge: mobile session management (conversation continuity, chat commands, per-conversation working directory)[/-] [+]Bot bridge: mobile session management for all IM channels (conversation continuity, chat commands, per-conversation working directory)[/+] on Aug 6, 2026
  2. me2seeks commented on Aug 9, 2026

    @me2seeks
    Contributor

    I'd like to take the conversation-continuity slice first. I'll keep it focused: add a dedicated durable conversation-to-Session binding seam, restore valid bindings after restart, rebind archived or removed Sessions through the existing lifecycle path, and preserve the platform source-message identity when submitting the Host turn so reconnect redelivery cannot start a duplicate Turn. Commands, cwd management, permissions, and media stay out of this slice. If nobody is already on it, pls assign it to me.

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

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions