Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
0635538
fix(qqbot): restore per-group session isolation under thread scope
Eric-GoodBoy-Tech Jul 31, 2026
f9cc1bc
fix(qqbot): address review — release anchors on cancel, purge seq orp…
Eric-GoodBoy-Tech Aug 1, 2026
7f1912b
fix(qqbot): set reply anchor deterministically at prompt start, respe…
Eric-GoodBoy-Tech Aug 1, 2026
f2daf02
fix(qqbot): release reply anchor after the final send, guard seq cont…
Eric-GoodBoy-Tech Aug 1, 2026
a41cdad
Merge remote-tracking branch 'origin/main' into fix/qqbot-session-sco…
Eric-GoodBoy-Tech Aug 1, 2026
2fd94e3
fix(qqbot): address review — purge own-key orphans only, extract anch…
Eric-GoodBoy-Tech Aug 1, 2026
2d9c4f3
fix(qqbot): address round-6 review — guard seq cleanup, turn-scoped s…
Eric-GoodBoy-Tech Aug 1, 2026
5bf5498
fix(qqbot): address round-7 review — clear flush flags on stale-state…
Eric-GoodBoy-Tech Aug 2, 2026
d4b99e6
fix(qqbot): address round-8 review — turn-aware anchor release, flush…
Eric-GoodBoy-Tech Aug 2, 2026
f5ace0c
fix(qqbot): address wenshao review — flush on cancel, chat_thread war…
Eric-GoodBoy-Tech Aug 2, 2026
2189179
fix(qqbot): address round-9 review — close release-cascade and cancel…
Eric-GoodBoy-Tech Aug 2, 2026
bdd4b86
fix(qqbot): address round-10 review — turn-guard onResponseComplete, …
Eric-GoodBoy-Tech Aug 2, 2026
cbd7faa
fix(qqbot): address round-11 review — purge ownership, orphan side-bu…
Eric-GoodBoy-Tech Aug 3, 2026
144b63f
fix(qqbot): bound the orphan purge to single-scope keys and guard msg…
Eric-GoodBoy-Tech Aug 6, 2026
216d201
Merge remote-tracking branch 'origin/main' into fix/qqbot-session-sco…
Eric-GoodBoy-Tech Aug 6, 2026
ca7c546
fix(qqbot): order anchor release before stream-state teardown and cle…
Eric-GoodBoy-Tech Aug 6, 2026
f5c3317
fix(cli): accept chat_thread in channel-settings sessionScope validation
Eric-GoodBoy-Tech Aug 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 26 additions & 25 deletions docs/users/features/channels/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,30 +50,30 @@ Channels are configured under the `channels` key in `settings.json`. Each channe

### Options

