@modesto_ma/dsh-channels 0.5.0-beta.5 → 0.5.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,9 +11,10 @@ DeepSeek Harness(DSH)飞书渠道插件:**一个 dsh 实例对应一个飞
11
11
  - **知识库读写**:开启 `feishuTools.wikiRead` / `feishuTools.wikiWrite`(默认都关)并在 `wikiRoots` 里配置知识空间链接 `https://<租户>.feishu.cn/wiki/space/<space_id>` 或节点链接 `https://<租户>.feishu.cn/wiki/<node_token>` 后,模型可用 `feishu_wiki_list` 浏览、`feishu_wiki_read` 读取飞书文档(Markdown,或带 block id 的飞书文档 XML,支持目录/章节/区间/关键词局部读取)、`feishu_wiki_create` 在范围内新建飞书文档、电子表格或多维表格(新节点也可作为子目录继续往下建)、`feishu_wiki_edit` 修改**本应用创建的**文档(追加、在块后插入、替换/删除块、文本替换、整篇重写;必须带上读取时的 `revision_id`,文档被他人改过会被拒绝)。电子表格用 `feishu_wiki_sheet_read` 按子表和 A1 范围读成 CSV(每行带 `[row=N]` 真实行号),`feishu_wiki_sheet_edit` 向**本应用创建的**表格写入单元格值或公式、在数据末尾追加行(追加位置会越过空行找到真正的最后一行,目标单元格非空时失败、不会覆盖;同样校验 `revision_id`;不改样式、图表和行列结构)。多维表格用 `feishu_wiki_base_read` 列数据表、读字段和记录(支持筛选、排序、关键词搜索、分页),`feishu_wiki_base_edit` 增改删记录:**本应用创建的**多维表格可增改删,其他多维表格**只能新增记录**(不改表和字段结构)。所有调用都用应用身份,并且:只有**管理员或知识库白名单**(`/auth wiki add @用户`)里的人发起的话题能用;`wikiRoots` 之外一律拒绝;打开某个文档或表格、每次新建、每次修改都会在话题里弹出**授权卡**,只有话题发起人能点(读取授权在本话题内对该文档持续有效,`/renew` 或重启后重新询问),超时(`approvalTimeoutMs`)自动拒绝;浏览目录不弹卡。需开通 `wiki:wiki`、`docx:document`(只读可用 `wiki:wiki:readonly` / `docx:document:readonly`),电子表格和多维表格另需细分权限 `sheets:spreadsheet:read` / `sheets:spreadsheet:write_only`、`base:table:read` / `base:field:read` / `base:record:read` / `base:record:create|update|delete`(旧版 `sheets:spreadsheet` / `bitable:app` 不生效),且应用要是目标知识空间的成员(可把包含机器人的群加为空间成员/管理员)
12
12
  - **HITL 审批**:当模型请求需要授权的动作(如 bash 沙箱升级 `sandbox_permissions`)时,插件监听主机 `approval/request` 事件,在同一话题里**新发一张独立审批决策卡**:卡上直接显示将执行的命令(或工具参数)和提权目标,提供 ✅ 允许 / ❌ 拒绝 / ♾️ 本话题不再询问 三个按钮;也可以在话题里直接回复「允许」「拒绝」「全部允许」,回复其他内容视为拒绝并作为下一条指令。等待期间流式主卡显示「⏳ 等待你审批」。超时(`approvalTimeoutMs`,默认 5 分钟)自动拒绝(fail-closed),非会话发起人点击或回复无效
13
13
  - **结构化提问**:模型调用 `ask_user_question` 时,插件监听主机 `user-questions/request` 瀑布并发一张问答卡(每个问题一组选项按钮,多选先勾选再「提交本题选择」),答案回填给模型;主机自带的应答者只有 Web 客户端,没有本插件时纯飞书会话会以 `NO_PROVIDER` 失败。超时(`questionTimeoutMs`,默认 5 分钟)或回合取消时本轮提问失败(fail-closed);没有可选项的问题直接提示用文字作答,**阻塞期间普通文本回复即作为该问题的自由文本答案**(同时支持多选后的自定义文本)
