Repository navigation
feat(channels): route messages to prefix-specific sessions - #12595
Conversation
Validation report
Scope: macOS local verification with fake transports and no model calls. This is not a claim of real provider delivery or hosted CI success. No production Channel was restarted or reconfigured. |
yiliang114
left a comment
There was a problem hiding this comment.
LGTM at 76d30e2 per maintainer request. Zero open threads; all lanes green except two notes:
web-shell E2E Smokeshows red, but it's the pre-#12593 timeout: the run hit the old 20-minute wall and was cancelled during cleanup with 183 passed / 1 flaky / 0 failed — the merge-base of this branch predates the 30-minute bump that just landed on main. The PR's diff is entirely channels/serve (no web-shell client surface), so this lane cannot be exercising it. It'll go green after the next merge from main (or a re-run once the branch carries the new timeout).review-pris still queued (bot infrastructure, not a gate).
The branch will need a merge from main anyway to pick up the timeout fix before this lane can pass.
chiga0
left a comment
There was a problem hiding this comment.
No blocking findings.
Approval blockers: none.
Checked: message-routes.ts algorithm (longest-prefix sort, mention-stripping loop, whitespace-plus-non-empty guard); ChannelBase.applyMessageRoute WeakSet idempotency between preflightInbound and recordPendingGroupHistory; SessionRouter.routingKey with routeKey nests the existing scoped key in JSON.stringify([baseKey, routeKey]) — unrouted keys unchanged; groupHistoryKey appends route only when present; ChannelLoopStore.sameTarget includes messageRoute; config-utils.parseMessageRoutingConfig validates all edge cases; TelegramAdapter msg.caption ? envelope.text : '' preserves route-stripped text through async download; QQChannel routeText preserves non-bot mentions, alreadyPrefixed suppressed when routes configured; bypassMessageRoutes set on DWS todo/document provider events.
Cross-check against prior reviews: prior reviewer noted activateManagedSession does not thread messageRoute — confirmed; not a current bug (constructor throws on the only code path that would reach that call while routes are configured), but agree a comment recording the invariant would help future composability.
Not run: local vitest suite (no working tree). Author validation reports 1,609 targeted tests passing; serve A/B shows no response changes.
Reviewed with AI assistance.
What this PR does
Adds shared
messageRoutesconfiguration so one Channel connection can run multiple prefix-selected workflows with separate conversation sessions and first-turn instructions. An optionaldefaultMessageRoutenames the route for ordinary messages; without it, unmatched chat messages are ignored. The longest matching prefix wins and is removed before dispatch.Route identity is retained through queued turns, session recovery, group history, and scheduled loops. DWS native document/todo triggers remain independent of IM prefix routing, and Telegram attachments and QQ message formatting preserve the routed message body.
Why it's needed
A single connection often serves more than one purpose, such as reviewing changes and answering repository questions. Prefix routing lets operators provide focused instructions for each workflow without running multiple connections or mixing their conversation sessions.
Reviewer Test Plan
How to verify
/reviewand/QAwith different instructions. Send both prefixes in the same chat: each should create a separate session, strip its prefix, and inject only its own instructions on the first turn./QA: ordinary text should reuse the QA session unchanged.Evidence (Before & After)
Before: one shared instruction set and conversation route per configured session scope.
After: deterministic adapter tests and a built-output integration smoke verify configuration parsing, distinct route sessions, default routing, unmatched-message filtering, first-turn instructions, collect dispatch, and persisted-session recovery. No external IM or model calls were made.
Validation: full workspace build and typecheck; targeted channel-base, DWS, Telegram, QQ, CLI configuration, and settings-management tests; ESLint on changed TypeScript files.
Tested on
Environment (optional)
Local workspace builds with fake transport and bridge implementations; no production service changes.
Risk & Scope
messageRoutesis currently incompatible withmultiSession. No legacymessagePrefixAPI is introduced. Adapter-generated media placeholders and native provider events remain outside prefix routing.Linked Issues
None.
中文说明
变更内容
新增通用
messageRoutes配置,让单个 Channel 连接通过不同前缀运行不同工作流,分别使用独立会话和首轮指令。可选defaultMessageRoute指定普通消息使用的路由;不设置时忽略未匹配的聊天消息。优先匹配最长前缀,并在分发前移除前缀。路由信息会保留到排队消息、会话恢复、群聊历史和定时任务中。DWS 原生文档与待办触发不受 IM 前缀过滤影响,Telegram 附件和 QQ 消息格式也会保留路由处理后的正文。
变更动机
同一个连接常常同时承担评审代码和回答仓库问题等职责。前缀路由允许为每个工作流设置针对性指令,不必运行多个连接,也不会混用各工作流的会话上下文。
评审验证计划
验证方式
/review和/QA配置不同指令,在同一个聊天中分别发送:应建立独立会话、移除前缀,并仅在首轮注入对应指令。/QA后,普通文本应原样进入 QA 会话。前后对比与证据
之前:每个配置的会话范围使用一套指令和会话路由。
之后:确定性的适配器测试和构建产物集成验证覆盖配置解析、独立路由会话、默认路由、未匹配过滤、首轮指令、collect 分发和持久化会话恢复。未调用外部 IM 或模型。
验证包括全工作区构建和类型检查,channel-base、DWS、Telegram、QQ、CLI 配置及设置管理的定向测试,以及修改的 TypeScript 文件的 ESLint 检查。
测试平台
测试环境
本地工作区构建,使用模拟传输与桥接实现,未修改生产服务。
风险与范围
multiSession同时使用,不引入旧messagePrefixAPI。适配器生成的媒体占位文本和原生平台事件不参与前缀路由。关联 Issue
无。