| Option | Required | Description |
| ------------------------ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | Yes | Channel type: `telegram`, `weixin`, `qq`, `dingtalk`, `wecom`, `feishu`, `github`, or a custom type from an extension (see [Plugins](./plugins)) |
| `token` | Telegram | Bot token. Supports `$ENV_VAR` syntax to read from environment variables. Not needed for WeChat, DingTalk, WeCom, or Feishu |
| `clientId` | DingTalk, Feishu | DingTalk AppKey or Feishu App ID. Supports `$ENV_VAR` syntax |
| `clientSecret` | DingTalk, Feishu | DingTalk AppSecret or Feishu App Secret. Supports `$ENV_VAR` syntax |
| `botId` | WeCom | WeCom intelligent robot Bot ID. Supports `$ENV_VAR` syntax. See [WeCom](./wecom) |
| `secret` | WeCom | WeCom intelligent robot Secret. Supports `$ENV_VAR` syntax. See [WeCom](./wecom) |
| `model` | No | Model to use for this channel (e.g., `qwen3.5-plus`). Overrides the default model. Useful for multimodal models that support image input |
| `senderPolicy` | No | Who can talk to the bot: `allowlist` (default), `open`, or `pairing` |
| `allowedUsers` | No | List of user IDs allowed to use the bot (used by `allowlist` and `pairing` policies) |
| `sessionScope` | No | How sessions are scoped: `user` (default), `thread`, or `single` |
| `cwd` | No | Working directory for the agent. Defaults to the current directory |
| `approvalMode` | No | Tool approval mode for channel sessions. Unattended webhook tasks require `yolo`; the setting applies to every session on the channel |
| `instructions` | No | Custom instructions prepended to the first message of each session |
| `webhooks` | No | Webhook sources and delivery targets for daemon-managed channels. See [Webhook-triggered tasks](#webhook-triggered-tasks) |
| `groupPolicy` | No | Group chat access: `disabled` (default), `allowlist`, or `open`. See [Group Chats](#group-chats) |
| `dmPolicy` | No | Private/DM access: `open` (default) or `disabled` (silently drop all DMs). Useful for group-only bots |
| `groupHistoryLimit` | No | Opt-in group history backfill. `0` or omitted disables it. A positive number persists that many authorized, unmentioned group messages for the next bot mention/reply. |
| `groups` | No | Per-group settings. Keys are group chat IDs or `"*"` for defaults. See [Group Chats](#group-chats) |
| `dispatchMode` | No | What happens when you send a message while the bot is busy: `steer` (default), `collect`, or `followup`. See [Dispatch Modes](#dispatch-modes) |
| `blockStreaming` | No | Progressive response delivery: `on` or `off` (default). See [Block Streaming](#block-streaming) |
| `blockStreamingChunk` | No | Chunk size bounds: `{ "minChars": 400, "maxChars": 1000 }`. See [Block Streaming](#block-streaming) |
| `blockStreamingCoalesce` | No | Idle flush: `{ "idleMs": 1500 }`. See [Block Streaming](#block-streaming) |
| Option | Required | Description |
| ------------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `type` | Yes | Channel type: `telegram`, `weixin`, `qq`, `dingtalk`, `wecom`, `feishu`, `github`, or a custom type from an extension (see [Plugins](./plugins)) |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] This PR's rewritten sessionScope row tells readers "GitHub and GitLab default to chat_thread", but this adjacent type row still omits gitlab from the supported channel types — the page contradicts itself, newly introduced by this diff's sessionScope edit. — Failure scenario: gitlab is bundled and first-class (channel-registry.ts:27 registers @qwen-code/channel-gitlab; gitlab.md documents "type": "gitlab"). A user who learns GitLab exists from the sessionScope row reads this row and concludes GitLab needs an extension or is unsupported. Suggested fix: add gitlab to the channel list (after github).

中文说明

本 PR 重写的 sessionScope 行告诉读者“GitHub 与 GitLab 默认 chat_thread”,但相邻的 type 行仍未把 gitlab 列入支持的渠道类型——页面自相矛盾,且是本 diff 的 sessionScope 修改新引入的。失败场景:gitlab 是内置一等渠道(channel-registry.ts:27 注册 @qwen-code/channel-gitlab;gitlab.md 文档写明 "type": "gitlab")。读者从 sessionScope 行得知 GitLab 存在,再看此行会误以为 GitLab 需要扩展或不被支持。修复建议:在渠道列表中加入 gitlab(放在 github 之后)。

— qwen3.8-max via Qwen Code /review (v0.21.7)

| `token` | Telegram | Bot token. Supports `$ENV_VAR` syntax to read from environment variables. Not needed for WeChat, DingTalk, WeCom, or Feishu |
| `clientId` | DingTalk, Feishu | DingTalk AppKey or Feishu App ID. Supports `$ENV_VAR` syntax |
| `clientSecret` | DingTalk, Feishu | DingTalk AppSecret or Feishu App Secret. Supports `$ENV_VAR` syntax |
| `botId` | WeCom | WeCom intelligent robot Bot ID. Supports `$ENV_VAR` syntax. See [WeCom](./wecom) |
| `secret` | WeCom | WeCom intelligent robot Secret. Supports `$ENV_VAR` syntax. See [WeCom](./wecom) |
| `model` | No | Model to use for this channel (e.g., `qwen3.5-plus`). Overrides the default model. Useful for multimodal models that support image input |
| `senderPolicy` | No | Who can talk to the bot: `allowlist` (default), `open`, or `pairing` |
| `allowedUsers` | No | List of user IDs allowed to use the bot (used by `allowlist` and `pairing` policies) |
| `sessionScope` | No | How sessions are scoped: `user` (default), `thread`, `chat_thread`, or `single`. Channel plugins may declare a different default (QQ defaults to `thread`; GitHub and GitLab default to `chat_thread`) |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] This PR adds chat_thread to this row and to the daemon settings validation set, but leaves the cross-linked sibling doc plugins.md:53 stating sessionScope is "user, thread, or single" — a contradiction this diff newly creates between the two pages. — Failure scenario: an extension/custom-channel user who wants per-chat-and-thread sessions reads plugins.md's enumeration (the only user-facing list now missing the value), concludes chat_thread is unavailable, and picks thread — for platforms with chat-local thread IDs, thread keys sessions as channel:(threadId||chatId) and merges unrelated chats sharing a thread ID into one shared session, the cross-chat bleed chat_thread exists to prevent. Suggested fix: update the plugins.md table row to user, thread, chat_thread, or single.

中文说明

本 PR 给这一行和守护进程设置校验集加了 chat_thread,却让相互链接的兄弟文档 plugins.md:53 仍写 sessionScope 为 "userthreadsingle"——本 diff 在两页之间新造了矛盾。失败场景:想要“按聊天+线程”会话的扩展/自定义渠道用户读 plugins.md 的枚举(唯一缺少该值的面向用户清单),误以为 chat_thread 不可用而选择 thread——对线程 ID 仅聊天内唯一的平台,thread 以 channel:(threadId||chatId) 建键,会把共享同一线程 ID 的无关聊天合并进同一个共享会话,正是 chat_thread 要防止的跨聊天串上下文。修复建议:把 plugins.md 表行更新为 userthreadchat_threadsingle

— qwen3.8-max via Qwen Code /review (v0.21.7)

| `cwd` | No | Working directory for the agent. Defaults to the current directory |
| `approvalMode` | No | Tool approval mode for channel sessions. Unattended webhook tasks require `yolo`; the setting applies to every session on the channel |
| `instructions` | No | Custom instructions prepended to the first message of each session |
| `webhooks` | No | Webhook sources and delivery targets for daemon-managed channels. See [Webhook-triggered tasks](#webhook-triggered-tasks) |
| `groupPolicy` | No | Group chat access: `disabled` (default), `allowlist`, or `open`. See [Group Chats](#group-chats) |
| `dmPolicy` | No | Private/DM access: `open` (default) or `disabled` (silently drop all DMs). Useful for group-only bots |
| `groupHistoryLimit` | No | Opt-in group history backfill. `0` or omitted disables it. A positive number persists that many authorized, unmentioned group messages for the next bot mention/reply. |
| `groups` | No | Per-group settings. Keys are group chat IDs or `"*"` for defaults. See [Group Chats](#group-chats) |
| `dispatchMode` | No | What happens when you send a message while the bot is busy: `steer` (default), `collect`, or `followup`. See [Dispatch Modes](#dispatch-modes) |
| `blockStreaming` | No | Progressive response delivery: `on` or `off` (default). See [Block Streaming](#block-streaming) |
| `blockStreamingChunk` | No | Chunk size bounds: `{ "minChars": 400, "maxChars": 1000 }`. See [Block Streaming](#block-streaming) |
| `blockStreamingCoalesce` | No | Idle flush: `{ "idleMs": 1500 }`. See [Block Streaming](#block-streaming) |

### Sender Policy

Expand All @@ -87,8 +87,9 @@ Controls who can interact with the bot:

Controls how conversation sessions are managed:

- **`user`** (default) — One session per user. All messages from the same user share a conversation.
- **`user`** (default) — One session per user. All messages from the same user share a conversation. `user` is the global default, but a channel plugin can declare a different default for its type: the QQ channel defaults to `thread`, while the GitHub and GitLab channels default to `chat_thread`.
- **`thread`** — One session per thread/topic. Useful for group chats with threads.
- **`chat_thread`** — One session per chat and thread, shared across participants even outside a group (for example, an issue or PR discussion).
- **`single`** — One shared session for all users. Everyone shares the same conversation.

### Channel Memory
Expand Down
12 changes: 11 additions & 1 deletion docs/users/features/channels/qqbot.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export QQ_APP_SECRET=<your-app-secret>
"appSecret": "$QQ_APP_SECRET",
"sandbox": false,
"senderPolicy": "open",
"sessionScope": "user",
"sessionScope": "thread",
"cwd": "/path/to/your/project",
"instructions": "你是一个通过 QQ Bot 对话的 AI 助手。回复控制在 2000 字符以内。",
"blockStreaming": "on",
Expand All @@ -73,6 +73,8 @@ export QQ_APP_SECRET=<your-app-secret>
}
```

> `sessionScope` defaults to `"thread"`. With `groupPolicy: "disabled"` this is a DM-only setup, where both `"thread"` and `"user"` behave identically — each direct message gets its own context. `"thread"` is shown here for consistency with the [Session Isolation](#session-isolation) section below.

### QQ-Specific Options

| Option | Default | Description |
Expand Down Expand Up @@ -106,6 +108,14 @@ To use the bot in QQ groups:

QQ Bot API V2 only delivers group messages that @mention the bot — the bot does not see all group messages. By default, `requireMention` is `true` and should be left that way for QQ.

### Session Isolation

The QQ channel defaults to `sessionScope: "thread"`: members of the same group share a single conversation context keyed by `<channel>:<group_openid>`, while different groups are isolated from each other. Each direct message gets its own context keyed by `<channel>:<user_openid>`.

A group thread session is a **shared session**: every member of the group reads and continues the same conversation history. Session-control commands (`/clear`, `/cancel`) and the `!` host-shell gate act on that shared session — in `"thread"` scope they affect the whole group, restricted to `allowedUsers` members when the list is non-empty. Permission-request answers are gated separately, by **chat + thread**: you can answer a pending permission request only for the same chat and thread you are in, and in a shared group session any member may do so — this gating is independent of `sessionScope`. If group members should not share history or control each other's turns, set `sessionScope: "user"` (each member gets a private session) or restrict membership via `senderPolicy` / `allowedUsers`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The new Session Isolation paragraph misstates two security-relevant gates. (1) The ! host-shell gate is not "restricted to allowedUsers members when the list is non-empty" — it is unconditionally disabled in ALL group chats and shared sessions (ChannelBase.ts:~5001-5031 rejects before any allowlist check; QQChannel has no override): nobody can run ! in a group. (2) Permission answers are not open to "any member" — /approve, /approve-always and /deny gate on isAuthorizedForSharedSession first (ChannelBase.ts:2721-2728), so with a non-empty allowedUsers a non-listed member's /approve is rejected. — Failure scenario: an operator reading this section believes allow-listed members can run host-shell commands (nobody can), or that any group member can approve tool permissions (only allowedUsers can) — the paragraph that exists to explain who controls the shared session misstates both gates, in opposite directions. Suggested fix: split the sentence — /clear and /cancel are restricted to allowedUsers when non-empty; the ! gate is disabled entirely in group chats; permission answers are likewise restricted to allowedUsers members in a shared session.

中文说明

新增的 Session Isolation 段落对两个安全相关门控的描述与实现不符。(1)! 宿主 shell 门控并非“当 allowedUsers 非空时限于其成员”——它在所有群聊和共享会话中无条件禁用(ChannelBase.ts:~5001-5031 在任何白名单检查之前拒绝;QQChannel 无重写):群里任何人都不能执行 !。(2)权限应答也并非“任何成员都可以”——/approve、/approve-always、/deny 先经 isAuthorizedForSharedSession 门控(ChannelBase.ts:2721-2728),allowedUsers 非空时非名单成员的 /approve 会被拒绝。失败场景:按此节配置的操作者会误以为白名单成员可执行宿主 shell 命令(实际不能),或任何群成员都能批准工具权限(实际仅 allowedUsers 可以)——这个本应解释“谁控制共享会话”的段落把两个门控都说反了。修复建议:拆分表述——/clear 与 /cancel 在白名单非空时限于其成员;! 门控在群聊中完全禁用;权限应答在共享会话中同样限于 allowedUsers 成员。

— qwen3.8-max via Qwen Code /review (v0.21.7)


For full-message mode this default is already what you want — with `groupAllPolicy: "all"`, keeping `"thread"` gives you shared context within a group and isolation across groups. If you instead set `sessionScope: "user"`, full-message traffic is fragmented per sender (a separate session for every member), which is not suitable for group full-message scenarios.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] This paragraph steers users into full-message mode via groupAllPolicy: "all", but groupAllPolicy is documented nowhere — not in this page's QQ-Specific Options table (appID/appSecret/sandbox only) nor anywhere else in docs/ at the reviewed commit — and the paragraph omits its mandatory companion setting. — Failure scenario: a reader who guesses groupAllPolicy: "all" from the paragraph hits a second trap: with the default requireMention: true, all non-@-mention group messages are silently dropped by GroupGate (QQChannel._checkGroupAllPolicyRequireMention warns at READY) — while this same page instructs "requireMention is true and should be left that way for QQ". Following the page's own guidance yields a bot that answers only @-mentions, unexplained. Suggested fix: add a groupAllPolicy row to the QQ-Specific Options table (values log/keyword/all, default off) and note that keyword/all also requires "groups": { "*": { "requireMention": false } } (mirroring the runtime warning).

中文说明

本段引导用户通过 groupAllPolicy: "all" 启用全量消息模式,但 groupAllPolicy 在任何地方都没有文档——本页 QQ-Specific Options 表(仅 appID/appSecret/sandbox)没有,被审 commit 的整个 docs/ 也没有——且段落遗漏了其必需的配套设置。失败场景:读者照本段猜出 groupAllPolicy: "all" 后会踩第二个坑:默认 requireMention: true 时所有非 @ 群消息被 GroupGate 静默丢弃(QQChannel._checkGroupAllPolicyRequireMention 在 READY 时告警)——而同一页面又写着“requireMention 默认 true 且 QQ 应保持如此”。照页面自身指引操作会得到一个只回复 @ 的机器人且无任何解释。修复建议:在 QQ-Specific Options 表加 groupAllPolicy 行(取值 log/keyword/all,默认关闭),并注明 keyword/all 还需 "groups": { "*": { "requireMention": false } }(与运行时告警一致)。

— qwen3.8-max via Qwen Code /review (v0.21.7)


See [Group Chats](./overview#group-chats) for full details on group policies and mention gating.

## Markdown Support
Expand Down
Loading
Loading