14
- - **准入与授权(默认开启)**:**管理员不受白名单限制**(私聊、任意群聊——包括尚未授权的群——都能直接发起会话);其他人私聊只收到一条固定提示(`/userid` 例外),群聊需要「群已授权 + 用户在全局白名单」两层同时命中。机器人被拉进群后私聊管理员发**群信息卡**(未授权群附授权按钮,用于放行白名单用户);已授权群里未授权用户 @ 会触发用户授权卡。授权入口:飞书 `/auth` 命令、授权卡、Web 设置面板;管理员 user_id 在面板配置(私聊 `/userid` 获取),**留空时自动使用飞书应用所有者**
14
+ - **准入与授权(默认开启)**:**管理员不受白名单限制**(私聊、任意群聊——包括尚未授权的群——都能直接发起会话);其他人私聊只收到一条固定提示(`/userid` 例外;打开 `dmAllowWhitelist` 后用户白名单里的人也可以私聊),群聊需要「群已授权 + 用户在全局白名单」两层同时命中。机器人被拉进群后私聊管理员发**群信息卡**(未授权群附授权按钮,用于放行白名单用户);已授权群里未授权用户 @ 会触发用户授权卡。授权入口:飞书 `/auth` 命令、授权卡、Web 设置面板;管理员 user_id 在面板配置(私聊 `/userid` 获取),**留空时自动使用飞书应用所有者**
15
15
  - 全部基于飞书官方 SDK [`@larksuiteoapi/node-sdk`](https://www.npmjs.com/package/@larksuiteoapi/node-sdk)(WebSocket 长连接,免公网)
16
- - **Web 设置面板**:Web/桌面端 **设置 → 飞书渠道**,顶部状态头 + 7 个标签页(连接 / 会话 / 模型 / 附件与工具 / 访问授权 / 知识库 / 高级);凭据不完整时插件不连接、面板显示三步引导,填写保存后自动连接(保存写入本机覆盖文件,部分字段需重连生效);详见下文「Web 设置面板」章节
16
+ - **Web 设置面板**:Web/桌面端左侧栏 **飞书渠道** 入口(主区整页),顶部状态头 + 7 个标签页(连接 / 会话 / 模型 / 附件与工具 / 访问授权 / 知识库 / 高级);凭据不完整时插件不连接、面板显示三步引导,填写保存后自动连接(保存写入本机覆盖文件,部分字段需重连生效);详见下文「Web 设置面板」章节
17
+ - **对话里的飞书来源**:从飞书发起的会话在 Web/桌面端对话标题旁多一个来源按钮(群名或「飞书私聊」),点开在右侧栏「飞书来源」标签页查看群聊/私聊、话题、发起人、开始与最近活跃时间,并可跳回飞书;其他会话不显示;详见下文「对话里的飞书来源」章节
17
18
 
18
19
  设计与可行性论证见 [docs/feasibility-and-design.md](docs/feasibility-and-design.md);工作区粒度与会话隔离的取舍见 [docs/workspace-isolation-design.md](docs/workspace-isolation-design.md);入站附件管线(存储 / 清单 / 工具 / 引用与历史 / 降级 / 隐私边界)见 [docs/attachment-pipeline-v2.md](docs/attachment-pipeline-v2.md)。
19
20
 
@@ -39,6 +40,8 @@ DeepSeek Harness(DSH)飞书渠道插件:**一个 dsh 实例对应一个飞
39
40
 
40
41
  `/new` 是 `/renew` 的别名。`/help` 由命令注册表按分组生成,新增命令只需在注册表里加一行。
41
42
 
43
+ 不在上面命令表中的 `/` 开头消息(如 `/guanxiang …`)不由插件处理,而是和普通消息一样经过准入后作为 prompt 交给 DSH,由 DSH 展开对应的用户可调用 skill;没有同名 skill 时按普通文本处理。
44
+
42
45
  **飞书原生斜杠指令面板**(可选,`slashCommandPanel: true` 开启,默认关闭;部分企业不允许应用添加斜杠指令):开启后每次连上飞书,插件在后台调用 `application/v7/app_slash_commands` 把命令注册表同步到飞书原生面板,用户在聊天框输入 `/` 即可直接选择命令(飞书客户端有约 5 分钟缓存)。同步是全量对齐且幂等:缺的创建、描述变了的原地更新、注册表里已没有的或重复的删除(别名不单独列出),注册表不变时再次连接不产生任何写操作;超时(30 秒)或断开后不再发起新的写操作。需要可选权限 `application:app_slash_command:read`(读取现有命令)与 `application:app_slash_command:write`(增删改),两项只在开启时列入权限自检。关闭时插件不读写面板。同步失败只影响面板列表,直接输入命令照常可用;失败原因显示在 Web 设置「连接」页并打印一条 warn。
43
46
 
44
47
  ## 流式卡片
@@ -59,9 +62,9 @@ DeepSeek Harness(DSH)飞书渠道插件:**一个 dsh 实例对应一个飞
59
62
  - 第二行:`📊 上下文 {tokens}/{window} ({占比}) · 📩 输入 {n} · 📨 输出 {n} · ⚡ 缓存命中 {pct}`。占比 = 上下文 tokens / 窗口(<10% 保留一位小数,≥10% 取整);没有窗口时只显示 tokens
60
63
  - 输入/输出为 session 累计口径;模型、推理强度、缓存命中按**当前轮次**,本轮在请求头产生前失败时模型/缓存命中显示 `—`、推理强度省略,不会沿用上一轮
61
64
 
62
- ## Web 设置面板(设置 → 飞书渠道)
65
+ ## Web 设置面板(左侧栏 → 飞书渠道)
63
66
 
64
- 包内含可选的浏览器半区(`lib/client.js`,DSH client module):在 Web/桌面端 **设置** 导航注册「飞书渠道」分区。宿主没有 `webServer` 服务(headless profile)时不加载面板,飞书通道本身不受影响。
67
+ 包内含可选的浏览器半区(`lib/client.js`,DSH client module):在 Web/桌面端左侧栏注册「飞书渠道」全局入口(`sidebar.panellist`),点击后在主区打开整页面板(`main` 槽)。宿主没有 `webServer` 服务(headless profile)时不加载面板,飞书通道本身不受影响。
65
68
 
66
69
  **连接时机**:启用插件时**不会无条件连接飞书**。插件启动先读取 profile 配置与 Web 覆盖,再检查 `appId` / `appSecret` 两个凭据引用能否解析出值:
67
70
 
@@ -77,7 +80,7 @@ DeepSeek Harness(DSH)飞书渠道插件:**一个 dsh 实例对应一个飞
77
80
  | 会话 | 「触发」:私聊、普通群需要 @、话题被拒时降级为会话级(均为开关);「工作区」:工作区根目录、话题工作目录隔离方式(`topicIsolation`);「话题与卡片」:话题过期(`routeTtlMs`,按天)、审批 / 提问卡片等待(`approvalTimeoutMs` / `questionTimeoutMs`,按秒)、流式刷新与状态行保活间隔;「飞书侧功能」:首次私聊引导、斜杠指令面板同步、权限切换 |
78
81
  | 模型 | 服务商、模型、推理档位、单次输出上限(`maxTokens`,留空使用模型默认)、Agent 预设 |
79
82
  | 附件与工具 | 「入站附件」:接收图片和文件、使用 DSH 原生附件库(开关,关闭则存到话题目录 `attachments/`)、图片预取、首轮话题历史;「工具」:`feishu_attachment_fetch` / `feishu_topic_messages` / `feishu_send_file` 开关;「限额与超时」:预取上限、读取附件上限与超时、读取话题消息超时、引用摘录与历史超时(共 8 项,默认收起,有改动或错误时自动展开) |
80
- | 访问授权 | 待处理申请(置顶,一键授权 / 忽略)、调试模式(`accessMode`,开关)、管理员 user_id、申请有效期;已授权群 / 用户白名单(添加表单默认收起) |
83
+ | 访问授权 | 待处理申请(置顶,一键授权 / 忽略)、调试模式(`accessMode`,开关)、白名单用户可私聊(`dmAllowWhitelist`,开关)、管理员 user_id、申请有效期;已授权群 / 用户白名单(添加表单默认收起) |
81
84
  | 知识库 | 知识库读 / 写开关(`feishuTools.wikiRead` / `feishuTools.wikiWrite`)、可访问范围 `wikiRoots`(每条链接一个输入框,逐条校验);「可使用的人」:知识库白名单增删(与用户白名单独立,等同 `/auth wiki add|remove`) |
82
85
  | 高级 | 日志分析接口(监听地址 `analyzeListen` 与访问令牌)、SDK 日志级别、Web 覆盖文件路径与「恢复 profile 配置」 |
83
86
 
@@ -103,7 +106,7 @@ DeepSeek Harness(DSH)飞书渠道插件:**一个 dsh 实例对应一个飞
103
106
 
104
107
  **可视化选择工作区根目录**:输入框旁的「选择目录…」按钮会调用**宿主机的系统目录选择框**(macOS 的 NSOpenPanel / Windows 的文件夹对话框),选中后把绝对路径填进草稿,仍需点「保存更改」才落盘。该能力转发自宿主可选的 `ctx.directoryPicker` 服务:桌面端/本机 GUI 会话(native 后端)提供按钮;headless、远程或 SSH 会话没有可服务的显示会话(browse 后端),面板不显示按钮、保留手写输入,插件在缺少该服务的旧宿主上照常加载。选择框打开期间请求保持挂起;取消是正常结果,不会报错。
105
108
 
106
- **访问授权**:「访问授权」标签页集中管理准入:管理员 user_id(私聊 `/userid` 获取)、调试模式、授权申请有效期;「已授权群」「已授权用户」列表支持增删(群 ID 可从入群群信息卡或 `/groupid` 获得,用户 user_id 可用 `/userid` 查看);「待处理申请」可一键授权/忽略。白名单只约束非管理员:管理员在任何私聊/群聊都直接放行。管理员留空时自动使用飞书应用所有者(连接时通过「获取应用信息」接口查询),管理员一行的说明会显示“已自动使用应用所有者”;`guarded` 且既未配置管理员、也查不到应用所有者时,标签页顶部会红字提示“当前会拒绝所有对话”并附上查询失败原因。授权白名单与申请记录保存在插件 `storageDomain`(`channels_routes_v2` 域),重启后保留;未建立飞书连接时白名单尚未加载,标签页会提示「连接建立后加载授权列表」并禁用写操作。
109
+ **访问授权**:「访问授权」标签页集中管理准入:管理员 user_id(私聊 `/userid` 获取)、调试模式、白名单用户可私聊、授权申请有效期;「已授权群」「已授权用户」列表支持增删(群 ID 可从入群群信息卡或 `/groupid` 获得,用户 user_id 可用 `/userid` 查看);「待处理申请」可一键授权/忽略。白名单只约束非管理员:管理员在任何私聊/群聊都直接放行。管理员留空时自动使用飞书应用所有者(连接时通过「获取应用信息」接口查询),管理员设置下方的「当前管理员」一行会注明“应用所有者”;`guarded` 且既未配置管理员、也查不到应用所有者时,标签页顶部会红字提示“当前会拒绝所有对话”并附上查询失败原因。管理员、群和用户白名单、待处理申请、知识库白名单在每次进入页面时实时读取飞书头像和名字(`GET /dsh-channels/profiles?users=…&chats=…`,每类最多 200 个,Host 侧缓存 30 秒):用户需要 `contact:user.base:readonly` 且在应用的通讯录权限范围内,群需要 `im:chat:readonly`;读不到时显示授权时记下的名字或 ID,并在标签页顶部注明失败原因。授权白名单与申请记录保存在插件 `storageDomain`(`channels_routes_v2` 域),重启后保留;未建立飞书连接时白名单尚未加载,标签页会提示「连接建立后加载授权列表」并禁用写操作。
107
110
 
108
111
  **配置层级**:`configSchema` 默认值 < profile `cordis.patch.yml` 的 `config` < Web 面板覆盖。面板只把可编辑字段写入 `$DSH_HOME/dsh-channels/settings.json`;「高级 → 恢复 profile 配置」确认后删除该文件并回落到 profile 配置。`baseDir` 与 `topicIsolation` 在「会话 → 工作区」编辑(只影响之后新建的话题);话题过期、审批 / 提问卡片等待与 `maxTokens` 保存后立即生效,不需要重连。凭据**值**在「连接 → 飞书应用凭据」编辑,凭据**引用名**(`appId` / `appSecret` 配置项)仍由 profile 配置决定。已移除的目录清理配置 `workspaceRetention` / `workspaceRetentionAuto` 与覆盖文件路径配置 `settingsFile` 若仍留在 profile 或覆盖文件中会被静默忽略,面板下次保存时从覆盖文件中去掉。
109
112
 
@@ -119,16 +122,28 @@ pnpm verify:host # 以纯 ESM 导入 Host bundle,检查 Cordis 插件面
119
122
  pnpm verify:client # 用 __ModuleLoader__ shim 做 loader 冒烟
120
123
  ```
121
124
 
125
+ ## 对话里的飞书来源
126
+
127
+ 从飞书发起的会话(话题、会话级降级、`/renew` 新开的会话、日志平台分析话题)在 Web/桌面端对话标题旁会出现一个来源按钮:群聊显示群名,私聊显示「私聊 · 对方姓名」(读不到姓名时为「飞书私聊」)。点击后在右侧栏打开「飞书来源」标签页,内容包括:
128
+
129
+ - **标题**:群聊为群头像和群名,私聊为对方头像和姓名;
130
+ - **来源**:私聊 / 群聊 / 群聊 · 话题;群名读不到时显示群 ID;
131
+ - **发起人**:头像、姓名 + `user_id`(日志平台分析话题显示请求人,并注明群内成员都可以继续);
132
+ - **开始于 / 最近活跃**:飞书侧已经换到新会话(`/renew`、`/new`)或路由过期后,「最近活跃」换成「状态:飞书侧已切换到新会话或已过期」;
133
+ - **在飞书中打开**(飞书 applink 打开对应聊天)与「刷新」。
134
+
135
+ Web 里直接新建的会话不显示按钮。来源在 Host 侧按会话 id 持久保存(存储表 `session_origins`,路由过期后仍可查;升级前已有的路由在启动时补写)。群名、群头像、姓名与头像在每次打开「飞书来源」标签页时实时读取(Host 侧缓存 30 秒,标题旁按钮与标签页同时打开只查一次):群用 `im:chat:readonly`,用户优先用通讯录 `contact:user.base:readonly`,缺失时群聊从群成员列表查姓名(没有头像),私聊只显示 `user_id`。接口为 `GET /dsh-channels/session-origin?sessionId=…`,与设置面板一样需要 DSH 登录。右侧栏不可用的宿主(如 headless)不注册这部分。
136
+
122
137
  ## 发布
123
138
 
124
- 版本号由 semantic-release 按 Conventional Commits 计算,**只在手动触发时发布**;推送到 `main` / `dev` 只跑检查,可以先累积多笔提交。
139
+ 版本号由 semantic-release 按 Conventional Commits 计算。推送(含合并 PR)到 `main` 在检查通过后**自动发布**正式版;推送到 `dev` 只跑检查,beta 版**只在手动触发时发布**,可以先累积多笔提交。
125
140
 
126
141
  | 分支 | 版本 | npm dist-tag |
127
142
  | --- | --- | --- |
128
143
  | `main` | 正式版,如 `0.5.0` | `latest` |
129
144
  | `dev` | 预发布版,如 `0.5.0-beta.1` | `beta` |
130
145
 
131
- 在 GitHub Actions 的 `pipeline` workflow 点击 **Run workflow**,选择 `main` 或 `dev`:`dry_run` 默认勾选,只预览下一个版本号;取消勾选才真正发布 npm 包、打 tag 并创建 GitHub Release。安装 beta 版用 `@modesto_ma/dsh-channels@beta`。
146
+ 手动发布:在 GitHub Actions 的 `pipeline` workflow 点击 **Run workflow**,选择 `dev`(或需要补发时选 `main`):`dry_run` 默认勾选,只预览下一个版本号;取消勾选才真正发布 npm 包、打 tag 并创建 GitHub Release。安装 beta 版用 `@modesto_ma/dsh-channels@beta`。
132
147
 
133
148
  ## 目录结构
134
149
 
@@ -188,8 +203,10 @@ src/
188
203
  ├── health.ts # 健康快照与卡片
189
204
  ├── session-export.ts # 逻辑日志导出
190
205
  ├── feishu-channel.ts # 官方 SDK Channel 适配(可注入 stub)
191
- ├── client/ # 浏览器半区:设置 →「飞书渠道」页面(构建到 lib/client.js)
192
- │ ├── index.ts # client 插件入口:settings.section 注册 + 样式
206
+ ├── client/ # 浏览器半区:左侧栏「飞书渠道」主区页面(构建到 lib/client.js)
207
+ │ ├── index.ts # client 插件入口:sidebar.panellist + main 注册、飞书来源(右侧栏标签 + 会话头部入口)+ 样式
208
+ │ ├── origin.tsx # 对话里的飞书来源:会话头部按钮与右侧栏标签页
209
+ │ ├── origin-cache.ts # 按会话缓存的来源查询
193
210
  │ ├── panel/ # 页面外壳 + 七个标签页
194
211
  │ │ ├── index.tsx # 加载 / 5s 状态轮询 / 全局草稿 / catalog 级联 / 顶部状态头 / Tab
195
212
  │ │ ├── shared.tsx # 行 / 开关 / 分组 / 保存栏等渲染原语、阶段徽标、草稿校验
@@ -282,6 +299,7 @@ npm 发行包已包含构建产物:Host 入口是 `lib/index.js`(profile 直
282
299
  slashCommandPanel: false # 连接时把命令同步到飞书原生 / 面板(需 app_slash_command 读写权限;改后需重连)
283
300
  allowPermissionSwitch: false # 是否允许 /permissions <name> 从飞书侧切换权限预设(安全敏感)
284
301
  accessMode: 'guarded' # 准入模式:guarded(默认,仅管理员+白名单)| open(旧行为,不安全)
302
+ dmAllowWhitelist: false # guarded 下用户白名单里的人也可私聊(默认关:私聊仅管理员)
285
303
  adminUserId: 'a1b2c3d4' # 管理员 user_id(可留空,留空时自动使用应用所有者;都没有时 guarded 下拒绝所有对话;面板可改)
286
304
  authRequestTtlDays: 7 # 入群/用户授权申请卡有效期(天,1–30)
287
305
  analyzeListen: '0.0.0.0:3090' # 日志平台分析接口的独立监听地址(不填 = 关闭;改后需重连)
@@ -302,13 +320,13 @@ FEISHU_APP_ID: cli_xxxxxxxx
302
320
  FEISHU_APP_SECRET: xxxxxxxx
303
321
  ```
304
322
 
305
- 也可以不手改文件:安装并在 Web 面板里打开 **设置 → 飞书渠道**,在「飞书应用凭据」卡片填入 App ID / App Secret 保存即可(值同样写进该凭据库)。
323
+ 也可以不手改文件:安装并在 Web 左侧栏打开 **飞书渠道**,在「飞书应用凭据」卡片填入 App ID / App Secret 保存即可(值同样写进该凭据库)。
306
324
 
307
325
  文件必须仅属主可读(dsh 强制校验,否则拒绝启动):`chmod 600 $DSH_HOME/.credentials.yaml`。解析优先级:进程环境变量 > `.credentials.yaml` > `<cwd>/.env` > `$DSH_HOME/.env`(由 `dsh-credentials-local` 提供,热加载)。配置里只写引用名,明文密钥会被 schema 拒绝。改动凭据后:Web 面板保存会即时写入,未连接时自动建立连接、已连接时提示重连;手改 `.credentials.yaml` 由凭据层热加载,但正在运行的连接不会自动使用新值,需在面板点「重启飞书连接」。
308
326
 
309
327
  验证组合(不启动服务):`DSH_HOME=$DSH_HOME pnpm dsh web --dump-config | grep dsh-channels`
310
328
 
311
- 启动成功标志(控制台):`[dsh-channels] feishu long connection established, bot open_id: ou_xxx`;凭据不完整时不会连接,日志会提示缺少的凭据引用(如 `飞书应用凭据未配置完整(缺少 FEISHU_APP_SECRET…)`),打开 Web **设置 → 飞书渠道 → 连接** 补齐即可。
329
+ 启动成功标志(控制台):`[dsh-channels] feishu long connection established, bot open_id: ou_xxx`;凭据不完整时不会连接,日志会提示缺少的凭据引用(如 `飞书应用凭据未配置完整(缺少 FEISHU_APP_SECRET…)`),打开 Web 左侧栏 **飞书渠道 → 连接** 补齐即可。
312
330
 
313
331
  宿主 profile 已内置所需服务(agent-loop、llm-deepseek、session 持久化、storage-domain、workspace、working-directory);web profile 默认组合还包含 DSH 原生附件库 `dsh-attachment-local` 与 `dsh-tools`(`ctx.attachments` / `ctx.tools`)。缺少原生附件库时入站附件自动回退 archive 后端(见「行为与降级」)。推荐确认 `dsh-token-meter` 已启用(卡片上下文占用显示)。Git worktree 隔离模式额外依赖可选的 `@deepseek-ai/dsh-experimental-worktree` 实验插件,缺失时自动回退为普通话题子目录。
314
332
 
@@ -428,7 +446,7 @@ Content-Type: application/json
428
446
  - **重启修复(流式中断)**:每条正在流式的 turn 会把自己的修复凭据(trigger 消息、Typing reaction id、全部段卡 id)写入 `channels_routes_v2` 域的 `inflight` 表(键 = 主卡 message id,turn 结束即删)。重启后长连接一建立,插件就把上一实例留下的中途卡片 patch 成「⚠️ 服务已重启,本次回复中断,请重新发送消息。」终态并补 DONE 表情;patch 失败重试一次(上限 2 次)后放弃。持久化盘点与缺口清单见 [docs/persistence.md](docs/persistence.md)。
429
447
  - **消息去重(WS 重投幂等)**:飞书长连接重连会重投未 ack 的事件;每条消息在任何副作用之前先落一条 `processed` 记录(键 = message id,飞书全局唯一),重投消息被直接丢弃——同进程靠内存守卫、跨重启靠持久化记录,每条消息至多处理一次。记录保留 24 小时后由 GC 清扫。
430
448
  - **路由表 GC 与过期话题拒绝**:超过 `routeTtlMs`(默认 30 天)无活动的真实话题路由会先写入最小持久化墓碑,再删除活动路由;此后该话题中的普通消息和 Session 命令会收到「话题已过期」提示,插件不会恢复旧 Session,也不会在可见历史仍属于旧 Session 的话题里新建 Session(`/help`、`/userid`、`/groupid`、`/health` 仍可用)。墓碑跨重启保留,仅含 thread/chat/owner/工作区路径/会话目录/最后活动时间;旧 DSH Session、日志与话题目录都不删除。降级的 chat-scoped 路由没有独立话题边界,仍直接清理。陈年 in-flight 快照同时在启动时及每 6 小时清扫。`routeTtlMs: 0` 关闭后续路由过期处理,已产生的墓碑仍保持拒绝语义。
431
- - **准入与授权**:`accessMode: 'guarded'`(默认)下,入站先过频道准入:**管理员不受任何白名单限制**,私聊/任意群聊(含未授权群)都可直接开会话;**非管理员**私聊回固定提示(`/userid` 例外),群聊要求 **群在白名单** 且 **user_id 在全局用户白名单**,两者缺一不可。未授权消息不建 Session、不建话题目录、不落附件、不调模型;群里静默、私聊回固定文案。机器人被拉进任意群时(订阅 `im.chat.member.bot.added_v1`),插件私聊管理员发群信息卡(群名/`chat_id`/成员数/邀请人),未授权群附「授权本群 / 忽略」按钮;已授权群里未授权用户 @ 机器人时,管理员会收到用户授权申请卡(含昵称/user_id)。授权状态持久化在 `channels_routes_v2` 域的 `access_groups` / `access_users` / `access_requests` 表,撤销立即生效。管理员 user_id 在 Web 面板配置,留空时为飞书应用所有者(连接时查询,需可选的「获取应用信息」权限;查询失败则 fail closed,手动填写管理员即可);`/auth` 命令、授权卡、面板三种入口等价。
449
+ - **准入与授权**:`accessMode: 'guarded'`(默认)下,入站先过频道准入:**管理员不受任何白名单限制**,私聊/任意群聊(含未授权群)都可直接开会话;**非管理员**私聊回固定提示(`/userid` 例外;`dmAllowWhitelist: true` 时全局用户白名单里的人可私聊,命令与群聊相同,`/auth` 仍仅管理员),群聊要求 **群在白名单** 且 **user_id 在全局用户白名单**,两者缺一不可。未授权消息不建 Session、不建话题目录、不落附件、不调模型;群里静默、私聊回固定文案。机器人被拉进任意群时(订阅 `im.chat.member.bot.added_v1`),插件私聊管理员发群信息卡(群名/`chat_id`/成员数/邀请人),未授权群附「授权本群 / 忽略」按钮;已授权群里未授权用户 @ 机器人时,管理员会收到用户授权申请卡(含昵称/user_id)。授权状态持久化在 `channels_routes_v2` 域的 `access_groups` / `access_users` / `access_requests` 表,撤销立即生效。管理员 user_id 在 Web 面板配置,留空时为飞书应用所有者(连接时查询,需可选的「获取应用信息」权限;查询失败则 fail closed,手动填写管理员即可);`/auth` 命令、授权卡、面板三种入口等价。
432
450
  - **HITL 审批**:插件以 `prepend: true` 注册为宿主 `approval/request` 瀑布的应答者——本频道路由的 session 的审批请求**优先于** Web 网关的通用应答(其面板对飞书用户不可见),非本频道的请求原样 `next()` 交给其他应答者。审批卡是**独立消息**(锚定到该 session 最新流式卡、同话题),绝不嵌入流式卡(其全量替换 patch 会冲掉按钮)。宿主请求不带工具参数,插件在 `session/event` 中按会话记录**当前回合**的 `tool/call`(回合结束即清空,不回读历史),按 `callId` 取出命令显示在卡上(超过 600 字截断并注明)。决策仅在会话发起人操作时生效:允许 → `allowed-once`;本话题不再询问 → `allowed-once`,并在内存里记下「工具名 + 提权目标」,该 session 之后的同类请求直接 `allowed-once`、不再发卡,同 session 里其他被覆盖的待审批卡一并允许(`/renew` 换 session 或重启后失效,提升到 `danger-full-access` 时按钮标红);拒绝 → `rejected`;文字回复中「允许 / 同意 / 好 / y」等、「拒绝 / 不行 / n」等、「全部允许」等关键词作用于该 session 最新一张待审批卡,其他文字把该 session 所有待审批卡按 `rejected` 结算后照常作为下一条消息交给 Agent;超时 → `rejected`(fail-closed,卡面显示「已超时」)、请求被撤回(abort)→ `cancelled`、卡片发送失败 → `unavailable`(fail-closed);每次决策后卡片更新为终态(注明按钮 / 文字回复 / 不再询问 / 被新消息取代)并移除按钮。审批记录(`approval/asked`/`decided`)由宿主会话日志负责,插件不做持久化——重启后遗留问题随 turn 消亡,与 Web 网关一致。需要飞书应用订阅 **card.action.trigger** 回调(见「飞书侧准备」);启动日志 `HITL 审批应答已就绪` 表示生效,`主机未挂载 approval 服务` 表示该部署未启用 HITL。
433
451
  - **终态与表情**:成功轮的面板标题变为「回复结束」(无推理内容时不渲染面板),并把 `Typing` 换成 `DONE`;`turn/end.kind = error` 时面板标题为「回复失败」(同样仅当有推理内容),错误只在正文出现一次(`❌ **本轮运行失败**` + 结构化错误消息与错误码),并把 `Typing` 换成 `No`。模型/推理强度/本轮缓存样本不会继承上一轮。
434
452
  - **限频**:卡片更新单条消息 5 QPS、应用级 1000 次/分钟;500ms(2 QPS)默认安全,`refreshMs` 下限 200ms;活动状态行的保活心跳默认 5s(`statusRefreshMs` 下限 1s),远低于限频。
@@ -214,7 +214,7 @@ native 后端下,准入通过后由 `inboundAttachmentState` 做**不下载的
214
214
 
215
215
  ## 11. 配置项全景
216
216
 
217
- 下表为 v2 相关配置(默认值 / 边界以 [src/config.ts](../src/config.ts) 为准)。Web 面板「设置 → 飞书渠道 → 附件与工具」把它们分为「入站附件」(`FieldGroup = attachments`)、「工具」(`tools`)与默认收起的「限额与超时」(`limits`)三组;`feishuTools.*` 在覆盖层中仍写成嵌套对象,保存时只合并被修改的子键。
217
+ 下表为 v2 相关配置(默认值 / 边界以 [src/config.ts](../src/config.ts) 为准)。Web 面板「左侧栏 → 飞书渠道 → 附件与工具」把它们分为「入站附件」(`FieldGroup = attachments`)、「工具」(`tools`)与默认收起的「限额与超时」(`limits`)三组;`feishuTools.*` 在覆盖层中仍写成嵌套对象,保存时只合并被修改的子键。
218
218
 
219
219
  | 配置项 | 默认 | 边界 / 说明 | 面板分组 |
220
220
  | --- | --- | --- | --- |
@@ -26,7 +26,8 @@
26
26
  | `im:resource`(获取与上传图片或文件资源,**可选**;`im:resource:upload` 亦可) | 模型用 `feishu_send_file` 把生成的图片/文件发给用户;不开通时该工具返回可读错误 |
27
27
  | `im:message:update`(更新消息卡片) | 流式卡片刷新(patch) |
28
28
  | `wiki:wiki`、`docx:document`(只读可用对应的 `:readonly` 权限),电子表格 `sheets:spreadsheet:read` / `sheets:spreadsheet:write_only`,多维表格 `base:table:read`、`base:field:read`、`base:record:read` / `base:record:create`、`base:record:update`、`base:record:delete`(**可选**;旧版 `sheets:spreadsheet` / `bitable:app` 对新版接口不生效) | 知识库工具(`feishuTools.wikiRead` / `wikiWrite` + `wikiRoots`):浏览知识库,读取/新建/修改飞书文档、电子表格、多维表格;应用还需是目标知识空间的成员 |
29
- | `im:chat:readonly` 一类(获取群信息) | 入群通知卡展示群名/成员数(具体 scope 以控制台要求为准) |
29
+ | `im:chat:readonly` 一类(获取群信息) | 入群通知卡展示群名/成员数;Web 设置页和对话「飞书来源」显示群名、群头像,并在缺少通讯录权限时从群成员列表读取发起人姓名(具体 scope 以控制台要求为准) |
30
+ | `contact:user.base:readonly`(获取用户基本信息,**可选**) | Web 设置页(管理员、白名单、待处理申请、知识库用户)和对话「飞书来源」显示用户真实姓名与头像;还要求用户在应用的「通讯录权限范围」内(最简单是选全部成员)。缺失时显示授权时记下的名字或 user_id,「飞书来源」的群聊发起人退回群成员列表取名字(没有头像) |
30
31
  | 「获取应用信息」一类(如 `admin:app.info:readonly` / `application:application:self_manage`,**可选**,以控制台要求为准) | 未配置 `adminUserId` 时连接后自动把应用所有者设为管理员;不便申请时跳过即可,按 3.3 手动设置管理员 |
31
32
  | `application:app_slash_command:read` 与 `application:app_slash_command:write`(读取/管理应用斜杠指令,可选,仅 `slashCommandPanel: true` 时需要) | 连接后把插件命令同步到飞书原生斜杠指令面板(输入 `/` 时列出,客户端约 5 分钟后刷新);缺失时只是面板没有列表,直接输入命令不受影响 |
32
33
 
@@ -174,14 +175,14 @@ chmod 600 $DSH_HOME/.credentials.yaml
174
175
  | `feishuTools.topicMessagesTimeoutMs` | `20000` | 单次 `feishu_topic_messages` 整段超时(1000–120000 ms) |
175
176
  | `loggerLevel` | `info` | 官方 SDK 日志级别 |
176
177
 
177
- > **面板与配置位置**:v2 附件配置在 Web 面板「设置 → 飞书渠道 → 附件与工具」:`acceptAttachments` / `attachmentStore` / `prefetchImages` / `historyBootstrapLimit` 在「入站附件」组,`feishuTools.*` 开关在「工具」组,各项字节上限与超时在「限额与超时」组;保存写入同一 Web 覆盖文件,`feishuTools.*` 按子键合并(不会把未修改子键写空)。直接写 profile 补丁层同样生效,面板覆盖优先。其中 `feishuTools.topicMessages=false` 只关闭模型工具,被动的引用/历史 prompt 摘录仍生效;要完全关闭首轮历史请设 `historyBootstrapLimit: 0`。archive 后端下 `feishu_attachment_fetch` 不挂载,`feishu_topic_messages` 仍可读取历史文本,但工具描述会提示附件不可取。
178
+ > **面板与配置位置**:v2 附件配置在 Web 面板「左侧栏 → 飞书渠道 → 附件与工具」:`acceptAttachments` / `attachmentStore` / `prefetchImages` / `historyBootstrapLimit` 在「入站附件」组,`feishuTools.*` 开关在「工具」组,各项字节上限与超时在「限额与超时」组;保存写入同一 Web 覆盖文件,`feishuTools.*` 按子键合并(不会把未修改子键写空)。直接写 profile 补丁层同样生效,面板覆盖优先。其中 `feishuTools.topicMessages=false` 只关闭模型工具,被动的引用/历史 prompt 摘录仍生效;要完全关闭首轮历史请设 `historyBootstrapLimit: 0`。archive 后端下 `feishu_attachment_fetch` 不挂载,`feishu_topic_messages` 仍可读取历史文本,但工具描述会提示附件不可取。
178
179
 
179
180
  ### 3.3 初始化管理员与授权(默认准入)
180
181
 
181
182
  插件默认 `accessMode: 'guarded'`:**没有管理员时,所有对话都会被拒绝**(这是 fail-closed 的安全默认值)。如果应用开通了可选的「获取应用信息」权限,留空 `adminUserId` 时连接后会自动把应用所有者设为管理员(Web「访问授权」页显示“已自动使用应用所有者”),可以跳过下面第 1–2 步;没开通或查询失败时按以下顺序手动设置:
182
183
 
183
184
  1. 启动 dsh 后在飞书**私聊机器人**发送 `/userid`(未配置管理员时任何私聊用户都可以用,只回固定文本,不建会话)→ 复制返回的 user_id;
184
- 2. 打开 Web **设置 → 飞书渠道 → 访问授权**,把它填进「管理员 user_id」并保存(立即生效);也可以直接写 profile 的 `adminUserId`;
185
+ 2. 打开 Web 左侧栏 **飞书渠道 → 访问授权**,把它填进「管理员 user_id」并保存(立即生效);也可以直接写 profile 的 `adminUserId`;
185
186
  3. 现在管理员私聊机器人即可正常对话。**其他用户私聊会收到一条固定提示**(“非管理员禁用私聊功能哦…”),不会建会话;
186
187
  4. **给群授权**(三种入口任选;目的是放行**非管理员**,管理员本人在任何群都能直接对话):
187
188
  - 机器人被拉进群 → 管理员私聊收到**群信息卡**(群名/`chat_id`/成员数/邀请人)→ 点「授权本群」;
@@ -233,7 +234,7 @@ DSH_HOME=$DSH_HOME pnpm dsh web # 默认 http://127.0.0.1:3080
233
234
  | `feishu_topic_messages 未挂载:宿主 tools 服务不可用(会话继续)` | 缺 `tools` 服务:话题读取工具不注册 |
234
235
  | `adopted N legacy staged attachment(s) into …; the one-time _staging migration is deprecated and will be removed after one release cycle` | 旧版 `_staging` 遗留文件被一次性迁入话题目录;按 D12,该兼容路径在 v2 发布后的下一个 minor 版本移除,可据此清理旧数据 |
235
236
 
236
- **未配置凭据(不是启动失败)**:日志会提示 `飞书应用凭据未配置完整(缺少 FEISHU_APP_…)`,插件照常加载但**不连接**;打开 Web **设置 → 飞书渠道 → 连接**,按三步引导填写 App ID / App Secret 并保存后自动连接。
237
+ **未配置凭据(不是启动失败)**:日志会提示 `飞书应用凭据未配置完整(缺少 FEISHU_APP_…)`,插件照常加载但**不连接**;打开 Web 左侧栏 **飞书渠道 → 连接**,按三步引导填写 App ID / App Secret 并保存后自动连接。
237
238
 
238
239
  **常见连接失败**(插件保持加载,面板「连接」标签页显示原因,可重试):
239
240