feat(channels): expose workspace-scoped observed contacts - #7109
Conversation
🩺 serve daemon A/BBuilt the PR base vs this PR head
|
| field | PR base (before) | this PR (after) |
|---|---|---|
features[] |
— | "workspace_channel_observed_contacts" |
— Qwen Code · serve A/B
3650a2c to
f840333
Compare
|
Please do not rebase or force-push to an active PR as it invalidates existing review comments. Note for future reference, the bots always squash all changes into a single commit automatically as part of the integration. 中文请勿对活跃的 PR 执行 rebase 或 force-push,因为这会使已有的评审评论失效。另外,供日后参考:作为集成流程的一部分,机器人始终会自动将所有改动压缩(squash)为单个提交。 |
Review — workspace-scoped observed contactsReviewed at head OverviewAccepted inbound IM messages (post direct/group, mention, sender, pairing gates) are normalized into What holds up under scrutiny
Suggestions (non-blocking)
Nits
Test coverageStrong overall: every gate class, dedup, non-blocking failure with leak-absence, direct-vs-group semantics, refresh, staleness, bounds, permissions, malformed data, workspace isolation, no-fallback, auth, and capability advertisement. Small gaps: route-level invalid freshness only covers SecurityGood posture. Returning complete platform IDs is intentional and clearly documented; bearer auth ordering, trust gating, workspace partition isolation, private file modes, symlink-safe writes, and sanitized error/log surfaces all check out. No message content, credentials, or webhook traffic is persisted, and the webhook production surface is untouched in this diff. 中文版本评审 — workspace 级观测联系人基于 head 概述通过单聊/群聊、@、发送者、配对各 gate 的入站 IM 消息,在 经得起推敲的部分
建议(非阻塞)
细节
测试覆盖整体扎实:全部 gate 类别、去重、非阻塞失败 + 无泄漏断言、单聊/群聊语义、刷新、过期、边界、权限、损坏数据、workspace 隔离、不回退、认证与 capability 广告。小缺口:路由层非法 freshness 仅覆盖 安全姿态良好。返回完整平台 ID 是有意为之且文档明确;Bearer 认证顺序、信任门控、分区隔离、私有文件权限、防符号链接写入、净化的错误/日志面均核查通过。不持久化消息内容、凭据或 webhook 流量,本 diff 未触碰 webhook 生产面。 |
|
Thanks for the PR! Template looks good ✓ Problem: This is a feature request (closes #7103), not a bug fix. The use case is well-defined — clients that help users configure delivery destinations need to discover platform user IDs that are currently transient. Live E2E evidence with real Feishu and DingTalk bots is described in the PR body. Direction: Aligned with qwen-code's channel infrastructure. The feature adds a read-only, workspace-scoped API for recently observed IM contacts behind existing bearer auth. No existing contracts are modified; webhook behavior is explicitly preserved. This is additive daemon API surface. Size: 19 files, +1511/-0 total. Breakdown: ~563 production logic lines, ~699 test lines, ~249 doc/design lines. No core module paths ( Approach: The scope feels right for the stated goal. Observation hook in 中文说明感谢贡献! 模板完整 ✓ 问题: 这是一个功能请求(关闭 #7103),不是 bug 修复。用例定义清晰——帮助用户配置投递目标的客户端需要发现当前瞬态的平台用户 ID。PR 正文描述了飞书和钉钉真实机器人的 E2E 证据。 方向: 与 qwen-code 的 channel 基础设施对齐。该功能在现有 Bearer 认证后增加只读的、workspace 级 IM 联系人观测 API。不修改现有契约,webhook 行为明确保持不变。这是纯新增的 daemon API 面。 规模: 19 个文件,+1511/-0。分解:约 563 行生产逻辑,约 699 行测试,约 249 行文档/设计。未触及核心模块路径( 方案: 范围与目标匹配。在 — Qwen Code · qwen3.7-max Reviewed at |
Round 2 — re-review at
|
Code ReviewReviewed the full diff at Architecture — The observation hook sits in What holds up well:
Non-blocking observations:
Test ResultsAll 1,308 focused tests pass locally: PR-specific code typechecks clean (pre-existing Tmux testing: N/A — this PR changes daemon API and channel runtime behavior with no TUI surface. The comprehensive unit test suite (covering every gate class, dedup, freshness, workspace isolation, auth, malformed data, and capability advertisement) is the appropriate verification. 中文说明代码审查基于 架构 — 观测钩子位于 做得好的部分:
非阻塞建议:
测试结果全部 1,308 个聚焦测试本地通过:ChannelBase 463、observed-contact-store 11、daemon-worker 66、runtime 12、route 6、server 750。 PR 相关代码 typecheck 通过( Tmux 测试: 不适用——本 PR 仅修改 daemon API 和 channel 运行时行为,无 TUI 界面。全面的单元测试套件(覆盖所有 gate 类别、去重、新鲜度、workspace 隔离、认证、损坏数据和 capability 广告)是合适的验证方式。 — Qwen Code · qwen3.7-max Reviewed at |
|
Confidence: 4/5 Verdict: Approve ✅This PR adds a well-scoped, read-only daemon API for workspace-scoped observed channel contacts. The observation boundary, storage hygiene, route gating, and security posture are all done carefully. The diff is purely additive (19 files, +1511/-0), webhook behavior is explicitly preserved, and the test suite is thorough. Summary:
What I'd flag for the maintainer's attention (non-blocking): The 500-entry cap spans all channels and groups in the workspace — a noisy group could evict direct-user entries. Fine as v1 behavior, but worth noting if this API sees heavy use. 中文说明置信度:4/5 结论:批准 ✅本 PR 增加了范围明确、只读的 daemon API,用于 workspace 级观测联系人。观测边界、存储卫生、路由门控和安全姿态都做得很仔细。diff 为纯新增(19 个文件,+1511/-0),webhook 行为明确保持不变,测试套件充分。 摘要:
提请 maintainer 注意(非阻塞): 500 条上限覆盖整个 workspace 的所有 channel 和群——高流量群可能挤掉单聊用户条目。作为 v1 可接受,但如果此 API 使用量大需注意。 — Qwen Code · qwen3.7-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
Local build & live-run verification — merge referenceVerified at head Verdict: GREEN — ready to merge from my side. 1,680 tests green, and a 17-check live E2E against the real daemon confirms every behavior the PR body claims, including both round-1 fixes. Round-1 findings → status at this head
Test suites (isolated worktree, real install)
Live E2E — real daemon, real channel worker, full inbound→outbound loopBuilt Highlights beyond the PR's own test plan:
What the fix commit changed, tied to live proofNotes for the merger
中文版本(Chinese version)本地构建与真实运行验证 — 合并参考在 head 结论:GREEN — 我这边认为可以合并。 1,680 个测试全绿,另有针对真实 daemon 的 17 项 live E2E 检查,逐一证实了 PR 描述声称的行为,包括两处 round-1 修复。 Round-1 发现 → 当前 head 状态
测试套件(隔离 worktree,真实安装)
Live E2E — 真实 daemon + 真实 channel worker + 完整出入站回路构建产物
给合并者的说明
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not reviewed: chunk 1 — launched with a prompt that is not the one the CLI built.
Not reviewed: chunk 5 — launched with a prompt that is not the one the CLI built.
Not reviewed: chunk 4 — launched with a prompt that is not the one the CLI built.
Not reviewed: chunk 3 — launched with a prompt that is not the one the CLI built.
Not reviewed: chunk 6 — launched with a prompt that is not the one the CLI built.
Not reviewed: chunk 6 — launched with a prompt that is not the one the CLI built.
Not reviewed: chunk 2 — launched with a prompt that is not the one the CLI built.
Not reviewed: chunk 5 — launched with a prompt that is not the one the CLI built.
Not reviewed: chunk 1 — launched with a prompt that is not the one the CLI built.
Not reviewed: chunk 3 — launched with a prompt that is not the one the CLI built.
Not reviewed: chunk 4 — launched with a prompt that is not the one the CLI built.
Not reviewed: chunk 2 — launched with a prompt that is not the one the CLI built.
— qwen3.7-max via Qwen Code /review
|
@qwen-code /review |
|
@wenshao @qqqys The two latest bot Critical threads were verified against the current head and are false positives: raw fallback labels intentionally accept the complete platform ID bound, then persistence truncates only the display label to 256 while preserving the delivery ID. I replied with the contract evidence and resolved both threads; no code change is needed. My |



What this PR does
This PR adds a workspace-scoped graph of contacts dynamically observed by daemon-managed DingTalk, Feishu, Telegram, and WeCom channels. After a real inbound message passes the existing direct/group, mention, sender, and pairing gates, the worker records the complete platform user ID and, when present, the group and topic IDs before command or Agent handling begins.
The authenticated read API separates direct-message users from group observations. Top-level
userscontains only users observed in direct conversations; each group contains users observed in that group, and Feishu or Telegram topics contain users observed in that topic. Results include labels and independentlastObservedAttimestamps, default to a seven-day freshness window, and accept a boundedfreshWithinSecondsoverride.Observations are stored in a bounded private registry partitioned by the daemon workspace hash under
QWEN_HOME. Qualified workspace requests resolve an exact registered, trusted runtime without primary fallback. Responses return complete IDs intentionally, useCache-Control: no-store, require daemon bearer authentication, and are discoverable through theworkspace_channel_observed_contactscapability. Message content, payloads, attachments, credentials, bot output, proactive sends, and webhook traffic are never stored. Existing webhook behavior is unchanged.Why it's needed
Clients that help users configure inspection reports or scheduled IM notifications need a simple way to present selectable delivery destinations. The channel worker already receives the relevant platform identifiers during normal interactions, but they are transient. This API exposes recent
label + idchoices without platform directory permissions, member enumeration, or manual ID discovery.Reviewer Test Plan
How to verify
workspace_channel_observed_contacts, unauthenticated requests are rejected, andGET /workspace/channel/observed-contactsinitially returns emptyusersandgroups.userswith the complete platform ID, label, and observation timestamp.users.freshWithinSecondsvalues. Confirm stale users, groups, topics, and relationships are filtered. Query a trusted secondary workspace and confirm exact partition isolation; missing or untrusted selectors must not fall back to the primary workspace.The released
qwen 0.18.5baseline returns HTTP 401 without its bearer token and HTTP 404 with a valid token forGET /workspace/channel/observed-contacts.Evidence (Before & After)
N/A — this changes daemon APIs and channel runtime behavior, with no TUI surface.
Live E2E evidence
users, the group participant undergroups[].users, and the topic participant undergroups[].topics[].users, with complete platform IDs and canonical UTC timestamps. A one-second freshness window returned an empty graph while a 600-second window returned the relationships, and the bot response was delivered back to Feishu.openConversationIdfor the group, and the participant undergroups[].users;topicsremained empty as expected for the current DingTalk adapter. Freshness filtering and the bot reply both succeeded.Tested on
Environment (optional)
Node.js 22+ on macOS with 833 focused channel/store/daemon/route tests, repository lint and typecheck, a complete build and bundle, and the exact no-AK capability integration test. Live Feishu and DingTalk transport was verified as described above. Telegram and WeCom were covered by automated tests but were not exercised against live platform credentials.
Risk & Scope
Linked Issues
Closes #7103
Follow-up: #7154
中文说明
本 PR 做了什么
本 PR 为 daemon 管理的 DingTalk、Feishu、Telegram 和 WeCom channel 增加 workspace 级动态观测联系人图谱。真实 IM 入站消息通过现有的单聊/群聊、@、发送者和配对校验后,worker 会在命令或 Agent 开始处理前记录完整的平台用户 ID,以及存在时的群聊和话题 ID。
带认证的读取 API 会区分单聊用户与群聊观测关系。顶层
users只包含在单聊中观测到的用户;每个群包含在该群中观测到的用户,飞书或 Telegram 话题还包含在对应话题中观测到的用户。结果包含标签和独立的lastObservedAt时间戳,默认使用七天新鲜度窗口,并支持有边界的freshWithinSeconds覆盖。观测数据保存在
QWEN_HOME下按 daemon workspace 哈希分区、容量受限的私有注册表中。带 workspace selector 的请求会解析到精确、已注册且可信的 runtime,不会回退到 primary。响应有意返回完整 ID,使用Cache-Control: no-store,要求 daemon Bearer 认证,并通过workspace_channel_observed_contactscapability 供客户端发现。消息内容、payload、附件、凭据、机器人出站、主动推送和 webhook 流量均不会写入注册表。现有 webhook 行为保持不变。为什么需要它
帮助用户配置巡检报告或定时 IM 通知的客户端,需要一种简单方式展示可选择的投递目标。channel worker 在正常交互中已经收到相关平台标识,但这些信息目前是瞬时的。该 API 无需平台目录权限、成员枚举或用户手工发现 ID,即可提供近期的
label + id选项。Reviewer 测试计划
如何验证
workspace_channel_observed_contacts,未认证请求会被拒绝,且GET /workspace/channel/observed-contacts初始返回空的users和groups。users中,且包含完整平台 ID、标签和观测时间。users。freshWithinSeconds查询。确认过期的用户、群聊、话题和关系会被过滤。查询可信的第二 workspace,确认严格的分区隔离;缺失或不可信的 selector 不得回退到 primary workspace。已发布的
qwen 0.18.5在没有 Bearer token 时对GET /workspace/channel/observed-contacts返回 HTTP 401,携带有效 token 后返回 HTTP 404。证据(Before & After)
N/A — 本变更影响 daemon API 和 channel runtime 行为,没有 TUI 界面。
真实 E2E 证据
users返回单聊用户,在groups[].users返回群参与者,在groups[].topics[].users返回话题参与者,并保留完整平台 ID 和规范 UTC 时间。1 秒新鲜度窗口返回空图,600 秒窗口返回完整关系,机器人回复也成功回发飞书。openConversationId,以及groups[].users中的参与者;当前钉钉适配器下topics按预期为空。新鲜度过滤和机器人回复均成功。已测试系统
环境(可选)
在 macOS 与 Node.js 22+ 上运行了 833 个 channel/store/daemon/route 聚焦测试、仓库 lint 与 typecheck、完整 build 与 bundle,以及此前失败的 no-AK capability 精确集成测试。真实飞书和钉钉传输验证见上文;Telegram 和 WeCom 有自动化测试覆盖,但未使用真实平台凭据执行 E2E。
风险与范围
关联 Issue
Closes #7103
后续优化:#7154