@dsh-enhanced/lark-channel 0.1.3 → 0.1.4
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 +69 -279
- package/bin/dsh-lark-setup.js +2 -2
- package/cordis.patch.yml +1 -0
- package/docs/operations.md +118 -0
- package/docs/progress-security.md +123 -0
- package/docs/setup.md +281 -0
- package/docs/supervised-growth.md +56 -0
- package/lib/adapter.d.ts +10 -18
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +152 -15
- package/lib/adapter.js.map +1 -1
- package/lib/config.d.ts +1 -0
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +2 -0
- package/lib/config.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/progress.d.ts +3 -1
- package/lib/progress.d.ts.map +1 -1
- package/lib/progress.js +107 -20
- package/lib/progress.js.map +1 -1
- package/lib/sdk.d.ts +9 -0
- package/lib/sdk.d.ts.map +1 -1
- package/lib/sdk.js +103 -0
- package/lib/sdk.js.map +1 -1
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +2 -0
- package/lib/service.js.map +1 -1
- package/lib/setup-profile.d.ts +1 -1
- package/lib/setup-profile.d.ts.map +1 -1
- package/lib/setup-profile.js +8 -4
- package/lib/setup-profile.js.map +1 -1
- package/lib/setup.d.ts +78 -1
- package/lib/setup.d.ts.map +1 -1
- package/lib/setup.js +963 -86
- package/lib/setup.js.map +1 -1
- package/lib/systemd.d.ts +25 -4
- package/lib/systemd.d.ts.map +1 -1
- package/lib/systemd.js +168 -19
- package/lib/systemd.js.map +1 -1
- package/lib/types.d.ts +18 -0
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/windows-task.d.ts +3 -0
- package/lib/windows-task.d.ts.map +1 -1
- package/lib/windows-task.js +27 -1
- package/lib/windows-task.js.map +1 -1
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -1,201 +1,58 @@
|
|
|
1
1
|
# @dsh-enhanced/lark-channel
|
|
2
2
|
|
|
3
|
-
飞书/Lark
|
|
3
|
+
飞书/Lark 的薄协议适配器:把 WebSocket 长连接消息写入 `assistant-delivery` 的 typed Inbox,并把已经落盘的 Outbox intent 转成飞书发送或回复 API。本包不拥有配对、会话、重试或授权真源。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
插件默认 `enabled: false`,安装后不会立即读取凭据或联网。启用后,合法新消息落盘时可添加 `Get`,最终答复发送成功后可添加 `DONE`;reaction 与原生执行进度都是 best-effort 展示,最终答复仍由 Delivery Outbox 保证。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
默认进度策略为 `progressDetails: direct`:经 Delivery 授权的私聊可显示限长、常见凭据已脱敏的工具参数和结果;默认向导只授权 owner。群聊只显示工具名、状态、步骤与待办。`progressDetails: off` 可在所有会话隐藏参数和结果。任何 reasoning/thinking 内容都不外发;飞书 `message_cot` 不可用时只降级展示,不影响任务或最终回复。
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## 文档
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
- [安装、凭据与常驻服务](docs/setup.md)
|
|
12
|
+
- [会话命令、卡片、可靠性与排障](docs/operations.md)
|
|
13
|
+
- [受监督成长激活器](docs/supervised-growth.md)
|
|
14
|
+
- [进度展示、安全与权限边界](docs/progress-security.md)
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
cd /path/to/dsh-enhanced
|
|
15
|
-
pnpm --filter @dsh-enhanced/lark-channel build
|
|
16
|
-
pnpm --filter @dsh-enhanced/lark-channel run onboard --profile web --create-app --allow-agent-tools
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
如果 Web profile 已按本仓库的本地源码方式安装,也可以直接运行:
|
|
20
|
-
|
|
21
|
-
```sh
|
|
22
|
-
~/.dsh/profiles/web/node_modules/.bin/dsh-lark-setup --profile web --create-app --allow-agent-tools
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
向导会依次:
|
|
26
|
-
|
|
27
|
-
1. 调用飞书官方 Node SDK 的 `registerApp`,显示十分钟有效的确认链接和终端二维码;
|
|
28
|
-
2. 你在飞书中选择已有应用或创建新应用,并确认权限增量;
|
|
29
|
-
3. 将返回的 `App Secret` 自动写入 macOS Keychain、Linux Secret Service 或当前 Windows 用户的 DPAPI 加密文件;Secret 不经过命令行参数、不写 profile、也不打印;
|
|
30
|
-
4. 用真实凭据建立一次临时长连接;
|
|
31
|
-
5. 显示一次性 `DSH-CONNECT-...` 短语,等待你私聊机器人并原样发送;
|
|
32
|
-
6. 从这条单聊取得应用作用域下的准确 `open_id`,只把该身份配为 owner;
|
|
33
|
-
7. 更新 `web/cordis.patch.yml`,启用 channel,添加精确 ingress/reply/credential 规则;显式传入 `--allow-agent-tools` 时,为本地 `foreground` Agent 添加跨 preset/workspace 的通用 capability 规则,同时为 Delivery 当前及兼容 preset、绝对 workspace、canonical owner principal、`external` initiator 添加精确主体的通用 capability 与工具规则;它们同时覆盖动态工具和插件内部二次 Policy 动作,不放宽 `background`;随后运行 `dsh --profile web --dump-config` 自检;
|
|
34
|
-
8. 安装并启动该 profile 的用户级常驻服务:macOS 使用 launchd,Linux 使用 systemd,Windows 使用 best-effort Task Scheduler;均以 `dsh --profile web --no-open` 运行。
|
|
16
|
+
## 兼容性
|
|
35
17
|
|
|
36
|
-
|
|
18
|
+
- DeepSeek Harness:`>=0.1.0-rc.8 <0.2.0` 基线语义(通过 `assistant-delivery`)。
|
|
19
|
+
- `@dsh-enhanced/assistant-delivery`:`>=0.1.0 <0.2.0`。
|
|
20
|
+
- `@dsh-enhanced/credentials-keychain`:handle 模式为 `>=0.1.0 <0.2.0`;env fallback 不要求其激活。
|
|
21
|
+
- 官方 `@larksuiteoapi/node-sdk`:固定 `1.73.0`。
|
|
22
|
+
- Node.js:`^22.19.0 || >=24.0.0`。
|
|
37
23
|
|
|
38
|
-
|
|
39
|
-
- `im:message.p2p_msg:readonly`、`im:message.group_at_msg:readonly`:接收私聊和群内 @ 消息;
|
|
40
|
-
- `im:message.reactions:write_only`:在原消息上添加 `Get` / `DONE` 状态;
|
|
41
|
-
- `im:message:send_as_bot`:发送及回复消息;
|
|
42
|
-
- `im:resource`:通过[获取消息中的资源文件](https://open.feishu.cn/document/server-docs/im-v1/message-resource/get)接口,按消息 ID 与该消息内的 image key 下载用户发送的图片。
|
|
43
|
-
- `im.message.receive_v1`:消息事件;
|
|
44
|
-
- `card.action.trigger`:处理审批卡片按钮、模型选择级联刷新和最终确认按钮。
|
|
24
|
+
仓库级基线见[兼容性说明](../../docs/compatibility.md)。
|
|
45
25
|
|
|
46
|
-
|
|
26
|
+
## 安装
|
|
47
27
|
|
|
48
|
-
|
|
28
|
+
先配置 `@dsh-enhanced/assistant-policy` 与 `@dsh-enhanced/assistant-delivery`,再安装本包:
|
|
49
29
|
|
|
50
30
|
```sh
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
--profile web \
|
|
54
|
-
--create-app \
|
|
55
|
-
--app-id cli_0123456789abcdef
|
|
56
|
-
|
|
57
|
-
# 仅手工录入已有应用凭据,不修改飞书控制台配置
|
|
58
|
-
~/.dsh/profiles/web/node_modules/.bin/dsh-lark-setup \
|
|
59
|
-
--profile web \
|
|
60
|
-
--app-id cli_0123456789abcdef
|
|
31
|
+
dsh plugin --profile web add @dsh-enhanced/lark-channel
|
|
32
|
+
dsh --profile web --dump-config
|
|
61
33
|
```
|
|
62
34
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
重复执行会更新同一个 account 的受管配置,不重复添加规则或 handle。Agent 能力策略是显式三态:不传参数会保留现状,`--allow-agent-tools` 写入本地 foreground 与精确 Delivery 主体两类通用 capability 可达性规则(并保留外部主体的工具级 allow/deny),`--disable-agent-tools` 删除向导为该 account 管理的这些规则;普通重跑不会意外授权或撤权。profile 校验失败时会在进程内恢复原内容;不会保留备份文件。
|
|
66
|
-
|
|
67
|
-
### 只刷新 Agent Policy
|
|
68
|
-
|
|
69
|
-
更新 capability 规则不需要重走 onboarding;Web/direct-only profile 即使没有启用飞书,也使用同一个非交互刷新模式:
|
|
35
|
+
推荐使用跨平台向导完成飞书应用授权、凭据保存、owner 绑定、最小 Policy 和用户级常驻服务:
|
|
70
36
|
|
|
71
37
|
```sh
|
|
72
|
-
# 写入/刷新本地 foreground;若飞书已启用,同时刷新精确 Delivery 能力规则
|
|
73
38
|
~/.dsh/profiles/web/node_modules/.bin/dsh-lark-setup \
|
|
74
39
|
--profile web \
|
|
75
|
-
--
|
|
40
|
+
--create-app \
|
|
76
41
|
--allow-agent-tools
|
|
77
|
-
|
|
78
|
-
# 删除 setup 托管的 foreground;若飞书已启用,同时删除该 account 的 Agent 能力规则
|
|
79
|
-
~/.dsh/profiles/web/node_modules/.bin/dsh-lark-setup \
|
|
80
|
-
--profile web \
|
|
81
|
-
--refresh-agent-policy \
|
|
82
|
-
--disable-agent-tools
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
`--refresh-agent-policy` 必须与上述 allow/disable 之一搭配,除 `--profile` 外只可选带 `--account`。本地 foreground 规则独立于 Lark row,总能在已安装的 personal-assistant profile 中刷新;只有 profile 已启用 Lark 时才同时重建精确 Delivery 规则,传入 account 时也必须与该 account 精确相同。该模式不读 App Secret,不发起设备授权,不修改 App、credential handle、owner、conversation binding 或其他 channel 配置,也不安装/重启常驻服务。它原子写入 profile 后立即运行 `dsh --dump-config` 校验;校验失败会原子恢复原 patch。
|
|
86
|
-
|
|
87
|
-
飞书授权只是建立应用凭据和 owner 绑定;`lark-channel` 本身仍是运行在 DSH Host 内的插件。因为这里安装到 `web` profile,默认由向导在后台常驻这个 profile,不需要保持浏览器打开,也不需要再手动执行 `dsh web`。
|
|
88
|
-
|
|
89
|
-
## 可选:受监督成长激活器
|
|
90
|
-
|
|
91
|
-
`dsh-supervised-growth-setup --profile web` 仅在完成上述 Lark onboarding 后使用。它只读取
|
|
92
|
-
Delivery/Automations 的本地 SQLite 控制面,不解析或执行任何模型输入;先等待一条与 profile 中
|
|
93
|
-
account、tenant、默认 workspace 和 preset 完全一致的活动 owner 私聊 binding。没有匹配时会要求
|
|
94
|
-
owner 再发一条普通私聊并有界轮询,超时或存在多个匹配时都不修改 profile。
|
|
95
|
-
|
|
96
|
-
激活器随后检查活动 automation:**任何**已有 active job(包括旧 `assistant-heartbeat` job)都会默认
|
|
97
|
-
阻止启用。scheduler 一旦开启会加载全部 durable row,不能按 owner 名称猜测旧 heartbeat 是否安全。
|
|
98
|
-
确认这些任务可在 scheduler 开启后继续运行时才显式传入 `--ack-existing-automations`;该确认不会恢复、
|
|
99
|
-
创建或改写 job 定义,但不会阻止其被 scheduler 领取。
|
|
100
|
-
|
|
101
|
-
通过检查后,激活器基于 `dsh --dump-config` 的有效组合树写入完整受管 overlay,而不是假定用户 raw
|
|
102
|
-
patch 已含 meta-bundle 的 config。Delivery/Automations DB 路径也从有效树读取。原子写入后它会再次
|
|
103
|
-
dump 并验证 scheduler、TraeX cwd/route、`automation-runs` budget、heartbeat 和每条受管 Policy rule;
|
|
104
|
-
若 home/profile 高优先级 layer 覆盖了其中任一项,立即恢复原 patch。它在写入前和重启前都会重读同一
|
|
105
|
-
owner binding 的完整 route/status/version;版本变化、撤销或多 binding 均 fail closed。
|
|
106
|
-
|
|
107
|
-
在重启 Host 前,激活器调用 TraeX provider 唯一的 installer-only readiness probe:固定 read-only ACP
|
|
108
|
-
catalog handshake 会验证可执行文件、登录和至少一个可用模型,但不会发送模型 prompt。普通
|
|
109
|
-
`listModels`/`resolveModel` 不使用这个静态 cwd 例外;实际模型执行仍要求 live loop session 的 canonical
|
|
110
|
-
cwd 与配置 workspace 完全一致。restart 后还必须通过 resident running health gate,否则会恢复原 profile
|
|
111
|
-
和旧服务。Windows Task Scheduler 没有这个实现可验证的健康信号,supervised-growth 因而在 Windows
|
|
112
|
-
拒绝激活,不伪装为常驻成功。
|
|
113
|
-
|
|
114
|
-
overlay 仅允许精确 workspace/preset 的后台 heartbeat 在 08:00–22:00 每 120 分钟运行一次(恰好每日
|
|
115
|
-
7 次):先 `evolution_review`,才可最多一次 `evolution_propose`;每轮最多 2 次工具调用和 512 输出
|
|
116
|
-
token。Policy budget 的 metric 是 `automation-runs`,每天最多 7 次、每次固定计 1;512 仅是输出上限,
|
|
117
|
-
不是不可证明的总 token 预算。pending Evolution proposal 的审批卡只可由固定 Evolution 背景主体投递到
|
|
118
|
-
这个 exact owner binding,owner approval 仍是唯一 apply 门。scratch 明确禁止 decide/apply、修改代码、
|
|
119
|
-
凭据、Policy 或既有 automation;无候选时精确输出 `HEARTBEAT_OK`。overlay 不授予 shell、文件系统、
|
|
120
|
-
网络或凭据权限。
|
|
121
|
-
|
|
122
|
-
```sh
|
|
123
|
-
~/.dsh/profiles/web/node_modules/.bin/dsh-supervised-growth-setup --profile web
|
|
124
|
-
# 仅当确认已有活动任务可以在 scheduler 开启后继续运行时:
|
|
125
|
-
~/.dsh/profiles/web/node_modules/.bin/dsh-supervised-growth-setup --profile web --ack-existing-automations
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
升级前已经存在的 conversation binding 会继续固定旧 preset/workspace(旧安装常见 preset 为 `primary`),新 binding 则使用 Delivery 当前配置的默认身份。执行上述 `--refresh-agent-policy --allow-agent-tools` 时会按完整 preset+workspace 保留精确 legacy 规则,同时把主规则更新到当前默认身份;refresh 会清除所有历史 account id 的 setup-managed external reply/capability/tool 规则,再只为当前 account 的 canonical owner principal 重建。Delivery 外部规则的 principal、preset、workspace 始终精确,capability 的 action/resource 使用 `*` 以覆盖该身份已挂载的动态工具及插件内部 Policy 动作;外部工具级规则仍用工具 id `*` 并可配置显式 deny。本地 `foreground` 规则则有意对 preset/workspace 使用 `*`,以支持 Web/direct 中的用户切换。
|
|
129
|
-
|
|
130
|
-
已完成飞书配置、只需安装或重启常驻服务时运行:
|
|
131
|
-
|
|
132
|
-
```sh
|
|
133
|
-
~/.dsh/profiles/web/node_modules/.bin/dsh-lark-setup \
|
|
134
|
-
--profile web \
|
|
135
|
-
--install-service
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
macOS 会创建 `~/Library/LaunchAgents/ai.deepseek.dsh.profile.web.plist`,登录后自动启动、异常退出后自动拉起;只写入 `DSH_HOME` 和不含相对目录的 `PATH`,不会复制当前 shell 的 token、password 或其他环境变量。常用运维命令:
|
|
139
|
-
|
|
140
|
-
```sh
|
|
141
|
-
# 查看状态
|
|
142
|
-
launchctl print gui/$(id -u)/ai.deepseek.dsh.profile.web
|
|
143
|
-
|
|
144
|
-
# 查看错误日志
|
|
145
|
-
tail -f ~/.dsh/logs/web-host.error.log
|
|
146
|
-
|
|
147
|
-
# 停止并卸载当前登录会话中的服务;重新执行 --install-service 可恢复
|
|
148
|
-
launchctl bootout gui/$(id -u)/ai.deepseek.dsh.profile.web
|
|
149
42
|
```
|
|
150
43
|
|
|
151
|
-
|
|
44
|
+
源码工作区也可运行:
|
|
152
45
|
|
|
153
46
|
```sh
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
systemctl --user restart dsh-profile-web.service
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
Linux 一键凭据需要当前桌面会话中运行 Secret Service,并提供 `/usr/bin/secret-tool` 与 `/usr/bin/systemd-ask-password`。无桌面 Secret Service 的服务器或容器请使用 `--no-service`,并通过下文的 `environment` provider 和自己的 systemd/container secret injection 管理凭据。
|
|
160
|
-
|
|
161
|
-
Windows 会把 DPAPI 加密的 PSCredential 保存到 `$DSH_HOME/credentials-keychain`,并创建当前用户的 `DSH profile web` 登录任务。该实现不会保存明文,但 Windows/Node/npm/Git Bash 组合差异较大,因此属于 best-effort,不作兼容承诺:
|
|
162
|
-
|
|
163
|
-
```powershell
|
|
164
|
-
schtasks.exe /Query /TN "DSH profile web"
|
|
165
|
-
schtasks.exe /End /TN "DSH profile web"
|
|
166
|
-
schtasks.exe /Run /TN "DSH profile web"
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
如果确实希望自己管理进程,在首次向导中加 `--no-service`,然后手动运行 `dsh --profile web --no-open`。不要同时启动前台和 LaunchAgent 两份相同 profile,否则 Web 端口与飞书长连接会发生竞争。
|
|
170
|
-
|
|
171
|
-
服务启动后,同一私聊或稳定群聊 lane 会持续恢复同一个 DSH session。`/status`(或 `/session`)显示当前代次与上下文统计;`/stop` 只停止正在执行的任务并保留该 session;`/new`(或 `/clear`)才停止旧任务并原子切换到空白的下一代 session;`/compact` 在宿主发布原生命令时压缩当前上下文。未知 slash 命令不会进入模型。
|
|
172
|
-
|
|
173
|
-
可以先在飞书里私聊机器人发送 `/model`。机器人会返回一张飞书卡片,依次选择“分组 / Provider”“模型”“Effort 程度”,再点击“确认选择”;选择 provider 时,同一张卡片会立即刷新为该分组的模型,选择模型时又会刷新为该模型实际支持的 effort。没有独立 effort 档位的模型只显示“默认(该模型无 effort 档位)”。系统确认后会发一条文字回复,下一条普通消息开始使用新选择,原上下文保留。目录来自当前 Host 实际注册的 provider/model,不消耗一次模型调用。若目录中的异常字段或飞书卡片格式错误导致卡片未被接受,机器人会立即改发完整纯文本目录,可继续使用 `/model use <provider/model>`,不会静默重试后进入死信。
|
|
174
|
-
|
|
175
|
-
飞书的多个静态选择器本身彼此独立,所以插件在 provider、model、effort 选择器上分别注册签名 callback,并通过长连接回调返回 `{ card: { type: "raw", data: ... } }` 更新同一张 schema 2.0 卡片。固定的官方 Node SDK 会忽略 `type=card` 长连接帧,transport 因此在其边界补充分片合并、callback 分发和 ACK 兼容桥。每次级联状态都带持久化 revision;旧卡片回调只会重绘当前权威状态,不会覆盖较新的选择。目录按 operation 保存在 Delivery SQLite,Host 短暂重启后仍能恢复。最终确认时还会在 adapter 与 Delivery 两层拒绝不匹配的分组/模型,并校验目标模型支持的 effort。选择“默认(由模型决定)”不会固定 reasoning effort。`/model use <provider/model>` 仍可作为文字后备,`/model reset` 恢复部署默认模型,`/new` 轮换到新 session 但保留该聊天的模型与 effort。Web 会话里可调用 `assistant_health` 查看 `larkChannel.state`,正常应为 `connected`。
|
|
176
|
-
|
|
177
|
-
例如本机已安装并登录 `@dsh-enhanced/coding-subscription-provider` 的 Codex CLI 时:
|
|
178
|
-
|
|
179
|
-
```text
|
|
180
|
-
/model
|
|
181
|
-
# 在卡片中选择三个下拉框并点击“确认选择”
|
|
182
|
-
你好,请介绍一下你自己
|
|
47
|
+
pnpm --filter @dsh-enhanced/lark-channel build
|
|
48
|
+
pnpm --filter @dsh-enhanced/lark-channel run onboard --profile web --create-app --allow-agent-tools
|
|
183
49
|
```
|
|
184
50
|
|
|
185
|
-
|
|
51
|
+
向导不会把 App Secret 放进 argv、profile 或日志。macOS 使用 Keychain,Linux 优先 Secret Service、无桌面环境降级为严格权限的 protected-file,Windows 使用 best-effort DPAPI。完整流程、已有应用接入和平台排障见[安装文档](docs/setup.md)。
|
|
186
52
|
|
|
187
|
-
|
|
53
|
+
## 最小配置
|
|
188
54
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
先安装并配置 `@dsh-enhanced/assistant-policy` 与 `@dsh-enhanced/assistant-delivery`,再安装本包:
|
|
192
|
-
|
|
193
|
-
```sh
|
|
194
|
-
dsh plugin --profile web add @dsh-enhanced/lark-channel
|
|
195
|
-
dsh --profile web --dump-config
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
如果不使用向导,可在 profile patch 中填写真实值并启用:
|
|
55
|
+
不使用向导时,可在 profile patch 中配置:
|
|
199
56
|
|
|
200
57
|
```yaml
|
|
201
58
|
config:
|
|
@@ -209,134 +66,67 @@ config:
|
|
|
209
66
|
domain: feishu
|
|
210
67
|
requireMentionInGroups: true
|
|
211
68
|
showProgress: true
|
|
69
|
+
progressDetails: direct
|
|
212
70
|
statusReactions: true
|
|
213
71
|
imageDownloadTimeoutMs: 30000
|
|
214
72
|
```
|
|
215
73
|
|
|
216
|
-
|
|
74
|
+
`credentialHandle` 应由 `@dsh-enhanced/credentials-keychain` 提供,并只允许 consumer `dsh-enhanced-lark-channel`、purpose `connect`。兼容部署可改用 `appSecretEnv`,两者只能选一个;配置不接受 `appSecret` 等明文字段。
|
|
217
75
|
|
|
218
|
-
|
|
219
|
-
LARK_APP_SECRET='...' dsh --profile web
|
|
220
|
-
```
|
|
76
|
+
飞书应用至少需要机器人身份读取、单聊/群内 @ 消息接收、机器人发消息、`im.message.receive_v1`;`Get`/`DONE` 需要 `im:message.reactions:write_only`,图片需要 `im:resource`,审批和选择卡片需要 `card.action.trigger`。事件与回调使用 WebSocket 长连接,不需要公网 callback URL。
|
|
221
77
|
|
|
222
|
-
|
|
78
|
+
## 常用命令
|
|
223
79
|
|
|
224
|
-
|
|
80
|
+
在飞书会话中使用:
|
|
225
81
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
82
|
+
```text
|
|
83
|
+
/status # 当前 session 代次与上下文统计;/session 同义
|
|
84
|
+
/stop # 停止当前任务,保留 session
|
|
85
|
+
/new # 停止旧任务并切换到空白 session;/clear 同义
|
|
86
|
+
/compact # 宿主支持时压缩当前上下文
|
|
87
|
+
/model # 打开 provider/model/effort 选择卡片
|
|
88
|
+
/model use <route> # 文字方式切换模型
|
|
89
|
+
/model reset # 恢复部署默认模型
|
|
90
|
+
/permissions # 查看并选择权限档位
|
|
91
|
+
/permission ask
|
|
92
|
+
/permission auto
|
|
93
|
+
/permission full confirm
|
|
94
|
+
```
|
|
231
95
|
|
|
232
|
-
|
|
96
|
+
未知 slash 命令不会进入模型。模型卡片、权限卡片和会话 lane 语义见[操作文档](docs/operations.md)。
|
|
233
97
|
|
|
234
|
-
|
|
98
|
+
运维常用:
|
|
235
99
|
|
|
236
100
|
```sh
|
|
237
|
-
#
|
|
238
|
-
|
|
101
|
+
# 重新安装或重启用户级常驻服务
|
|
102
|
+
dsh-lark-setup --profile web --install-service
|
|
239
103
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
--create-app \
|
|
243
|
-
--app-id cli_0123456789abcdef
|
|
104
|
+
# 刷新 setup 托管的 Agent Policy,不重走 onboarding
|
|
105
|
+
dsh-lark-setup --profile web --refresh-agent-policy --allow-agent-tools
|
|
244
106
|
```
|
|
245
107
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
长连接订阅至少需要:
|
|
249
|
-
|
|
250
|
-
- `im.message.receive_v1` 接收消息事件;
|
|
251
|
-
- 发送/回复消息所需的 IM 权限;
|
|
252
|
-
- 机器人读取自身身份所需权限;
|
|
253
|
-
- 如果希望群内无需 `@机器人` 就触发,必须同时把 `requireMentionInGroups` 设为 `false` 并在飞书侧申请更宽的群消息权限。
|
|
254
|
-
|
|
255
|
-
`account` 和 `tenant` 是 DSH 内部的稳定路由命名,不是 Secret。个人单应用通常保持默认 `account: primary`、`tenant: personal`;真正的飞书身份由向导发现的应用作用域 `open_id` 决定。
|
|
256
|
-
|
|
257
|
-
陌生外部身份仍会由 `assistant-delivery` 以 fail-closed 方式拒绝;不要依赖飞书侧“能收到事件”等同于已授权。
|
|
258
|
-
|
|
259
|
-
## 可靠性语义
|
|
260
|
-
|
|
261
|
-
- 事件处理器会等待 `assistant-delivery.acceptInbound()` 完成,即 inbox 落盘后才向长连接回调返回成功。
|
|
262
|
-
- 飞书 SDK 标准化产生的 `` 等资源标记会在进入 Delivery 前被改写;纯图片消息的正文保持为空,provider image key 只保留在隔离的附件描述符中,不会伪装成用户提示词。
|
|
263
|
-
- 图片下载由 Delivery 在再次确认绑定、Policy、目标模型图片能力和 AttachmentStore 可用后显式调用,不在 WebSocket 回调内下载。adapter 只接受同一入站消息的严格 message ID/image key,并固定请求 `GET /open-apis/im/v1/messages/:message_id/resources/:file_key?type=image`;拒绝 URL、路径片段、重定向和非图片资源。`imageDownloadTimeoutMs` 默认 30 秒,可设为 1–120 秒;总截止时间覆盖 tenant token 获取和资源 GET,caller 取消、插件卸载或 credential lease 切换在 token cache miss 阶段也会及时返回。调用方同时传入逐图 byte 上限,transport 在 header、流读取和 PNG/JPEG/GIF/WebP magic/MIME 三层校验。
|
|
264
|
-
- 只有合法、非重复且状态为 `queued` 的入站消息会异步添加 `Get`;未授权、死信或 provider 重放不会重复 reaction。精确表情类型为大小写敏感的 `Get` 和 `DONE`。
|
|
265
|
-
- Agent 的最终回复会显式携带原始飞书 `messageId`,先成功回复该消息,再异步添加 `DONE`。Delivery 在飞书返回有效 message id 后立即记录 `accepted`,不会等待 presentation-only reaction;发送失败、无最终回复、任务失败或取消均不会添加 `DONE`,reaction 失败只降级 channel health。首版保留 `Get`,不会为了替换表情再申请 reaction 读取权限。
|
|
266
|
-
- 执行进度使用飞书原生 `/open-apis/im/v1/message_cot` 展示:`showProgress: false` 可关闭。该接口不在固定版 Node SDK 的高层 API 中,因此作为可降级展示使用;创建或更新失败只记录 channel health,不会重跑任务或影响最终 Outbox 回复。
|
|
267
|
-
- 进度映射只接受 Delivery 的强类型事件:开始、步骤说明、脱敏工具名、工具成功/失败、待办快照和终态;`assistant/chunk`(包括流式 `reasoning-delta`)、工具 arguments、工具 output、系统提示和错误详情不会进入飞书载荷。步骤说明优先取 `assistant/message` 已定稿的 `reasoning` 块(助手自己对该步的说明,不是流式片段);**并非所有 provider 都会产出 reasoning**——订阅制 CLI 只声明 effort 能力、实际仅回传 `text`,ACP 的 thought 通道到 DSH reasoning 的映射尚未实现,因此 `step/start` 另外映射为中性阶段文案,保证这些 provider 也不会出现空面板。同一次运行的每条步骤/待办各用独立 `messageId` 追加,避免互相覆盖。
|
|
268
|
-
- 回合失败时除 `RUN_ERROR` 外还会在面板正文写入一行「任务未完成」,附带上游错误码(如 `ACP_PROTOCOL_ERROR`):provider 可能在产出任何内容前就失败,只发 `RUN_ERROR` 会让面板停在首行、看起来像卡住。只透传短错误码,provider 的原始错误消息可能包含 prompt 或上游载荷,不出边界。
|
|
269
|
-
- Agent 回答按 Markdown 渲染:回答本身是 Markdown(表格、加粗、行内代码),以 `plain` 文本发送会把 `|---|`、`**` 等原始语法直接暴露给用户。Delivery 仅在渠道 adapter 声明了 `markdown` 能力时才请求该格式,否则降级为 `plain`——coordinator 会把 adapter 未声明的格式判为 `unsupported-format` 并丢弃整条消息,因此降级是为了保证回复不会因能力不匹配而丢失。回答卡片保持内容优先:不额外加 header(飞书已在气泡上方显示机器人名称与头像,再加会重复),只用一个 `markdown` 组件承载正文,并通过 `wide_screen_mode` 避免 Markdown 表格在宽屏下折行。
|
|
270
|
-
- `messageId` 是稳定 inbound event id;delivery 的 `(channel, account, eventId)` 唯一约束承担跨重启去重。
|
|
271
|
-
- 单聊按 account/tenant/user/chat 绑定。群聊顶层消息没有 provider `root_id`,因此按发送者生成 `dsh-lark-top-sender/<sha256(open_id)>` 合成 thread;account/tenant/chat 仍是 conversation 的独立字段,同一群内同一发送者可连续复用 DSH session,不同发送者不会落入同一个 owner binding。飞书真实提供 `root_id` 时仍直接使用可寻址的根消息 id,使不同回复串彼此隔离;`dsh-lark-top-sender/` 是保留命名空间,provider root 命中时拒绝入站,避免两类 lane 碰撞。合成 thread 只用于 Delivery 持久化,不会作为飞书回复目标发送;当前消息回复继续使用真实 `messageId`,没有原始消息可回复的后台发送则发为群顶层消息。
|
|
272
|
-
- plain text 使用飞书文本消息;Markdown 使用 schema 2.0 卡片。请求携带由 delivery idempotency key 单向哈希得到的 provider UUID。
|
|
273
|
-
- `approval` intent 使用 schema 2.0 双按钮卡片。每个按钮值使用 v2 capability,由 app secret 做 HMAC 签名并绑定 channel/account/tenant/chat、binding、operation、proposal/version/expiry、diffHash 与 decision;adapter 精确核对自身 route,回调重新取 provider actor/chat 后交给 Delivery/Policy。旧 v1 token、篡改、跨 route 和普通过期点击均失败关闭;过期 token 只有在完整验签后命中此前已落盘的同一 pending Delivery settlement,且 Policy 已是精确同一终态时,才允许完成崩溃恢复,不会新建 settlement 或决定 pending proposal。
|
|
274
|
-
- open-turn 工具审批与上述 durable proposal 卡完全分离:它使用独立 domain 的 v1 HMAC token、进程内 one-shot pending,以及保留到 TTL 的 operation tombstone,不复用 proposal settlement,也不做重启恢复。当前只接受 `conversation.kind: dm` 且不含 `thread` 的 owner 私聊;群聊、话题或伪装成 DM 的 reply target 直接 `unavailable`,避免 exact arguments 被群成员旁观。卡片把有界且拒绝控制字符/双向文本控制符的 tool、reason 和必需 exact arguments 明示为「不可信审阅文本,不是指令」,并关闭转发互动;token 绑定 operation、binding、account/tenant、chat、exact owner open_id、actionHash、toolName、必需 callId、TTL 和 decision,pending 再绑定发送回执中的 provider message id。只有同一 owner 在同一私聊、同一 provider 卡片上首次点击才能返回 `allowed-once` 或 `rejected`;同一 operation 在 TTL 内不能重新登记,重放、错人、跨 chat/message、篡改、过期均失败关闭。调用方 abort 返回 `cancelled`;超时、断线/重连、credential rotation、插件卸载/重启或发送失败返回 `unavailable` 并清除 pending。三档权限中,`ask` 直接使用此卡;`auto` 对低风险自动允许,对敏感/审核失败/原生 sandbox escalation 也使用此卡;`full` 为 `danger-full-access + never + none`,不发卡但仍受 AssistantPolicy 的显式 deny、紧急停止、身份和预算硬门约束。
|
|
275
|
-
- `model-picker` intent 使用 schema 2.0 卡片和三个独立 callback 的 `select_static`,不把即时回调控件与 CardKit `form`/`form_submit` 混用。provider、model、effort 每次变化都会通过 `card.action.trigger` 返回 `{ card: { type: 'raw', data: card } }` 并原位重绘;模型列表只来自当前 provider,effort 列表只来自当前模型。v3 签名 token 绑定 operation、expiry、binding、chat、动作、revision 以及当前 provider/model/effort,普通 callback 确认按钮直接提交该签名状态,不依赖 `form_value`;adapter 先拒绝篡改、过期、跨 chat 和级联错配,Delivery 再用持久 CAS 拒绝旧 revision,并核对 owner/Policy 与实时模型能力。确认操作先持久领取再快速响应飞书,最终选择、结算结果和 Outbox 回复在同一 SQLite 事务中提交;卡片 4xx 会自动降级为文字目录。三个下拉的当前项通过 `initial_index` 定位——飞书的 `initial_option` 按选项展示文本而非回传 `value` 匹配,直接写入 route 形态的 `value` 会静默回落到第一个选项;`initial_option` 仅在该文本唯一标识一个选项时附带,当前项不在选项列表内时两者都不下发,不伪造预选。卡片版式按飞书官方卡片风格规范组织:header 用 `title` + `subtitle` + `icon` 承载「这是什么 / 当前模型」,三个下拉各自放进带描边的 `interactive_container` 分块(而不是平铺 markdown 标签),每块含一行灰色 `notation` 说明,末尾只保留一个 `primary` 按钮作为唯一焦点。升级后应重新发送 `/model`,旧 v1/v2 卡片不会继续生效。
|
|
276
|
-
- `permission-picker` intent 使用独立 schema 2.0 三档卡片,当前档位显示勾选,full 使用 danger 样式和飞书原生二次确认。卡片关闭转发互动,三个按钮分别携带独立 HMAC capability;token 绑定 channel/account/tenant/chat、精确 owner、binding/version/session、权限状态指纹、目标档位与 TTL。adapter 先拒绝错人、跨 chat、篡改和过期,Delivery 再核对原 Outbox 及 provider message id、active owner/binding/session、Policy 和当前权限状态;通过后只把固定 `/permission` 命令送入同一 durable Inbox 串行路径,不在 adapter 内直接改权限。旧卡、复制卡和晚到的相反选择都不能覆盖新状态;卡片格式 4xx、缺少签名或结算能力时退回完整文字说明。
|
|
277
|
-
- 权限/格式错误是确定未发送;限流和未连接可以安全重试;网络超时或未知 SDK 错误进入 `unknown_after_send`,不会盲目重发。
|
|
278
|
-
- 当前飞书 API 没有为该发送 UUID 暴露可靠查询/对账接口,因此 adapter 明确声明 `reconcileUnknownSend: false`、无 delivered/read receipt;Delivery 会保留这类 `unknown_after_send` 等待 owner 决策,不会反复领取无实现的对账任务或消耗 attempt。
|
|
279
|
-
|
|
280
|
-
官方长连接会自动重连,但不提供可持久化 replay cursor 或历史补拉接口。本包记录 `reconnecting` / `connected-with-gap` 与 `gapGeneration`;它依赖飞书 redelivery 和 delivery inbox 去重,不宣称断线期间零丢失。若未来出现官方 cursor/backfill contract,应先加入崩溃与重放测试再启用。
|
|
281
|
-
|
|
282
|
-
## 安全与权限
|
|
108
|
+
## 权限与数据边界
|
|
283
109
|
|
|
284
|
-
`--allow-agent-tools`
|
|
110
|
+
- `--allow-agent-tools` 是高权限显式开关:为本地 `foreground` 与精确 owner Delivery 主体建立 capability/工具可达性;不授权 `background`,也不绕过显式 deny、紧急停止、身份、预算及插件业务硬门。
|
|
111
|
+
- `ask` 和 `auto` 中需要人工确认的工具调用只向 active owner 私聊发送一次性审批卡;`full` 关闭逐次审批并放开 sandbox,应保持 owner 与应用可用范围最小。
|
|
112
|
+
- 网络仅访问所选飞书/Lark OpenAPI、token 与 WebSocket endpoint;图片读取使用固定消息资源端点,不接受消息或模型提供的 URL,并关闭重定向。
|
|
113
|
+
- App Secret 不写 Delivery 数据库、工具参数、health、route、日志或异常;Linux protected-file 没有额外静态加密,同 UID、root 和可读备份仍能取得内容。
|
|
114
|
+
- Delivery SQLite 保存标准化文本、路由 id 和最多 10 个受限附件描述符;不保存 raw 事件、token 或下载 URL。图片字节只交给 AttachmentStore。
|
|
115
|
+
- 私聊详细进度只使用 Delivery 生成的限长、常见凭据脱敏 preview;它不是秘密扫描器。群聊始终不发送参数或结果,reasoning/thinking 内容在任何会话都不外发。
|
|
116
|
+
- 群消息默认必须直接提及机器人;`@all` 不等于提及。最终授权始终由 Delivery/Policy 决定。
|
|
285
117
|
|
|
286
|
-
|
|
118
|
+
完整的文件系统、子进程、浏览器、审批签名和 Policy 说明见[进度与安全文档](docs/progress-security.md)。
|
|
287
119
|
|
|
288
|
-
|
|
120
|
+
## 当前限制
|
|
289
121
|
|
|
290
|
-
|
|
122
|
+
- v0.1 支持文本、受限图片描述符、文本/Markdown-card 出站、durable proposal、owner-DM 一次性工具审批、模型/权限卡片、reaction 和原生进度;模型不能提交任意 card JSON,也不能直接控制 reaction 或进度载荷。
|
|
123
|
+
- 只有图片资源具备受限下载能力,且依赖 AttachmentStore 与模型图片能力;文件、音频、视频和 sticker 只进入 metadata quarantine。没有病毒扫描、附件上传或消息编辑。
|
|
124
|
+
- 单应用长连接是集群竞争消费,不提供广播、多节点 exactly-once 或断线历史补拉;目标部署是受 supervisor 管理的单机进程。
|
|
125
|
+
- setup wizard 正式支持 macOS 与 Linux;Windows 为 best-effort。企业管理员审批和应用可用范围仍由飞书控制。
|
|
126
|
+
- `message_cot` 与 reaction 都可能因租户、应用类型或接口能力而降级;它们失败不会重跑任务,也不会阻断最终回复。
|
|
291
127
|
|
|
292
|
-
|
|
128
|
+
更细的传输可靠性和故障处理见[操作文档](docs/operations.md)。
|
|
293
129
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
若外部会话报告某个工具被拒,先查 `~/.dsh/assistant-policy/policy.sqlite` 的 `audit_events`:`reason_code` 为 `default-deny` 说明该会话没有匹配到 allow 规则,通常是 principal、preset 或 workspace 与 Delivery 实际绑定不一致,运行 `~/.dsh/profiles/web/node_modules/.bin/dsh-lark-setup --profile web --refresh-agent-policy --allow-agent-tools` 即可对齐;为 `rule-deny` 说明命中了显式 deny。注意这两者都不是审批拦截:审批由 `tools/pre-execute` 审查器发起,不写入 `audit_events` 的 denied 记录。
|
|
297
|
-
|
|
298
|
-
Policy tool guard 和插件内部 `authorizeAgent()` 都携带 Delivery 绑定的 canonical principal;Lark setup 生成的 external reply/capability/tool 规则只匹配当前 account 的精确 owner。其他 connector、Lark account 或 principal 即使使用相同 preset/workspace 也不会继承该授权。owner 在群内 @ 机器人时仍沿用同一 principal,因此这不是“仅 owner 私聊”的限制。
|
|
299
|
-
|
|
300
|
-
- **网络:**仅访问所选 `domain` 的飞书/Lark OpenAPI、token 服务和 WebSocket endpoint;图片读取使用固定的消息资源相对端点,不接受模型、消息正文或 provider payload 提供的 URL,并关闭重定向;没有通用 HTTP 工具。
|
|
301
|
-
- **凭据:**优先通过 `credentials-keychain` handle 获取;兼容模式只读取 `appSecretEnv` 指定的一项。值不写数据库、不进入 tool、health、route、日志或异常文本。`appId` 不是 secret。
|
|
302
|
-
- **文件系统:**运行时无业务文件读写;setup wizard 会原子更新所选 profile patch,通过 `assistant-delivery` 的本地控制面写入精确 owner,并以 `0600` 写入用户级 LaunchAgent plist、在 `$DSH_HOME/logs` 创建 Host 日志。官方 SDK 依赖的 `protobufjs` postinstall 只打印版本建议,仓库显式设为 `allowBuilds: false`,运行不需要安装脚本。
|
|
303
|
-
- **子进程:**运行时只使用 credential provider 的固定无 shell 命令。setup wizard 在 macOS 调用 `/usr/bin/security` 与 launchd,在 Linux 调用 `/usr/bin/secret-tool`、`/usr/bin/systemd-ask-password` 与 `systemctl --user`,Windows 调用固定 PowerShell DPAPI 命令与 Task Scheduler;自动生成的 Secret 只通过标准输入传递且缓冲区随后清零,不作为 argv 传递。所有平台都会调用 `dsh --dump-config` 验证 profile;常驻配置只包含解析后的程序路径和最小环境,不复制 ambient token/password。
|
|
304
|
-
- **浏览器:**setup wizard 会输出飞书官方的短期设备授权链接与二维码,但不会自动操控浏览器;由用户在飞书中选择已有应用或创建新应用,并查看、确认权限增量。
|
|
305
|
-
- **消息数据:**标准化文本、provider message id、chat/user/thread id,以及最多 10 个受限附件描述符会进入 delivery SQLite;raw 事件、token 和下载 URL 不保存,provider file key 只作为隔离账本中的不可信引用,不进入模型正文。授权 worker 下载的图片字节只交给 AttachmentStore,随后会话仅持有 AttachmentStore 返回的引用;本插件不把二进制写入 Delivery session 或 prompt。
|
|
306
|
-
- **进度数据:**仅发送有长度上限的工具名、已定稿的步骤说明、显式待办文本和固定状态文案;不发送流式思维链片段、工具参数/结果、凭据或内部错误详情。原生进度 API 与 reaction API 失败均按展示降级处理。
|
|
307
|
-
- **群消息:**默认必须直接提及机器人;`@all` 不等于提及机器人。最终授权始终由 delivery/policy 决定。
|
|
308
|
-
|
|
309
|
-
## 当前边界
|
|
310
|
-
|
|
311
|
-
- v0.1 自动处理文本及图片描述符入站、文本/Markdown-card 出站、durable proposal、owner-DM one-shot tool approval、model-picker 与 permission-picker 卡片、`Get`/`DONE` 状态和脱敏原生执行进度;模型仍不能提交任意 card JSON,也不能直接控制 reaction 或进度载荷。
|
|
312
|
-
- 仅图片资源具备受限下载能力,而且只有部署同时提供 Delivery 图片桥接与 AttachmentStore、当前模型明确声明图片输入能力时才会启用。文件、音频、视频和 sticker 仍只进入 durable metadata quarantine;本插件不做病毒扫描,也不提供附件出站上传。
|
|
313
|
-
- 消息编辑和上传尚未实现;未来也必须先创建 Delivery 的持久 operation,不得从模型直接调用 SDK。
|
|
314
|
-
- 单个飞书应用的长连接是集群竞争消费,不提供广播或多节点 exactly-once。当前 suite 的可靠性目标是受 supervisor 管理的单机进程。
|
|
315
|
-
- setup wizard 在 macOS 和 Linux 受支持,Windows 为 best-effort;它需要交互式终端和一次 owner 私聊,不接受 `--app-secret` 一类参数。一键模式会在用户扫码确认后通过飞书官方流程选择或创建应用;企业管理员审批等租户控制面仍由飞书强制执行。
|
|
316
|
-
|
|
317
|
-
## 常见问题
|
|
318
|
-
|
|
319
|
-
- **提示凭据或 bot identity 错误:**重新核对 App ID/Secret、`feishu`/`lark` 域和机器人能力;重新运行向导会更新同一 Keychain 条目。
|
|
320
|
-
- **一键创建链接过期或被拒绝:**重新运行 `dsh-lark-setup --profile web --create-app`;链接仅在页面显示的期限内有效,且只能由一位用户确认。
|
|
321
|
-
- **长连接成功但一直等不到短语:**确认事件接收方式是长连接、已添加 `im.message.receive_v1`、应用版本已发布且你在可用范围内,并且是私聊机器人原样发送短语。
|
|
322
|
-
- **`assistant_health` 显示 `disabled`:**向导尚未成功写入 profile,或常驻服务仍在运行旧配置;重新执行 `--install-service`。
|
|
323
|
-
- **`launchd bootstrap failed: Bootstrap failed: 5: Input/output error`:**新版安装器会检查稳定 job 状态并对这个 macOS 瞬时竞态做有限重试;先重新 build 本项目的 `lark-channel`,再执行 `dsh-lark-setup --profile web --install-service`。若仍失败,用 `plutil -lint ~/Library/LaunchAgents/ai.deepseek.dsh.profile.web.plist` 校验配置,并查看 `~/.dsh/logs/web-host.error.log`。
|
|
324
|
-
- **`caller is not allowlisted for this credential handle`:**这是旧版默认导出丢失稳定 Cordis plugin identity 导致的错误;更新并重新 build `lark-channel`,再执行 `--install-service`,不要把 credential consumer allowlist 改宽。
|
|
325
|
-
- **显示 `connected-with-gap`:**连接曾中断;飞书没有可持久化 replay cursor,检查这段时间是否有漏处理消息。
|
|
326
|
-
- **收到消息但无回复:**先发送 `/model`,选择一个目录中可用且已登录的 route;再看 `assistant_health`。未授权身份会 fail-closed,不会自动成为 owner。
|
|
327
|
-
- **能回复但提示技能/插件工具被拒绝:**先确认对应技能或插件已经安装并挂载到当前 profile,再升级并重启 `assistant-delivery`,确认 session 的 preset 在 create/resume 时已挂载;需要执行时运行 `~/.dsh/profiles/web/node_modules/.bin/dsh-lark-setup --profile web --refresh-agent-policy --allow-agent-tools`,或手工添加 exact preset/workspace/initiator 的 capability 与工具 Policy。刷新命令不会重启服务;只执行 `--install-service` 会重启,不会改能力可达性规则。
|
|
328
|
-
- **没有弹出审批卡,却反复显示 `the user rejected tool`:**旧 native full session 可能只有 `danger-full-access + never` 而缺少 AssistantPolicy reviewer,导致 reviewer 保守回落为 `user`、工具进入 `ask-review`,又被 `approval=never` 在展示前自动拒绝;这不代表用户点过“拒绝”。升级构建并重启 `assistant-policy` 所在 profile;AssistantPolicy 会在已有 session 扫描、新建和执行门前全局迁移 Web/direct 与 Delivery 的精确 legacy full 状态,持久补齐 `reviewer=none`,Delivery create/resume 另做提前检查。非 full 的 `never` 状态会返回 `[approval-disabled] ... no user approval was requested`,不再错误归因。若随后出现 `default-deny`,运行 `~/.dsh/profiles/web/node_modules/.bin/dsh-lark-setup --profile web --refresh-agent-policy --allow-agent-tools` 刷新 foreground/Delivery capability 可达性规则;不要把 `run_code` 在 ask/auto 档无条件白名单化,它仍是 bash-equivalent 执行面。
|
|
329
|
-
- **权限卡片未显示或点击后提示失效:**卡片只在 active owner 私聊中提供,并绑定原 chat、原卡 message id、binding/session、权限状态和 15 分钟默认有效期;群聊、过期卡、`/new` 后旧卡或期间已用文字切档都会退回/提示重发 `/permissions`。点击 Toast“已受理”后,以机器人随后发送的“已切换”回复为准;同一卡片想改选另一档时请重新打开。
|
|
330
|
-
- **有最终回复但没有 `Get` / `DONE`:**旧应用通常缺少 `im:message.reactions:write_only`。重新运行 `dsh-lark-setup --profile web --create-app`,在官方页面选择当前 App ID,确认权限增量并发布应用版本。reaction 失败不会阻断回答。
|
|
331
|
-
- **文字能回复但图片不能处理:**确认应用已获 `im:resource` 且新版本已发布,并确认 profile 已安装提供 `attachments` 服务的 AttachmentStore、当前 `/model` 路由明确支持图片输入。重新运行 `dsh-lark-setup --profile web --create-app --app-id <当前 App ID>` 可增量补齐飞书权限。下载拒绝路径型 key、重定向、超限内容、MIME/magic 不一致和非 PNG/JPEG/GIF/WebP 数据,这是预期的 fail-closed 行为。
|
|
332
|
-
- **看不到执行进度但能正常回答:**确认 profile 中 `showProgress: true`。原生进度接口是可降级能力,租户或应用类型不支持时最终回答仍会正常发送;查看 `assistant_health` 的 `larkChannel.lastErrorCode` 和 Host 错误日志定位权限/接口问题。
|
|
333
|
-
- **能看到模型卡片但下拉框不联动或确认后无回复:**确认应用已订阅 `card.action.trigger` 且回调方式是长连接;运行 `dsh-lark-setup --profile web --create-app --app-id <当前 App ID>` 会锁定该应用并以增量方式补齐回调。确认授权、发布应用版本、重启插件后,请重新发送一次 `/model`,不要继续使用升级前已打开的旧卡片。若实时目录已经变化或 effort 不受模型支持,机器人会要求重新发送 `/model`。
|
|
334
|
-
|
|
335
|
-
## 兼容性
|
|
336
|
-
|
|
337
|
-
- DeepSeek Harness:`>=0.1.0-rc.8 <0.2.0` 基线语义(通过 `assistant-delivery`)。
|
|
338
|
-
- `@dsh-enhanced/assistant-delivery`:`>=0.1.0 <0.2.0`。
|
|
339
|
-
- `@dsh-enhanced/credentials-keychain`:handle 模式为 `>=0.1.0 <0.2.0`;env fallback 不要求其激活。
|
|
340
|
-
- 官方 `@larksuiteoapi/node-sdk`:固定 `1.73.0`;使用 `WSClient`、`EventDispatcher`、`Client` 和 `normalize`,而不是高层 Channel 的内存 dedup/retry 状态机。
|
|
130
|
+
## License
|
|
341
131
|
|
|
342
|
-
|
|
132
|
+
MIT
|
package/bin/dsh-lark-setup.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { runLarkSetup } from '../lib/setup.js'
|
|
3
|
+
import { formatLarkSetupError, runLarkSetup } from '../lib/setup.js'
|
|
4
4
|
|
|
5
5
|
void runLarkSetup().catch(error => {
|
|
6
|
-
process.stderr.write(`${
|
|
6
|
+
process.stderr.write(`${formatLarkSetupError(error)}\n`)
|
|
7
7
|
process.exitCode = 1
|
|
8
8
|
})
|
package/cordis.patch.yml
CHANGED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# 会话、卡片、可靠性与排障
|
|
2
|
+
|
|
3
|
+
## 会话命令
|
|
4
|
+
|
|
5
|
+
同一私聊或稳定群聊 lane 会持续恢复同一个 DSH session。未知 slash 命令不会进入模型。
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
/status # 当前代次与上下文统计;/session 同义
|
|
9
|
+
/stop # 停止正在执行的任务,保留 session
|
|
10
|
+
/new # 停止旧任务并原子切换到空白 session;/clear 同义
|
|
11
|
+
/compact # 宿主提供原生命令时压缩当前上下文
|
|
12
|
+
/model # 打开模型选择卡片
|
|
13
|
+
/model use <route> # 使用 provider/model 文字 route
|
|
14
|
+
/model reset # 恢复部署默认模型
|
|
15
|
+
/permissions # 查看并选择 ask / auto / full
|
|
16
|
+
/permission ask
|
|
17
|
+
/permission auto
|
|
18
|
+
/permission full confirm
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`/new` 会轮换 session,但保留该聊天已经选择的模型和 effort。`/stop` 不会清空上下文。
|
|
22
|
+
|
|
23
|
+
## 模型选择
|
|
24
|
+
|
|
25
|
+
私聊机器人发送 `/model` 后,会收到 schema 2.0 卡片。依次选择“分组 / Provider”“模型”“Effort 程度”,再点击“确认选择”。选择 provider 时,同一卡片立刻刷新为该分组的模型;选择模型时再刷新为该模型真实支持的 effort。没有独立 effort 档位的模型只显示“默认(该模型无 effort 档位)”。
|
|
26
|
+
|
|
27
|
+
确认后,原卡先进入不可交互的验证态,后台完成校验后原位更新为成功或失败,并保留文字结果通知。成功选择从下一条普通消息生效,原上下文保留。目录来自当前 Host 实际注册的 provider/model,不消耗模型调用。
|
|
28
|
+
|
|
29
|
+
如果目录字段异常或卡片格式被飞书拒绝,机器人立即降级发送完整纯文本目录;可继续使用 `/model use <provider/model>`,不会静默重试后进入死信。
|
|
30
|
+
|
|
31
|
+
飞书的静态选择器彼此独立,插件因此分别为 provider、model 与 effort 注册签名 callback,并用长连接响应 `{ card: { type: "raw", data: ... } }` 重绘同一张卡片。固定版官方 SDK 会忽略 `type=card` 长连接帧,transport 在边界完成分片合并、callback 分发和 ACK 兼容桥。
|
|
32
|
+
|
|
33
|
+
每个级联状态都有持久 revision;旧卡回调只重绘当前权威状态,不能覆盖新选择。目录按 operation 保存在 Delivery SQLite,Host 短暂重启后仍可恢复。最终确认还绑定已经投递卡片的 provider message id:callback ACK 只表示受理,随后以同一 HTTP PATCH 链先锁定只读验证态,再在 durable settlement 完成后更新为只读终态。
|
|
34
|
+
|
|
35
|
+
adapter 与 Delivery 两层都会拒绝分组/模型错配,并校验目标模型的 effort 支持。选择“默认(由模型决定)”不会固定 reasoning effort。`/model` 是渠道无关的 Delivery 控制命令,不会交给当前失效的 LLM;即使旧默认 route 已不存在,也可以先切换后恢复会话。
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
/model
|
|
39
|
+
# 在卡片中选择三个下拉框并确认
|
|
40
|
+
你好,请介绍一下你自己
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
三个下拉的当前项通过 `initial_index` 定位。飞书的 `initial_option` 按展示文本而非回传 `value` 匹配,直接写 route 形态的 value 会静默回落到第一项;只有文本能唯一标识选项时才附加 `initial_option`。当前项不在列表中时两者都不下发,不伪造预选。升级后应重新发送 `/model`,旧 v1/v2 卡片不再生效。
|
|
44
|
+
|
|
45
|
+
## 权限选择卡片
|
|
46
|
+
|
|
47
|
+
`/permissions` 返回 ask、auto、full 三档卡片,当前档位带勾选,full 使用 danger 样式和飞书原生二次确认。卡片关闭转发互动,每个按钮携带独立 HMAC capability;token 绑定 channel/account/tenant/chat、精确 owner、binding/version/session、权限状态指纹、目标档位与 TTL。
|
|
48
|
+
|
|
49
|
+
adapter 先拒绝错人、跨 chat、篡改和过期,Delivery 再核对原 Outbox、provider message id、active owner/binding/session、Policy 与当前权限状态。通过后只把固定 `/permission` 命令写入同一 durable Inbox 串行路径,不在 adapter 内直接修改权限。旧卡、复制卡和晚到的相反选择不能覆盖新状态;卡片 4xx、缺少签名或 settlement 能力时退回完整文字说明。
|
|
50
|
+
|
|
51
|
+
权限卡只在 active owner 私聊中生效,并绑定原 chat、原卡 message id、binding/session、权限状态和默认 15 分钟 TTL。群聊、过期卡、`/new` 后旧卡或期间已经用文字切档都会失败关闭;请重新发送 `/permissions`。点击 Toast“已受理”后,以机器人随后发送的“已切换”回复为准。
|
|
52
|
+
|
|
53
|
+
## 入站与会话 lane
|
|
54
|
+
|
|
55
|
+
- 事件处理器等待 `assistant-delivery.acceptInbound()` 完成,即 Inbox 落盘后才向长连接 callback 返回成功。
|
|
56
|
+
- `messageId` 是稳定 inbound event id;Delivery 的 `(channel, account, eventId)` 唯一约束承担跨重启去重。
|
|
57
|
+
- 飞书 SDK 标准化生成的 `` 等标记会在进入 Delivery 前改写。纯图片正文保持为空,provider image key 只存在于隔离附件描述符,不会伪装成用户 prompt。
|
|
58
|
+
- 单聊按 account/tenant/user/chat 绑定。
|
|
59
|
+
- 群顶层消息没有 provider `root_id`,因此按发送者生成 `dsh-lark-top-sender/<sha256(open_id)>` 合成 thread。同群同发送者连续复用 session,不同发送者不落入同一 owner binding。
|
|
60
|
+
- 飞书提供真实 `root_id` 时直接使用可寻址的根消息 id,让不同回复串隔离。`dsh-lark-top-sender/` 是保留命名空间,provider root 命中该前缀时拒绝入站,避免两种 lane 碰撞。
|
|
61
|
+
- 合成 thread 只用于 Delivery 持久化,不作为飞书回复目标。当前消息仍回复真实 `messageId`;没有原消息可回复的后台发送则发到群顶层。
|
|
62
|
+
|
|
63
|
+
## 图片资源
|
|
64
|
+
|
|
65
|
+
图片只在 Delivery 再次确认 binding、Policy、目标模型图片能力和 AttachmentStore 可用后显式下载,不在 WebSocket callback 内下载。
|
|
66
|
+
|
|
67
|
+
adapter 只接受同一入站消息的严格 message ID/image key,并固定请求 `GET /open-apis/im/v1/messages/:message_id/resources/:file_key?type=image`;拒绝 URL、路径片段、重定向和非图片资源。`imageDownloadTimeoutMs` 默认 30 秒,可设为 1–120 秒;总截止时间覆盖 tenant token 与资源 GET,caller 取消、插件卸载或 credential lease 切换在 token cache miss 时也会及时返回。
|
|
68
|
+
|
|
69
|
+
调用方同时提供逐图 byte 上限;transport 在响应 header、流读取和 PNG/JPEG/GIF/WebP magic/MIME 三层校验。
|
|
70
|
+
|
|
71
|
+
## 出站与发送可靠性
|
|
72
|
+
|
|
73
|
+
- 只有合法、非重复且状态为 `queued` 的入站才异步添加 `Get`;未授权、死信或 provider 重放不重复 reaction。表情类型是大小写敏感的 `Get` 与 `DONE`。
|
|
74
|
+
- 最终回复携带原始飞书 `messageId`,先成功回复,再异步添加 `DONE`。Delivery 收到有效 provider message id 后立刻记录 `accepted`,不等待 presentation-only reaction。
|
|
75
|
+
- 发送失败、没有最终回复、任务失败或取消都不添加 `DONE`。reaction 失败只降级 channel health;首版保留 `Get`,不会为替换表情申请 reaction 读取权限。
|
|
76
|
+
- plain text 使用飞书文本消息;Markdown 使用 schema 2.0 卡片。请求携带由 Delivery idempotency key 单向哈希得到的 provider UUID。
|
|
77
|
+
- Agent 回答本身是 Markdown;若以 plain 发送会暴露表格分隔符、`**` 等语法。Delivery 只在 adapter 声明 `markdown` 能力时请求该格式,否则降级为 plain,避免 coordinator 以 `unsupported-format` 丢弃整条消息。
|
|
78
|
+
- Markdown 回答卡片不添加 header,避免与飞书气泡已有的机器人名称/头像重复;单一 `markdown` 组件承载正文,并用 `wide_screen_mode` 减少表格折行。
|
|
79
|
+
- 权限或格式错误是确定未发送;限流和未连接可以安全重试;网络超时或未知 SDK 错误进入 `unknown_after_send`,不会盲目重发。
|
|
80
|
+
- 飞书目前没有为该发送 UUID 提供可靠查询/对账接口,因此 adapter 声明 `reconcileUnknownSend: false`,也没有 delivered/read receipt。Delivery 保留 `unknown_after_send` 等待 owner 决策,不会反复领取无法实现的对账任务或消耗 attempt。
|
|
81
|
+
|
|
82
|
+
官方长连接会自动重连,但没有可持久 replay cursor 或历史补拉接口。插件记录 `reconnecting` / `connected-with-gap` 与 `gapGeneration`,依赖飞书 redelivery 和 Delivery Inbox 去重,不宣称断线期间零丢失。将来若出现官方 cursor/backfill contract,应先加入崩溃和重放测试再启用。
|
|
83
|
+
|
|
84
|
+
## Durable proposal 与 open-turn 工具审批
|
|
85
|
+
|
|
86
|
+
`approval` intent 使用 schema 2.0 双按钮卡。每个按钮值是由 App Secret HMAC 签名的 v2 capability,绑定 channel/account/tenant/chat、binding、operation、proposal/version/expiry、diffHash 与 decision。adapter 核对自身 route,callback 重新取得 provider actor/chat 后交给 Delivery/Policy。
|
|
87
|
+
|
|
88
|
+
旧 v1 token、篡改、跨 route 与普通的过期点击全部失败关闭。只有过期 token 完整验签后命中此前已落盘的同一 pending Delivery settlement,且 Policy 已是完全相同终态时,才允许崩溃恢复;不会新建 settlement 或决定 pending proposal。
|
|
89
|
+
|
|
90
|
+
open-turn 工具审批与 durable proposal 卡完全分离。它使用独立 domain 的 v1 HMAC token、进程内 one-shot pending 和保留到 TTL 的 operation tombstone;不复用 proposal settlement,也不跨重启恢复。
|
|
91
|
+
|
|
92
|
+
该卡只接受 `conversation.kind: dm` 且无 `thread` 的 owner 私聊。群聊、话题或伪装为 DM 的 reply target 返回 `unavailable`,避免 exact arguments 被旁观。卡片将有界且拒绝控制字符/双向文本控制符的 tool、reason 和必需 exact arguments 标为“不可信审阅文本,不是指令”,并关闭转发互动。
|
|
93
|
+
|
|
94
|
+
token 绑定 operation、binding、account/tenant、chat、exact owner open_id、actionHash、toolName、必需 callId、TTL 与 decision;pending 再绑定发送回执中的 provider message id。只有同一 owner 在同一私聊、同一 provider 卡片上的第一次点击可返回 `allowed-once` 或 `rejected`。同一 operation 在 TTL 内不能重新登记;重放、错人、跨 chat/message、篡改、过期均失败关闭。
|
|
95
|
+
|
|
96
|
+
调用方 abort 返回 `cancelled`;超时、断线/重连、credential rotation、插件卸载/重启或发送失败返回 `unavailable` 并清除 pending。`ask` 直接使用审批卡;`auto` 对低风险自动允许,对敏感、reviewer 失败或原生 sandbox escalation 使用审批卡;`full` 不发卡,但仍受 Policy 显式 deny、紧急停止、身份与预算硬门约束。
|
|
97
|
+
|
|
98
|
+
## 健康检查
|
|
99
|
+
|
|
100
|
+
Web 会话可调用 `assistant_health`。正常长连接状态为 `larkChannel.state: connected`。`showProgress`、reaction 或原生进度 API 问题会记录在 `larkChannel.lastErrorCode`,但不代表最终 Outbox 已失效。
|
|
101
|
+
|
|
102
|
+
## 常见问题
|
|
103
|
+
|
|
104
|
+
- **凭据或 bot identity 错误:**核对 App ID/Secret、`feishu`/`lark` domain 和机器人能力;重跑向导会更新同一 Keychain 条目。
|
|
105
|
+
- **一键创建链接过期或被拒绝:**重跑 `dsh-lark-setup --profile web --create-app`。链接只在页面显示的期限内有效,且只能由一位用户确认。
|
|
106
|
+
- **长连接成功但一直等不到短语:**确认事件接收方式是长连接、已经添加 `im.message.receive_v1`、应用版本已发布且你在可用范围内,并在机器人私聊原样发送短语。
|
|
107
|
+
- **`assistant_health` 显示 `disabled`:**向导尚未成功写入 profile,或常驻服务还在运行旧配置;执行 `--install-service`。
|
|
108
|
+
- **`launchd bootstrap failed: Bootstrap failed: 5: Input/output error`:**新版会检查稳定 job 状态并有限重试 macOS 瞬时竞态。先重新 build 本包,再执行 `dsh-lark-setup --profile web --install-service`;仍失败时用 `plutil -lint ~/Library/LaunchAgents/ai.deepseek.dsh.profile.web.plist` 校验,并查看 `~/.dsh/logs/web-host.error.log`。
|
|
109
|
+
- **`caller is not allowlisted for this credential handle`:**旧版默认导出可能丢失稳定 Cordis plugin identity。更新并重新 build,再执行 `--install-service`;不要放宽 credential consumer allowlist。
|
|
110
|
+
- **显示 `connected-with-gap`:**连接曾中断。飞书没有持久 replay cursor,应检查断线期间是否漏处理消息。
|
|
111
|
+
- **收到消息但无回复:**先发送 `/model`,选择目录中可用且已登录的 route,再查看 `assistant_health`。未授权身份会 fail closed,不会自动成为 owner。
|
|
112
|
+
- **工具或技能被拒绝:**先确认对应技能/插件已安装并挂载到 profile,并升级重启 `assistant-delivery`,确认 session 的 preset 在 create/resume 时已挂载。需要执行时运行 `dsh-lark-setup --profile web --refresh-agent-policy --allow-agent-tools`,或手工添加 exact preset/workspace/initiator 的 capability 与工具规则。refresh 不重启服务;`--install-service` 只重启,不改能力规则。
|
|
113
|
+
- **没有审批卡却反复显示 `the user rejected tool`:**旧 native full session 可能只有 `danger-full-access + never`,缺少 reviewer,导致保守回落到 user、工具进入 ask-review,随后被 `approval=never` 自动拒绝。这不表示用户点过拒绝。升级构建并重启 `assistant-policy` 所在 profile;它会为 legacy full 状态持久补齐 `reviewer=none`。非 full 的 never 状态会返回 `[approval-disabled] ... no user approval was requested`。若随后出现 `default-deny`,刷新 Agent Policy。不要在 ask/auto 中无条件允许 `run_code`,它仍是 bash-equivalent 执行面。
|
|
114
|
+
- **权限卡不显示或点击失效:**卡片仅用于 active owner 私聊,并绑定原 chat/card、binding/session、权限状态和 15 分钟默认 TTL。群聊、过期、`/new` 后旧卡或已经文字切档都会失效;重新发送 `/permissions`。
|
|
115
|
+
- **最终回复正常但没有 `Get` / `DONE`:**旧应用通常缺少 `im:message.reactions:write_only`。重跑一键向导,选择当前 App ID,确认权限增量并发布新版本。reaction 失败不会阻断回答。
|
|
116
|
+
- **文字能回复但图片不能处理:**确认应用已有 `im:resource` 且发布新版本,profile 安装了 AttachmentStore,当前 route 声明图片输入。可用 `dsh-lark-setup --profile web --create-app --app-id <App ID>` 增量补权限。路径型 key、重定向、超限、MIME/magic 不一致或非 PNG/JPEG/GIF/WebP 会按预期 fail closed。
|
|
117
|
+
- **看不到进度但能回答:**确认 `showProgress: true`。`message_cot` 是可降级能力;检查 `assistant_health` 的 `larkChannel.lastErrorCode` 和 Host 错误日志。私聊详情还要求 `progressDetails: direct`;群聊始终只显示状态。
|
|
118
|
+
- **模型卡片下拉不联动或确认后无回复:**确认订阅 `card.action.trigger` 且回调走长连接;用一键向导锁定当前应用并增量补齐回调,发布、重启后重新发送 `/model`,不要继续使用升级前旧卡。实时目录变化或 effort 不受支持时,机器人会要求重新打开。
|