dsh-plugin-lark
English | 简体中文
这是一个面向 DeepSeek Harness 的飞书/Lark 长连接桥接插件。收到的文本会转为 Agent follow-up;每轮对话、工具生命周期、审批过程和结构化问题都会通过 Card 2.0 返回到原始会话。
功能概览
- 无需入站公网地址: 通过官方 SDK 的 WebSocket 长连接接收飞书/Lark 事件。
- 隔离且可恢复的会话: 私聊、群聊回复树和原生话题分别使用独立的持久化 Harness 会话;需要时也可显式绑定到一个全局共享会话。
- 有界会话导航: 在精确会话范围内用已存标题、时间、项目标签和不透明引用列出合格历史,并原子恢复所选 transcript;不接受原始 Session ID 或路径。
- 项目注册与按会话选择: 项目管理员可在私聊中注册当前 Session 目录或移除注册;所有已授权会话都能列出和选择已注册 Workspace,聊天参数不能指定任意路径。
- 按会话选择模型: 列出已挂载 provider 及其公布的模型,接受 adapter 可解析的精确 provider/model 路由,并在新 generation 与恢复过程中保留每个会话的选择。
- 图片历史路由安全: 只按精确压缩后的模型可见 surface 检测图片,阻止模型切换、Session 恢复或普通 prompt 把这段历史发送给纯文本或能力未知路由。
- 可选私聊图片: 严格验证一个有界的静态 PNG 或 JPEG,通过 Harness 附件服务持久化,并只向明确支持图片的模型提交内容寻址引用。
- 结构化人工输入: 把官方
ask_user_question工具渲染为有界的原生单选、多选或自由文本卡片,并把已授权回答返回同一个运行中 turn。 - 可选私聊文本附件: 通过严格的鉴权、文件名、MIME、字节数与内容检查,接收一个有界的 UTF-8
.txt、.log、.patch或.diff消息,不接受 URL,也不创建临时文件。 - 经审批的 Workspace 产物发送: 提供默认关闭的 Agent 作用域工具;只有原 Lark 用户批准精确实时 turn 后,才能发送一个有界文本文件或静态 PNG/JPEG。
- 可靠的主动通知: 把一条完成或关注卡片受理到本轮已经注册的会话,并通过持久化发件箱保证重启既不丢也不重复投递已受理通知。
- 实时执行卡片: 将思考过程、待办、重试、上下文压缩、Hook、工作流、工具调用与结果、Token 用量和最终答案持续更新到一张有大小上限的 Card 2.0 卡片中;服务停机打断执行时会有界尝试移除失效的实时控件。
- 安全的工具审批与停止: 审批和停止操作绑定到发起它们的会话、聊天和用户;过期或跨聊天操作默认拒绝。
- 可靠的回复投递: 卡片及降级文本始终回复触发消息或原生话题;长答案会完整续发,并通过持久化回执避免常规 WebSocket 重投造成重复执行。
- 有界的进程内驻留: 释放已完成持久化检查点的最近最少使用空闲 Agent;再次访问时精确冷恢复原会话,且不会删除历史记录。
- 本地化与可观测性: 内置
zh-CN、en-US界面文案,并可选提供脱敏的 WebSocket readiness 接口。 - 运维状态与诊断:
/status和/diag用 Card 2.0 向运维展示版本、运行时间、连接、会话范围、项目、模型与工作状态,以及脱敏修复建议,不含平台 ID 或秘密。 - 会话级策略: 运维可以为单个聊天或群单独收窄额外授权用户、提及要求、可见 Workspace、可选模型,以及允许的审批或出站工具类别。本地规则只与全局默认拒绝配置取交集。
- 文档交接(可选): 只读取用户在会话里给出的文档链接,内容有界并标注为不可信数据;用户明确要求时把长报告发布成云文档,同时照常投递聊天答复。需要独立的飞书权限,默认关闭。
- 显式并行任务(可选):
/task run启动一个有界任务,拥有独立会话、不透明编号、回复目标与生命周期卡片。普通的连续消息仍然串行,绝不会被重新解释为并行工作;未显式配置共享时,两个存活任务也不能同时占用同一个项目。 - 可选的运行时监管: 连接之前先跨进程认领该机器人的归属,并发布一份外部探针无需加载 Harness profile 就能读取的状态文档,同时提供可审阅的 systemd 与就绪检查模板。
- 失败时默认拒绝: 授权默认拒绝、Lark 应用凭据仅允许来自启动环境;媒体摄入必须显式开启且全程有界,审批失败也绝不会放行。
稳定性
1.0 冻结两份契约。公开配置面就是上面那 31 个选项:后续 1.x 可以新增可选项,删除或改名属于破坏性变更,需要主版本号。持久化 storage domain 为 lark_conversations、lark_inbound、lark_notify、lark_policy、lark_tasks,均为 domain version 0;提升版本必须先有迁移路径和 [UPGRADING.md](./UPGRADING.md) 条目才能发布。两者都有发布门禁强制校验,偏离会直接让构建失败,而不是流到部署里。
冻结这两份契约,不等于声称每条部署路径都验证过。有两块仍在本项目的证据范围之外:
- 带凭据的 Web profile 启动。 发布门禁会把打好的候选包全新安装并升级进隔离的标准 profile 并校验组合结果,但不会用真实飞书/Lark 凭据启动应用。带凭据启动、对真实平台的 WebSocket readiness、以及带凭据升级过程中的持久状态迁移,只有 [SMOKE_TESTS.md](./SMOKE_TESTS.md) 里的人工清单覆盖。
- 长时间运行的资源表现。 没有 soak 测试。连续运行数天的内存、句柄、持久化存储增长都没有测量过。有界驻留、发件箱、任务上限本身有强制约束和单元测试,但它们在长时间在线下的表现没有证据支撑。
请把这两项当作未验证,而不是"应该没问题"。如果要把 1.0 放进长期运行的生产通道,先跑一遍带凭据的冒烟清单,并自行观察资源占用。
环境要求
- Node.js 22.x,或在插件 v0.8.5 及更高版本中使用 Node.js 24.x
- 一组版本一致的 DeepSeek Harness
0.1.0-rc.7软件包 - Harness
agents与sessions服务;标准 Web profile 已挂载两者 - 结构化 Lark 输入还需要 Harness
tools、Session 持久化与兼容的 rc.6ask_user_question定义;标准 Web profile 已挂载它们 - 持久化的
storageDomain服务;标准 Web profile 已提供基于 JSON 的完整存储栈 - 会话导航还要求
sessionPersistence、sessionQuery与workspaceRegistry;标准 rc.6 Web profile 已提供这些服务 - 入站图片还要求 Harness
attachments服务;标准 rc.6 Web profile 已提供本地内容寻址存储 - 出站产物还要求
sessionPersistence、workspaceRegistry、approval、图片所需的attachments,以及标准本地文件系统 Workspace runtime - 一个带机器人的飞书或 Lark 自建应用
支持的 Harness 兼容矩阵
下表中的支持状态只对应经过发布门禁验证的精确基线;某个版本仅仅满足宽泛的 semver 范围,并不代表该组合已受支持。
| 插件版本 | DeepSeek Harness 版本组 | 宿主库 | Node.js | 验证状态 |
|---|---|---|---|---|
1.1.0–1.1.x | 所有已解析的 @deepseek-ai/dsh-* 软件包均为 0.1.0-rc.7 | Cordis 4.0.1;Schemastery 3.18.1 | 22.x;24.x | 与 1.0.x 行相同的 Linux 与 macOS package/runtime 门禁,在 rc.7 版本组上重新跑过。被冻结的配置面与持久化 domain 没有变化。带凭据的 Web profile 启动与长时间运行的资源表现仍未验证——见[稳定性](#稳定性)。 |
1.0.0–1.0.x | 所有已解析的 @deepseek-ai/dsh-* 软件包均为 0.1.0-rc.6 | Cordis 4.0.1;Schemastery 3.18.1 | 22.x;24.x | 与 0.9.x 行相同的门禁;1.0 冻结公开配置面与持久化 storage domain,而不是扩大这张矩阵。带凭据的 Web profile 启动与长时间运行的资源表现仍未验证——见[稳定性](#稳定性)。 |
0.9.0–0.9.x | 所有已解析的 @deepseek-ai/dsh-* 软件包均为 0.1.0-rc.6 | Cordis 4.0.1;Schemastery 3.18.1 | 22.x;24.x | 沿用 v0.8.7 的 Linux 与 macOS package/runtime 门禁。v0.9.0 增加真实 rc.6 Workspace Registry 生命周期测试;v0.9.1 增加 owner context 服务依赖与首条命令冷恢复覆盖;v0.9.2 修正飞书 Card 2.0 元素兼容性并脱敏分类 SDK 失败;v0.9.3 增加有界精确范围 Session 导航;v0.9.4 增加直接 Native 结构化人工输入;v0.9.5 让 Cordis 真正拥有异步 disposer 并限制终态 Card 的停机预算;v0.9.6 增加可选、有界的入站 UTF-8 文本文件;v0.9.7 让模型与 Session 路由对图片历史默认拒绝不兼容目标;v0.9.8 在优雅停机时终态化已知的运行中执行卡;v0.9.9 增加可选、有界的静态入站图片;v0.9.10 在受支持的 Linux descriptor 边界上增加经审批的 Workspace 产物发送,其他平台失败关闭;v0.9.11 增加对已注册会话的可靠主动通知;v0.9.12 让同一进程内后续受理与退避重试继续排空发件箱;v0.9.13 增加运维 /status 与 /diag;v0.9.14 增加会话级策略;v0.9.15 让卡片回调也走同一策略,并且不再因为缺少健康探针就判定机器人正常;v0.9.16 增加可选的运行时监管与跨进程通道归属;v0.9.17 增加显式的有界并行任务;v0.9.18 让无法解析的归属记录失败关闭;v0.9.19 增加可选的文档交接;v0.9.20 对照代码全量复核随包文档。 |
0.8.7–0.8.x | 所有已解析的 @deepseek-ai/dsh-* 软件包均为 0.1.0-rc.6 | Cordis 4.0.1;Schemastery 3.18.1 | 22.x;24.x | 支持 GitHub 托管的 Ubuntu x64。Node 22 生成 canonical archive;Node 22 与 24 都执行相邻版本升级 profile 门禁。GitHub 托管的 macOS 26 arm64 还验证 Node 22 和 24 的 package/runtime 兼容性,但不验证 Web profile 部署。 |
0.8.6 | 所有已解析的 @deepseek-ai/dsh-* 软件包均为 0.1.0-rc.6 | Cordis 4.0.1;Schemastery 3.18.1 | 22.x;24.x | Ubuntu 支持范围相同;macOS 26 arm64 的 package/runtime 证据只覆盖 Node 22。 |
0.8.5 | 所有已解析的 @deepseek-ai/dsh-* 软件包均为 0.1.0-rc.6 | Cordis 4.0.1;Schemastery 3.18.1 | 22.x;24.x | 支持 GitHub 托管的 Ubuntu x64。Node 22 执行 canonical Release 与相邻版本升级门禁;Node 24 重跑源码/Harness 和 packed-consumer 门禁,再把同一份 canonical archive 全新安装到标准 rc.6 Web profile。 |
0.8.0–0.8.4 | 所有已解析的 @deepseek-ai/dsh-* 软件包均为 0.1.0-rc.6 | Cordis 4.0.1;Schemastery 3.18.1 | 22.x | 支持原有 Node 22/Linux 基线;v0.8.4 新增不启动应用的 Web profile package lifecycle 门禁。 |
必需测试会组装真实的 rc.6 Cordis、Agent、Agent Loop、LLM、Session、语义 checkpoint policy、Session Title、SQLite Session Query 精确读取路径、JSONL 持久化、JSON storage-domain、本地 Attachment Store、Tools、User Questions、Approval 与 Workspace 服务;平台连接、模型 provider 和浏览器行为使用受控替身,项目变更和经审批产物投递另有真实 Registry/持久化生命周期测试。CI 把官方 Lark SDK 精确固定为 1.73.0,在 Node 22 上打出 canonical 候选包,把它全新安装到隔离的标准 rc.6 Web profile,并把第二个隔离 profile 从经过严格验证的 v0.9.12 Release package 升级到候选版本,同时保持用户 patch 不变。两条路径都必须匹配已安装 package 版本、唯一 bundle 注册和唯一组合后的 Lark 配置层。
Profile 门禁还会把 npm 解析固定在 rc.7 版本组发布完成后的 registry 时间快照。Harness 预发布包内部使用 caret 范围,因此只把顶层写成精确 dsh@0.1.0-rc.6,在全新 npm-exec 环境中仍可能漂移到更晚的预发布版本;门禁仍会逐一要求所有已解析 DSH 包精确为 rc.6。
从 v0.8.5 起,同一个 Linux Release 门禁随后会切到 Node 24,以 engine-strict 重新创建 node_modules,重跑完整源码/Harness 和独立 packed-consumer 门禁,再在隔离的标准 profile 中消费前面已经打好的同一份 canonical 候选包。v0.8.5 的基线 v0.8.4 只支持 Node 22,因此当时执行的是全新安装;从 v0.8.6 起,Node 24 还会验证从已经兼容的 v0.8.5 基线相邻升级。
从 v0.8.6 起,另一个必需门禁会在 GitHub 托管的 macOS 26 arm64 上运行 engine-strict Node 22;从 v0.8.7 起,同一隔离流程同时覆盖 Node 22 与 24。每条 runtime 都会重跑完整源码/Harness 测试、audit 和独立 packed-consumer 安装,然后在 Actions artifact digest 校验后下载并消费由 Ubuntu 生成的同一份 canonical archive。两条 runtime 都不会在 macOS 上执行 dsh plugin、组合标准 Web profile,也不验证应用启动和有状态操作。
该 Web profile 门禁刻意不启动应用:它验证 package 安装、升级、bundle 解析与配置组合,但不会启动 Web app,也不覆盖凭据、SDK WebSocket 连接、/api/lark/health、飞书/Lark 网络链路或持久化状态迁移;这些仍属于部署和真实凭据冒烟检查。
插件会把直接宿主 peer 固定在这组基线上,解析图中的所有 DSH 软件包也必须来自同一个 rc.6 版本组。混用 DSH 版本、Node.js 23.x 或 25 及更高版本、在 v0.8.4 及更早插件上使用 Node.js 24、更高版本的 Cordis 或 Schemastery、其他 Harness 版本组、Ubuntu x64 以外的 Ubuntu 架构,以及完全缺少可选 Approval 服务的宿主都尚未验证。从 v0.8.7 起,macOS 证据仅限 macOS 26 arm64/Node 22 或 24 的 package/runtime 消费;Intel Mac、其他 macOS 版本、标准 Web profile 运行和状态迁移仍未验证。替代持久化栈也未验证。自定义 profile 只有在提供[配置](#配置)章节所述服务时才受支持;缺少 agents、sessions、tools 或持久化 storageDomain 明确不受支持。
安装
把已发布的包装进 Harness profile:
dsh plugin --profile web add dsh-plugin-lark或者从检出目录构建——发布门禁验证的正是这条路径:
git clone https://github.com/LPX-E5BD8/dsh-plugin-lark.git
cd dsh-plugin-lark
npm ci --ignore-scripts
npm run build
dsh plugin --profile web add .本 README 中的 dsh plugin 安装与运维流程仍只由 Ubuntu/Linux 门禁验证。macOS 门禁只验证打包模块,不代表标准 Web profile 部署已受支持。
同一版本的 registry 包与 GitHub Release 归档是同一份产物:发布门禁只打包一次,以独立消费者身份验证它,然后发布这份完全相同的归档。使用前如何校验见 [Release 来源证明](#release-来源证明)。如果改用检出目录安装,则 profile 使用期间请保留该目录。
替换该检出目录或回滚带持久化状态的版本前,请遵循 [UPGRADING.zh-CN.md](./UPGRADING.zh-CN.md) 中的冷备份流程和 schema 边界。插件代码降级并不等于持久化状态可以自动降级。
在飞书/Lark 开发者后台中:
1. 选择以长连接接收事件。 2. 订阅 im.message.receive_v1。 3. 注册 card.action.trigger 回调。 4. 为机器人授予 im:message 消息收发权限。 5. 开启 inboundTextFiles、inboundImages 或 outboundArtifacts 时必须授予 im:resource。这些功能都未开启时该权限仍为可选,仅用于启用内置动态加载图;缺少它时卡片会使用静态图标。
Release 来源证明
从 v0.8.3 开始,每个 GitHub Release 都会包含通过 packed-consumer 冒烟测试的同一份 npm 格式 .tgz,以及 GitHub 托管、针对该文件生成的 SLSA build provenance attestation。本工作流不会发布到 npm registry;GitHub 自动生成的 Source code 压缩包也不是被证明的 package。
可以使用 GitHub CLI 下载并验证 Release package:
set -eu
version='1.1.1'
repository='LPX-E5BD8/dsh-plugin-lark'
archive="dsh-plugin-lark-${version}.tgz"
tag="v${version}"
tag_object="$(gh api "repos/${repository}/git/ref/tags/${tag}" --jq '.object.type + ":" + .object.sha')"
object_type="${tag_object%%:*}"
object_sha="${tag_object#*:}"
if [ "$object_type" != 'tag' ]; then
printf 'remote %s is not an annotated tag\n' "$tag" >&2
exit 1
fi
peel_depth=0
while [ "$object_type" = 'tag' ]; do
peel_depth=$((peel_depth + 1))
if [ "$peel_depth" -gt 8 ]; then
printf 'remote %s exceeds the tag peel limit\n' "$tag" >&2
exit 1
fi
tag_object="$(gh api "repos/${repository}/git/tags/${object_sha}" --jq '.object.type + ":" + .object.sha')"
object_type="${tag_object%%:*}"
object_sha="${tag_object#*:}"
done
if [ "$object_type" != 'commit' ]; then
printf 'remote %s resolves to %s, not a commit\n' "$tag" "$object_type" >&2
exit 1
fi
tag_commit="$object_sha"
release_target="$(gh release view "$tag" --repo "$repository" --json targetCommitish --jq .targetCommitish)"
if [ "$release_target" != "$tag_commit" ]; then
printf 'release target %s does not match tag commit %s\n' "$release_target" "$tag_commit" >&2
exit 1
fi
gh release download "$tag" --repo "$repository" --pattern "$archive"
gh attestation verify "$archive" \
--repo "$repository" \
--signer-workflow "$repository/.github/workflows/ci.yml" \
--source-ref refs/heads/main \
--source-digest "$tag_commit" \
--deny-self-hosted-runners该 attestation 会把压缩包 digest 绑定到本仓库、workflow、ref 与 Release commit。它证明来源和完整性,并不表示代码或依赖一定没有漏洞。
运行
请从 Lark Agent 要操作的目标项目目录启动 DSH:
cd /path/to/target-project
export DSH_LARK_APP_ID='<app-id>'
export DSH_LARK_APP_SECRET='<app-secret>'
dsh --profile web --host 127.0.0.1 --port 3080启动目录会成为每个新 Lark 会话的 workspace;持久化会话恢复时则沿用其已存储的 workspace。/project register <名称> 可以注册当前 Session 目录,/project 可以把单个会话切换到任意已注册 Workspace。是否让 Web UI 监听非回环地址属于具体部署配置;飞书/Lark 事件本身通过出站长连接投递,不需要入站公网监听器。
凭据
插件只从环境变量读取应用凭据,不接受在插件配置中写入凭据。
export DSH_LARK_APP_ID='<app-id>'
export DSH_LARK_APP_SECRET='<app-secret>'这些 DSH_* 值必须由 DSH 启动进程继承。DSH 0.1.0-rc.7 会拒绝调用目录 .env 和 $DSH_HOME/.env 中的所有 DSH_* 项;请在启动 shell 中 export,或通过服务管理器/容器环境注入。为兼容已有部署,FEISHU_APP_SECRET 仍可作为仅限启动环境的后备项。
模型凭据属于 Harness provider,不属于本插件。使用默认 provider 时,推荐通过 Web profile 的 Models 页面配置;也可以在权限为 0600 的 $DSH_HOME/.credentials.yaml 中保存以下映射:
DEEPSEEK_API_KEY: <provider-api-key>如需仅覆盖本次运行,请在启动 DSH 前导出:
export DEEPSEEK_API_KEY='<provider-api-key>'标准 Web profile 每次请求按以下顺序解析该密钥:启动进程继承环境、受管 .credentials.yaml、调用目录 .env、$DSH_HOME/.env。后两个 .env 层可作为该 provider key 的低优先级后备,但所有包含密钥的文件都必须保持未跟踪状态。绝不要把解析后的密钥写入 cordis.patch.yml 或提交到仓库。
可重复执行的飞书和 Lark 凭据冒烟测试见 [SMOKE_TESTS.md](./SMOKE_TESTS.md)。
配置
仓库内置 Cordis patch 的默认值如下:
- id: lark
name: dsh-plugin-lark
config:
domain: feishu # feishu / lark
locale: zh-CN # zh-CN / en-US
# 鉴权。所有名单默认拒绝且默认为空。
allowAllUsers: false
allowFrom: [] # 已授权的飞书/Lark open_id
projectManageFrom: [] # 允许在私聊中注册/移除项目的 open_id
operatorFrom: [] # 允许使用 /status、/diag 和 /policy 的 open_id
# 会话路由。
defaultSessionId: '' # 留空 = 按私聊/群会话分别隔离
provider: deepseek-official # 会话没有保存选择时的默认值
model: deepseek-v4-flash # 会话没有保存选择时的默认值
streamUpdateIntervalMs: 1000
maxConversationHandles: 32 # 稳态常驻会话句柄目标值
# 入站媒体。按类型分别开启,每项都有上限。
inboundTextFiles: false # 开启有界 UTF-8 文本文件消息
maxInboundTextFileBytes: 131072 # 默认 128 KiB;硬上限 256 KiB
inboundImages: false # 开启单张静态 PNG/JPEG 私聊图片
maxInboundImageBytes: 5242880 # 默认/硬上限 5 MiB
maxInboundImagePixels: 20000000 # 默认/硬上限 2000 万像素
maxConversationImages: 4 # 默认 4;硬上限 20
maxConversationImageBytes: 20971520 # 默认/硬上限 20 MiB
# 出站产物。仅 Linux,且必须经过审批。
outboundArtifacts: false # 开启经审批的 Agent 作用域发送工具
maxOutboundTextFileBytes: 131072 # 默认 128 KiB;硬上限 256 KiB
maxOutboundImageBytes: 5242880 # 默认/硬上限 5 MiB
maxOutboundImagePixels: 20000000 # 默认/硬上限 2000 万像素
# 主动通知。
proactiveDelivery: false # 开启 Agent 作用域通知工具
# 运行时监管。runtimeDir 留空即关闭。
runtimeDir: '' # 绝对路径,设置后开启监管
runtimeOwnerTtlMs: 30000 # 归属心跳预算
# 显式并行任务。
parallelTasks: false # 开启 /task
maxParallelTasks: 2 # 单个会话同时存活的任务数上限
taskWorkspaces: exclusive # 或 shared,允许多个任务共用一个项目
# 文档交接。需要独立的飞书权限。
documentHandoff: false # 开启文档读取与发布工具
maxDocumentReadBytes: 65536 # 默认 64 KiB;硬上限 512 KiB
maxDocumentPublishBytes: 262144 # 默认 256 KiB;硬上限 1 MiB这组基线要求宿主提供 agents、sessions、tools 和持久化 storageDomain 服务。需要持久化的重置、项目/会话/模型选择、冷恢复与结构化 Lark 问题还要求 sessionPersistence;/project 依赖 workspaceRegistry,/session 还依赖 sessionQuery 与持久化会话绑定,/model 依赖 Harness llm 服务。含图片的 Session 还要求该服务公开精确 resolveModelInfo modality 元数据;摄入图片还要求兼容的 attachments 服务。任一能力缺失时图片工作默认拒绝,但纯文本 Session 不受影响。结构化输入要求 Agent 仍能看到精确兼容的 rc.6 ask_user_question 定义;缺失或不兼容时会记录诊断并委派,而不会注册第二个 provider。缺少 Session Query 或 Workspace 能力时,会话导航会返回不可用;单独执行 /session 列表不会创建 Agent。审批卡片和 readiness 路由分别依赖可选的 approval 与 webServer 服务。已验证矩阵使用标准 JSON/JSONL、本地附件和 SQLite 精确读取实现,替代实现仍未验证。
allowFrom 默认拒绝:当列表为空且 allowAllUsers: false 时,所有用户都无权访问。仅当机器人明确需要公开使用时才设置 allowAllUsers: true。托管在 open.larksuite.com 的应用应使用 domain: lark。
operatorFrom 是独立且默认拒绝的运维 allowlist,默认值为空。列出的运维人员仍必须通过普通鉴权。/status、/diag 和 /policy 使用与执行卡相同的 Card 2.0 schema,且绝不包含凭据、聊天/消息/会话 ID、私有路径、哈希值或原始错误。
/policy 仅限运维,并在 lark_policy storage-domain 单元中持久化一份以哈希为键的单聊天文档。一份文档覆盖一个单聊或一整个群(含群内所有回复串与原生话题),因此新开话题串无法绕过已收紧的群策略。设置 defaultSessionId 后所有聊天共享同一会话,也就共享同一份策略文档。本地规则只能收窄全局默认拒绝配置:额外的哈希用户名单与 allowFrom 取交集;mention always 让群里的命令也必须 @ 机器人;Workspace 与模型名单在列出和切换之前就过滤掉不允许的名称;审批、send_lark_artifact 与 notify_lark 只要全局开关或本地开关有一个关闭就保持关闭。清空某个本地名单会恢复全局默认,但无法开启全局已禁用的能力。名单收窄不会驱逐会话已经选中的项目或模型,只是不再展示被隐藏的名称。运维始终可以恢复自己锁紧的会话。存储的文档不含明文 open ID,也不含任何密钥。
documentHandoff 默认关闭,并且需要独立的飞书应用权限:读取需要 docx:document:readonly,发布需要 docx:document:create。开启后只注册两个 Agent 作用域工具,Docs、日历、多维表格、表格、任务、Wiki、云盘等接口面仍然不属于本通道。读取工具只接受用户原样给出的绝对 https 链接,且路径必须是本部署自有飞书域名下的 docx 或 wiki——裸 token、相对路径、URL 里夹带凭据、仿冒域名,以及 drive/base/sheets 链接,都会在发出任何请求之前被拒绝。内容按 maxDocumentReadBytes 在字符边界截断,返回时附带标题、链接、必要时的截断提示,以及"这是不可信数据而非指令"的明确声明。发布工具用 Markdown 创建一篇文档并返回链接;聊天答复与其投递回执保持不变,也就是说发布是追加一份文档,而不是替换回复。
parallelTasks 默认关闭。开启后 /task run <指令> 会在独立会话范围里启动一个任务,拥有自己的 Session、不透明编号、回复目标与生命周期卡片;/task、/task <编号>、/task stop <编号> 分别用于列出、查看和停止。只有这套词汇会创建并行工作:普通消息无论内容如何,始终由会话本身按顺序处理。maxParallelTasks 限制单个会话同时存活的任务数;taskWorkspaces: exclusive 会拒绝第二个任务占用已被占用的项目——有已注册 Workspace 时按它识别,没有时按工作目录识别,因此在注册项目之前该保护同样生效。只有当并发写同一目录对你的工作确实安全时,才设为 taskWorkspaces: shared。当既拿不到已注册 Workspace 也拿不到工作目录时,这类任务会共用同一个占用标识而不是丢掉保护,因此独占配置下同一时刻最多只跑一个。持久化的行只保存由指令派生的有界标题,不保存指令正文、文件系统路径或凭据。进程中途死亡留下的行会在下次启动时退役,其占用的项目也随之释放。
runtimeDir 默认为空,即不开启监管。指向一个已存在的绝对路径时,插件会在第一次连接之前在该目录认领机器人的跨进程归属;只要另一个存活实例还持有归属就拒绝启动,因此同一台主机上不会有两个进程同时服务一个机器人。归属通过文件的独占创建认领,因此启动中的实例绝不会删除别人的记录:存活记录会拒绝本次启动,超过 runtimeOwnerTtlMs 未心跳的遗留记录同样拒绝——因为“发现过期就删除”这个动作本身会与其他所有竞争者相互竞争。清理遗留记录是单一执行者的恢复步骤(contrib/systemd/lark-clear-stale-owner.sh,由 ExecStartPre 或运维手动执行),它拒绝删除心跳仍在 ttl 之内的记录。万一归属仍被他人夺走,旧实例会停止服务而不是继续竞争。同一目录下还有一份 status.json,包含组件、实例、pid、版本、状态、就绪与心跳,不含凭据、平台标识、会话范围或其他路径,外部探针无需加载 Harness profile 图即可判断通道状态。该保证的边界是同一个运行目录;两个使用不同运行目录却指向同一机器人的部署仍属于不受支持的配置。可审阅的 systemd 与就绪检查模板位于 contrib/systemd/。
projectManageFrom 是独立且默认拒绝的项目管理 allowlist,默认值为空。管理员仍必须通过普通 allowFrom/allowAllUsers 鉴权,注册与移除命令只接受私聊;allowAllUsers: true 绝不会自动授予项目管理权限。项目管理要求标准可写 Workspace Registry 同时提供 create、delete 和 resolveByPath;只读自定义 Registry 仍只能列出和选择。
inboundTextFiles 默认关闭。开启后,maxInboundTextFileBytes 可设为不超过 256 KiB 硬上限的正整数,默认 128 KiB;机器人必须具备 im:resource 权限,且只在私聊中接收文件。鉴权、持久化消息去重及安全文件名/扩展名检查都会在下载前完成。客户端只会从当前配置的飞书/Lark OpenAPI 域名读取这条已认证消息携带的精确 file key,禁用重定向,同时限制声明长度与实际流长度;它不接受 URL 或本地路径。
inboundImages 同样默认关闭,且只接受私聊。下载前必须依次通过鉴权、去重、Agent 真正空闲、全局唯一且满时立即拒绝的图片槽、精确 resolveModelInfo 元数据明确包含 image,以及稳定附件服务检查。插件只接收一幅结构合法的 PNG 或 baseline/progressive JPEG;APNG、MPO、拼接/尾随图片、GIF、WebP、MIME 不匹配、畸形结构与越界数据全部拒绝。有效限制取插件配置与附件服务限制的较小值。插件硬上限为单图 5 MiB 编码字节、2000 万像素,以及精确当前模型可见会话内 20 张/20 MiB;默认分别为 5 MiB、2000 万像素、4 张/20 MiB。下载只使用已认证消息携带的精确 image key、固定 OpenAPI 域名,并禁用重定向。
outboundArtifacts 同样默认关闭。标准本地 Linux Web profile 开启后,Agent 作用域的 send_lark_artifact 工具只接受当前注册 Workspace 内一个有界的相对 .txt、.log、.patch、.diff、.png、.jpg 或 .jpeg 路径。文本默认 128 KiB、硬上限 256 KiB;图片默认值和硬上限均为 5 MiB/2000 万像素,且任一边都不能超过平台的 12000 像素限制。URL、URI scheme、绝对/穿越/反斜杠/隐藏/保留路径、最终 symlink、逃出 Workspace 或解析到不安全 canonical 路径段的中间 symlink、hardlink、目录、设备、FIFO、跨设备目标、不安全文本、动画与伪装格式全部默认拒绝;稳定指向同一 Workspace 内安全 canonical 目标的中间 symlink 可以使用。只有 Approval、Session 持久化、Workspace Registry 与平台上传/回复 seam 都存在时才注册工具;Web 来源、subagent、过期 turn 与嵌套 Code Mode 调用均无 Lark 发送权。
审批不能仅根据通用 allowed-once 推断。原 Lark 用户必须在同一聊天、同一运行中 turn 的精确已确认 Card 上操作,之后 approval audit 还必须持久 flush,才能开始上传。插件会在审批前通过 descriptor 校验读取并哈希快照后丢弃字节;审批后重新打开,并重新校验相同 root/file 身份、digest、类型与限制。rc.6 无法证明普通 Workspace 文件最初由哪个进程生成,因此最终来源决策依赖人工审批,而不是“由 Agent 生成”的虚假声明。该本地 descriptor 边界只在受支持的 Linux 部署上验证;同设备特权 bind mount 不在非特权威胁模型内。缺少 Linux /proc descriptor 边界的宿主不会注册该工具,inspect/send 一律失败关闭。
proactiveDelivery 默认关闭。开启后,Agent 作用域的 notify_lark 工具只会把一条完成或关注通知受理到本轮已经注册的 Lark 会话。模型不能传入聊天、用户或消息 ID;提及列表最多接受有界的 initiator token。已受理项写入持久化发件箱(键哈希;聊天/用户/消息 ID 只存在 destination 表),带幂等键、重试、过期和每会话速率限制;进程重启既不会静默丢失也不会重复投递已受理通知。调度仍由 Harness 或外部调度器负责。投递卡片沿用执行卡/审批卡同一套 Card 2.0 schema、内边距和字体。
0.1.0 已使用真实飞书凭据完成冒烟测试。Lark 域名路径通过官方 SDK 的域名切换和自动化测试覆盖;在宣称 Lark 已完成真实凭据测试前,仍需按发布手册记录一次 Lark 实测。
保持 defaultSessionId 为空即可隔离会话。私聊沿用兼容的 lark:<chatId> 会话;在群聊中,普通回复树按根消息划分可恢复范围,原生 Lark 话题则按聊天 ID 和话题 ID 划分。parent_id 不会用于选择会话。仅当所有已授权私聊、回复树和话题都应共享同一个 Harness 会话、项目、模型选择、会话目录和恢复权限时,才设置 defaultSessionId。在这种显式共享模式下,所有已授权用户都能看到同一组有界的会话标题、时间、项目和引用元数据,并能恢复其中合格的条目。
存在 Harness 会话持久化后端时,桥接器会在重启后恢复精确会话范围中已提交的 generation。/new 和 /clear 只重置当前私聊、回复树或话题;配置了 defaultSessionId 时,它们会有意重置全局共享会话。新 generation 与 /session resume 会先分别确认当前和候选 Session 的检查点,再把精确的活跃绑定原子提交到持久化存储,然后才回复。创建、恢复或检查点工作被拒绝时,旧绑定仍保持当前状态;后端中可能已部分发布的候选只会成为 orphan,重启时会被忽略。绑定写入出现歧义错误时,只会 fail-stop 当前会话并持续重试同一个值,直到读回确认,而不会报告不确定结果。尚无已提交绑定的普通进程内聊天可以在没有 sessionPersistence 时运行;/new、/clear、项目、会话和模型选择都要求会话持久化与持久化会话绑定 sidecar 同时可用,已有提交绑定的会话在冷恢复时缺少会话持久化也会默认拒绝。
maxConversationHandles 是单个插件实例中活跃会话句柄数量的稳态目标,并非硬并发上限。数量超出目标后,桥接器仅在会话没有活跃 turn、待处理 inbox 工作或桥接器操作,且 sessions.flush() 确认有持久化监听器参与后,才释放最近最少使用的句柄。它不会为了腾出空间取消或拒绝这些工作。缺少持久化或检查点失败时会保留句柄,因此活跃数量可以暂时高于目标。一旦终止清理开始,已退役句柄不会被重新使用;清理失败会记录日志,之后的访问会从持久化会话冷恢复。
设置 maxConversationHandles: 0 后,不会让任何已完成持久化检查点的空闲句柄保持热状态。下次收到消息时,会精确恢复对应 generation、已选模型、Agent preset 和作用域工具。淘汰只移除进程内 Agent 和 Session,不会删除持久化 transcript。冷恢复可能增加延迟;没有会话持久化的自定义 profile 会保留句柄,以免丢失上下文。
0.3.0 以前创建的群聊会话以整个聊天为范围,无法安全归属到某个回复根节点。这些数据仍保留用于回滚或导出,但 0.3.0 不会自动把它们绑定到新的回复树或话题。私聊会话和显式 defaultSessionId 的身份保持兼容。
成功处理的入站消息会记录在一个持久化的 1,024 条回执窗口中,因此正常重启后的 WebSocket 重投不会重复执行 follow-up 或命令。回执介质(Web profile 中通常为 `$DSH
…