You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Bot bridge: mobile session management for all IM channels (conversation continuity, chat commands, per-conversation working directory) #2325
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:
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:
Keep the bridge thin — adapters normalize I/O only; sessions, directories, commands, and permissions live in the core (all three projects agree).
Persist the conversation mapping with a deterministic key — restart recovery becomes free.
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.
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).
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
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.
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:
bot-incoming-main.ts(${platform}:${chatId}→ sessionId) is an in-memoryMap. Restart Maka and every bot conversation silently starts over in a fresh session.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.restart/helpkeyword. No way to list conversations, start a new one, stop a running turn, or check status. (/stopdoesn't exist at all.)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:
conversationKey → sessionIdmapping (Hermessessions.json/ PawWorkSessionPointers/ OpenClaw SQLite are the reference shapes). A conversation resumes its session across restarts, and any directory/model attributes survive with it./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./statusand in the sidebar grouping. The bridge only passes the directory through at session creation — it does not implement any folder logic of its own.auto/bypass) instead of being forced toexplore. Inautomode, permission requests render as chat text and are answered by keywords (PawWork's blocker pattern: 是/好/允许/同意). Native buttons (Hermessend_exec_approval) are a later iteration, on channels that support them.Alternatives or workarounds
No workaround today. We compared three open-source bridges:
BasePlatformAdapternormalizes toMessageEvent; Runner owns everything elseChannelPlugincontract; core is channel-agnostic<platform>:<channel>:<user>key, persistedSessionPointers(atomic, root-conflict check)agent:main:<platform>:dm:<chat_id>, persistedsessions.json,resume_pendingrecoveryagent:<id>:<channel>:<scope>:<peerId>, per-agent SQLitecreateSession()has no directory param → all IM sessions land in hardcoded~/PawWork(lesson: the seam must accept a directory from day one)explicit_cwd, only explicit dirs persist)spawnedCwd), reused every turn/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 themConclusions:
resolveBotCreateTarget) is the single place to extend.~/PawWork. WeChat iLink has no server push (replies echocontext_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 对话都会悄悄地在全新会话里重新开始。resolveBotCreateTarget() → { cwd: await projectRoot.current() }——用的是桌面端最后选中的项目,所有渠道的所有对话共享。既不能把某个对话绑定到某个文件夹,甚至看不到它在哪个文件夹干活。restart/help关键词。不能列出会话、不能开新会话、不能中断进行中的回合、不能查状态。(/stop压根不存在。)permissionMode: 'explore',agent 在任意 IM 渠道里完全不能执行写操作。问题不在于"怎么选文件夹",而在于IM 对话不是一个会话管理面。本地会话有项目选择器、侧栏、模型选择、权限提示;bot 对话这些全都没有。反观 OpenClaw 的 Control UI 和 Hermes 的聊天命令:两者都把会话管理能力(续聊、新建、停止、状态、工作目录、审批)暴露到了 IM 里。
期望结果
把 bot 桥变成手机端的会话管理面,对所有已接入渠道统一生效,并且以本地会话机制为唯一实现:
conversationKey → sessionId映射(参考形态:Hermessessions.json/ PawWorkSessionPointers/ OpenClaw SQLite)。对话跨重启续上原会话,绑定的目录、模型等属性随之保留。/new(开新会话)、/sessions(列出/续聊)、/stop(中断当前回合)、/status(当前会话、工作目录、模型)。在平台无关的 bot 层(bot-incoming-main.ts)实现一次,所有渠道——微信、Telegram、飞书、企业微信、Discord、钉钉、QQ、Slack——自然继承。/status和侧栏分组里可见。桥接器只在建会话时把目录传进去,自身不实现任何文件夹逻辑。auto/bypass),不再强制explore。auto模式下,权限请求渲染成聊天文本,用关键词回复(PawWork 的 blocker 模式:是/好/允许/同意)。原生按钮(Hermessend_exec_approval)留到后续迭代,且只用于支持的渠道。替代方案或变通
今天没有任何变通。我们对比了三个开源桥接实现:
BasePlatformAdapter归一化为MessageEvent;其余全归 RunnerChannelPlugin契约;核心与渠道无关<platform>:<channel>:<user>key,SessionPointers持久化(原子写、根冲突检查)agent:main:<platform>:dm:<chat_id>,sessions.json持久化,resume_pending恢复agent:<id>:<channel>:<scope>:<peerId>,按 agent 存 SQLitecreateSession()没有目录参数 → 所有 IM 会话落进硬编码~/PawWork(教训:接缝第一天就要接受目录)explicit_cwd,只有显式选择的目录才落库)spawnedCwd),每轮沿用/new/sessions/stop/help;blocker 以聊天文本呈现、关键词回复/new/reset/stop/sessions;按钮化模型选择、clarify、exec 审批/new+ customCommands;渠道支持时用原生审批按钮结论:
resolveBotCreateTarget)就是唯一需要扩展的地方。~/PawWork。微信 iLink 没有服务端推送(回复必须回显context_token)、一份凭据只能一个轮询者(409)——命令必须作为入站消息的回复来工作;其他渠道也有各自的约束(比如 Telegram 的单一 getUpdates consumer)。