@tetherline/agent
v0.3.2
Published
牵星 Tetherline dsh bundle:一条命令把 router / backend-dsh / 渠道 / 网页配置界面装进 dsh profile
Readme
@tetherline/agent —— 牵星的 dsh 入口包
一行装完牵星:router + backend-dsh + 网页配置界面 + 移动端网页,一次装进 dsh profile。
dsh plugin --profile web add @tetherline/agent # 一行装完
dsh web # 设置 → 牵星 → 填配置 → 拨开关 → 保存IM 渠道不在这一层(open-items 第 18 条)。要飞书就再装一次,装完重启 dsh:
dsh plugin --profile web add @tetherline/channel-feishu --allow-build=protobufjs不用记这条命令——设置页上没装的渠道会显示成一张「未安装」卡片,上面就是它,带复制按钮。 装好飞书并拨开开关,就得到 PRD 的 M1 能力——手机飞书里下发任务 → 流式观察 → 按钮审批 → 收到完成通知,全程不需要公网服务器。
这是仓库里唯一允许同时引用多个 packages 的地方(CONTRIBUTING §1)。 形态与设计见 ../../docs/web-config.md。
2026-09-05 起同一条安装命令也带上了移动端网页:
@tetherline/pwa作为默认未启用的 宿主插件进了本 bundle,设置页因此多一张「mobile-web」卡片。启用它还需要一台公开网关, 而网关侧(登录、反代)属 WS-12D,尚未落地——见下面第 6 节。边界与 tarball 安全门禁见 ../../docs/mobile-web.md。
它由什么组成
| 文件 | 作用 |
| --- | --- |
| package.json | dsh bundle manifest(dsh.bundle.patch)+ 四个包的 workspace:^ 依赖(发布时被替换成 ^版本号) |
| cordis.patch.yml | bundle 层:装哪四个插件、按什么顺序,以及还能装哪些渠道的目录。装完即可启动——会对外连接的那一项默认未启用,没有任何必须先手工替换的占位符 |
| scripts/install.sh | 构建 workspace → dsh plugin add → --dump-config 自检(只服务本地 checkout 开发) |
| test/patch.test.ts | 装配层的结构测试:依赖表 ↔ patch 行必须是同一份清单(见下) |
| test/e2e.test.ts | keyless e2e:录制的飞书事件回放进真实装配,跑完整闭环 |
| test/fixtures/session.ndjson | 四条录制事件(全部是 test-only 假值,无任何真实凭证或用户数据) |
patch 层装的五行(顺序即 ../../docs/mobile-web.md §3.3):
| id | 包 | 作用 |
| --- | --- | --- |
| tetherline-router | @tetherline/router | 会话路由 / 出站编排 / 审批,提供 tetherline 服务 |
| tetherline-backend-dsh | @tetherline/backend-dsh | 把任务交给 dsh 的 agent 执行 |
| tetherline-ui | @tetherline/ui-dsh | 网页配置卡片 |
| tetherline-mobile-web | @tetherline/pwa | 移动端网页 + frpc 隧道,默认未启用(第 6 节) |
| tetherline-channel-feishu | @tetherline/channel-feishu | 飞书渠道,默认未启用 |
这两份清单——package.json 的 dependencies 与上面这张表——很容易各写各的:@tetherline/pwa
的宿主插件写完、发布形态也做好了,却因为两边都没加,一行安装装出来根本没有这个插件,而且
没有任何东西会发现。test/patch.test.ts 因此把它们断言成双射,顺带守住行的顺序、
tetherline- 前缀、「有卡片的行 entry id 必须等于该包的 settings 命名空间」、「对外连接的行
默认未启用」和「没有占位符」。以后再加包,漏了哪一边都会红。
1. 前提
- 本机装了
dshCLI,且已有一个能跑通模型的 dsh 配置(provider/API key 由 dsh 自己管,本 bundle 不碰)。 - 装进带网页外壳的 profile。dsh 自带的
webprofile 正合适(模板即[dsh-base, dsh-web-app]);换别的 profile 就得自己往它的bundles里补@deepseek-ai/dsh-web-app一行——那是 in-box bundle,dsh plugin add永远加不进来 (../../docs/web-config.md §1.3)。 - 一个飞书自建应用,拿到
appId(cli_开头)与appSecret。 - 从本仓库 checkout 装时还需要:
pnpm install && pnpm -r build(bundle 用link:指向packages/*/lib)。
飞书应用需要开哪些能力
以飞书开放平台后台为准。以下清单已于 2026-08-31 在真企业自建应用上走通:
- 机器人能力已启用;
- 事件订阅采用长连接方式(本 channel 用官方 SDK 的 WSClient,不监听公网端口,
因此不需要配置请求网址)。连上时 SDK 会打印
ws client ready; - 订阅
im.message.receive_v1事件与卡片回传交互(card.action.trigger); - 权限(应用身份 / tenant):
cardkit:card:write—— 流式卡片的全部三个调用都要它 (cardkit.v1.card.create建实体、cardElement.content追加正文、card.settings收尾)。 漏开它是最容易踩的一个:入站、回执、agent 执行全都正常,只有流式卡片发不出来;- 以应用身份发送消息的权限(
im.v1.message.create/.reply)。
排错:日志里出现
code: 99991672就是权限没开,飞书会在msg里直接给出缺哪个 scope 以及一条指向你自己应用的申请链接,照着开即可。自建应用改权限后通常要发布一个版本才生效。
2. 装进 profile
两条路径,选一条。
A. 从 npm 装(推荐;2026-09-01 已实测)
dsh plugin --profile web add @tetherline/agent不需要 clone 本仓库:dsh 给 profile 写的 pnpm 设置是 nodeLinker: hoisted
(deepseek-harness packages/boot/app-boot/src/profile.ts,0.2.1-alpha.1 在 :243,行号随版本漂移),传递依赖会被
扁平化到 profile 根,loader 解析得到。
要飞书的话再来一条(这一条必须带 --allow-build,见下面「必踩的一步」),装完重启 dsh:
dsh plugin --profile web add @tetherline/channel-feishu --allow-build=protobufjs装完 dsh.profile.bundles 会变成 [dsh-base, dsh-web-app, @tetherline/agent]——
声明了 dsh.bundle 的直接依赖会被自动追加成一层(apps/cli/src/plugin.ts:59-90),
而模板自带的两个 in-box bundle 不受影响。
必踩的一步:装飞书时的 allowBuilds
只影响装 IM 渠道那一条命令,默认安装碰不到它(2026-09-10 起,open-items 第 18 条)。
不带 --allow-build=protobufjs 跑装飞书那条命令会停在这里:
[ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: [email protected]
dsh: pnpm failed in profile directory ~/.dsh/profiles/web原因:protobufjs 是飞书 SDK @larksuiteoapi/node-sdk 的直接依赖,带 build script;
而 dsh 装 profile 用的是 pnpm 11,它把「忽略了 build script」从警告升级成了错误退出
(pnpm 10 只是警告)。
后果不只是报个错——pnpm 非零退出会让 dsh 的 bundle 层登记步骤不执行:包全装上了、
node_modules 里都能解析到,但 dsh.profile.bundles 里不会出现 @tetherline/agent,
启动时牵星根本不加载。装完务必回头确认那一行在。
最省事的办法是把这个决定直接写在命令行上——dsh plugin 是 pnpm 的透传壳,标志原样到达
pnpm,而 pnpm 会把决定写回 profile 的 pnpm-workspace.yaml,所以只有第一次要带:
dsh plugin --profile web add @tetherline/channel-feishu --allow-build=protobufjs已经撞上了的话,pnpm 也把待办写进了 profile 的 pnpm-workspace.yaml:
allowBuilds:
protobufjs: set this to true or false把它改成 false(我们不需要 protobufjs 的 build script),再跑一遍上面那条命令:
# ~/.dsh/profiles/web/pnpm-workspace.yaml
allowBuilds:
protobufjs: false这一步没法由 bundle 代劳:allowBuilds 只在 workspace 根配置里生效,包自己声明不了;
而 profile 的 pnpm-workspace.yaml 由 dsh 在初始化时生成,之后不再改写。
装完自检(bundles 里应有 agent;装了飞书的话还有 channel-feishu):
dsh --profile web --dump-config | grep tetherline新版发布后的 24 小时窗口
pnpm 11 有个供应链防护叫 minimumReleaseAge,默认拒绝安装发布不足 24 小时的版本。
所以刚发出来的修复,装的时候大概率还拿不到——pnpm 会在剩下的候选里挑一个更早的版本,
过程中不报错,只在 pnpm-workspace.yaml 里留一行 minimumReleaseAgeExclude 记录它选了谁:
Added N entries to minimumReleaseAgeExclude in pnpm-workspace.yaml
@tetherline/[email protected] ← 挑中的是旧版,不是刚发的 0.1.1这不是缺陷,是 pnpm 有意的防护,默认就该让它拦着。装完想确认自己拿到了哪一版:
cat ~/.dsh/profiles/web/node_modules/@tetherline/backend-dsh/package.json | grep '"version"'急着要某个刚发布的修复时,显式钉版本可以绕过(这等于替这一个包放弃 24 小时防护, 只在你确知要装哪一版时才这么做):
dsh plugin --profile web add @tetherline/[email protected]把版本加进
minimumReleaseAgeExclude是没用的:那份名单只放行已经被选中的版本, 不改变解析时的优先级。删掉 lockfile 重新解析同样无效。实测(2026-09-01)只有显式钉 版本这一条路走得通。
B. 从本仓库 checkout 装(改本仓库代码时用)
cd bundles/agent
./scripts/install.sh # 参数是 profile 名,默认 web
# dsh 不在 PATH 上时(比如你跑的是 deepseek-harness 源码树):
DSH_BIN=~/.local/bin/dsh ./scripts/install.sh脚本做四件事:构建 workspace、把 workspace 包与 bundle 一起 dsh plugin add 进 profile、
自检这些包能否从 profile 根解析到、打印 --dump-config 让你确认插件行都在。
默认不装飞书(与 npm 那条路一致);要连飞书一起装就 WITH_FEISHU=1 ./scripts/install.sh。
为什么 workspace 包要单独装:loader 是从 profile 根目录解析插件包名的,而 pnpm 对
link:目标不处理它自己的 dependencies——bundle 里的link:../../packages/*不会被 提升到 profile 的node_modules,只装 bundle 会在启动时报Cannot find package '@tetherline/router'。发布到 npm 后pnpm add <bundle>会自动 带上这些传递依赖,只有本地 link 开发这条路有这个洞。dsh 对这些包会提示 「installed as a plain dependency, not a profile layer」,那正是我们要的:它们由 bundle 的 patch 层按包名引用,不自成一层。
3. 在网页上配置
不用编辑任何文件。 装完直接起:
dsh web # 或 dsh --profile <你的 profile 名>浏览器里进 设置 → 牵星(与「通用设置 / 模型 / 插件 / Agent 预设」并列)。默认安装完 会看到两张卡片:
mobile-web—— 移动端网页,「未启用」。见第 6 节。channel-feishu—— 「未安装」。卡片上是安装命令与一个复制按钮;在这台电脑的终端里 跑完、重启一次 dsh,它就换成下面这张配置卡片(同样默认「未启用」)。
channel-feishu 装好之后的卡片:
卡片上只有四项要填,其余都收在「高级设置」折叠区里(都有可用默认值):
| 填什么 | 说明 |
| --- | --- |
| App ID | cli_ 开头的 16 位十六进制 |
| App Secret | 直接填密钥。它写进 dsh 的凭证面,不进任何配置文件,保存后也不再显示出来(留空即不改动) |
| 白名单 | 允许下发任务的飞书 open_id,一行一个。先随便给机器人发条消息,从 dsh 日志里读出你的 open_id |
| 工作目录 | 新会话默认绑定的目录 |
填好后拨右上角的开关启用。
点保存。渠道未启用时不会建立任何连接,字段也不校验;一旦打开开关,配置必须完整合法—— 不合法的写入会被当场拒绝、值不会落地,卡片提示「没保存成功」并保留草稿,进程不受影响 (宿主不回传拒绝原因,具体原因看 dsh 日志) (../../docs/web-config.md §4)。改完即时生效,按新配置重连, 不必重启 dsh。
字段说明来自各渠道 Config schema 的 description,界面自己不存文案——所以卡片上看到的
文字就是 packages/*/src/config.ts 里写的那句。
还是想写文件?
可以,网页上的值只是叠在装配层之上的用户覆盖层,装配层仍然是 profile 自己的
cordis.patch.yml:
$EDITOR "${DSH_HOME:-$HOME/.dsh}/profiles/web/cordis.patch.yml"- id: tetherline-channel-feishu
config:
enabled: true
appId: 'cli_0123456789abcdef'
appSecretEnv: 'TETHERLINE_FEISHU_APP_SECRET'
allowedSenders: ['ou_你的飞书 open_id']
defaultCwd: '/abs/path/to/your/workspace'两处要注意:
config是整体替换,不是深合并(dsh 的applyEntryPatches:target[key] = value, 见 deepseek-harnessvendor/include/src/index.ts)。覆盖某个插件时必须把它的字段写全。- 配置文件里写
enabled: true却漏了必填项,会在插件加载期 fail loud——那是错配置, 该崩;网页写入的同类错误则在写入时被拒。两条路的拦截点不同,都不会让错配置进入运行期。
密钥去哪了
配置里没有任何一处写密钥:appSecretEnv 是凭证引用(一个环境变量名),插件在每次建立
长连接时解析一次——宿主有 ctx.credentials 就问它,没有该服务时读启动环境变量,两处都拿不到
则 fail loud。所以改了密钥不必改配置,也不必重启进程,下次连接即生效(决策记录见
../../docs/open-items.md 第 6 条)。
网页上填的密钥就写在这个引用名下。也可以手写受管文档:
# $DSH_HOME/.credentials.yaml(默认 ~/.dsh/.credentials.yaml,仅本人可读)
version: 1
refs:
TETHERLINE_FEISHU_APP_SECRET: 你的-app-secret外部编辑会热重载,改完不必重启 dsh。临时调试也可以直接
TETHERLINE_FEISHU_APP_SECRET=… dsh web,继承的环境优先级最高。
4. 启动与冒烟
dsh web真机 checklist(对应 PRD 的用户故事,需人工逐条走)。2026-08-31 首次真企业验收结果:
- [x] US-1 下发:手机飞书私聊机器人发一句任务,收到流式更新的卡片。
- [x] US-4 完成通知:任务结束时卡片停止流式,正文末尾出现状态与 token 用量
(
**完成** · 输入 305 / 输出 325 tokens)。 - [x] US-2 审批:让 agent 执行一条需要审批的命令(写 workspace 之外的路径即可触发,
/tmp不行——沙箱把平台临时区也算可写),收到审批卡片;点「批准」后 agent 提权执行、 卡片原地变成「✅ 已批准」,点「拒绝」后 agent 停下、卡片变红。 - [x] 至少有一次审批是在手机上、不在电脑旁完成的(roadmap M1 的产品验收项)。
- [ ] 用非白名单账号发一条消息,收到明确的拒绝回执,且没有产生 agent 会话。 (keyless e2e 已覆盖这条路径;真机验证需要第二个飞书账号。)
- [x] 网页配置界面(2026-08-31 在本机浏览器实测):设置里出现「牵星」一项,卡片显示 「未启用」,四个必填项 + 高级折叠 + 开关都正常,改一个参数保存后落进 settings.yaml、 「恢复默认」能清掉。
- [ ] 网页配置 → 真飞书:填完 appId / 密钥 / 白名单 / 工作目录并打开开关后渠道能连上; 改白名单后按新配置重连;故意填错 appId 时保存被拒、值不落地、进程没崩。
- [x]
/new/sessions/abort/status各跑一次,回执符合预期。/use <绝对路径或 ~ 路径>切工作目录并新建会话(未跑)。 - [x] 杀掉 dsh 进程再启动:上一条 pending 审批被判失效并通知,不再重发可点卡片 ——agent 侧的审批等待方随进程消失,跨不了进程,详见 architecture §5.3。
5. keyless e2e(无需飞书凭证)
pnpm --filter @tetherline/agent testtest/e2e.test.ts 把 test/fixtures/session.ndjson 里的录制事件回放进真实装配:
dsh 自己的 AgentLoop、ApprovalService、工具管线与会话日志(@tetherline/backend-dsh/testing
的运行时夹具),router 与 backend-dsh 两个 cordis 插件按 profile 的方式挂载,
channel-feishu 的归一化、降级与 CardKit 卡片渲染全走真实代码。
只有两处是替身,且都在系统边界上:
- 模型换成脚本化
LlmAdapter(不联网、可断言); - 飞书传输层换成
ReplayTransport(不联网,出站只记录,入站由录制事件驱动)。
覆盖三条路径:US-1/US-4 的流式闭环、US-2 的审批闭环(含点击回传)、非白名单拒绝。
想加事件:把真实事件按 RecordedFeishuEvent 格式({"type":…,"event":…})追加进 NDJSON,
去标识化后再提交——录制里不得出现真实 open_id、聊天内容或凭证。
6. 移动端网页(mobile-web 卡片)
装完就有这张卡片,默认未启用。关着的时候它不挂 /tetherline 路由、不解析凭证、
不起 frpc 子进程、不碰网络——所以一行安装本身不会产生任何对外连接。
打开它需要三样本 bundle 给不了的东西,缺一样就不要拨那个开关:
- 一台公开网关:跑 frps、有域名和证书、把某个远端端口反代成 HTTPS。部署件在
deploy/vps, 登录层与反代宿主 PWA 的那份配置见其 SETUP 第 12 节(2026-09-06 已在真 VPS 上验收: 网页登录、/tetherline/反代宿主插件、WebSocket、免登录探测路径全通)。 切过去之前记得停掉旧形态那条常驻 frpc,否则两个 frpc 抢同一个远端端口。 - frpc 可执行文件:六个平台二进制包还没发布,暂时只能用高级项
frpcPath指一个 自备的 frpc。这是开发选项,不是产品出口(../../docs/mobile-web.md §3.4)。 - dsh 信任那个公开 authority,也就是下面这一节。
必踩的一步:让 dsh 信任公开 authority
dsh 按外部 authority 校验浏览器会话与来源。手机经网关过来时 Host 是
gateway.example.com,而 dsh 默认只信任自己绑的那个地址,于是每个请求直接 403——
隧道通、卡片绿、手机上却什么都打不开,是这条链路上最难查的一种故障。
这一步 bundle 替不了你:公开地址是你填在卡片里的值,装配层在安装时并不知道它。
而且它是装配期事实,不能热更新——填完公开地址要重启一次 dsh 才生效
(../../docs/mobile-web.md §8,已用 0.1.2-alpha.2 的实际 tarball 实证)。
两条路,选一条。
A. 启动参数(临时验证用最省事):
dsh web --trusted-host gateway.example.comB. 写进 profile 自己的 patch 层(长期部署用这条,重启后仍在):
$EDITOR "${DSH_HOME:-$HOME/.dsh}/profiles/web/cordis.patch.yml"- id: connection
config:
trustedHosts: !!js ['gateway.example.com', ...ctx.webRuntime.trustedHosts]connection 是 @deepseek-ai/dsh-web-app 装的行,它的 config 只有 trustedHosts
一个键,所以照抄上面这三行就是完整的——但请记住 patch 是整体替换而非深合并:
哪天 dsh 给这一行加了第二个键,你这份覆盖会把它抹掉。...ctx.webRuntime.trustedHosts
不能省,它是 dsh 自己算出来的本机与局域网地址,去掉就登不上本机的 dsh 了。
只填了公开地址、没做这一步会怎样:手机上 403。宿主计划在设置卡片的「验证」里 直接把这个情况判出来(合成一个只带
Host的探测请求,403 就指到本节), 那是 §6 分层验证的第 1 层,尚未实现。在它落地之前,这一步只能靠本节记着。
现状 / 已完成 / 下一步
- 现状:M1 与 WS-11 都已在真飞书企业上完成验收。M1 是 2026-08-31(当时还叫
@tetherline/feishu-agent、装在自定义tetherlineprofile 上、配置靠手工编辑文件); WS-11 的一行装完 + 网页配置是 2026-09-01:install.sh web装入后,全程在浏览器里 填配置、拨开关,没有编辑过任何文件,手机上收发正常。 2026-09-05 起 WS-12C 也接上了:@tetherline/pwa进依赖表与 patch 层,同一条安装 命令现在也装移动端网页。2026-09-06 这一半也完成了真机验收:真 VPS 网关 + 真 dsh + 真 frpc,装配后分层验证第 1~5 层全绿,手机浏览器上的第 6 层仍待人工完成。装的是本地 tarball(@tetherline/pwa还没发到 npm),一并把「从 tarball 的一行安装 e2e」实跑了一遍: 七个包解析得到、五行插件齐全、第一次照旧卡在allowBuilds。frpc 平台包仍未发布,所以 还得靠高级项frpcPath指一个自备的 frpc。 那次真机撞出并修掉的一个缺陷正好属于本层:@tetherline/ui-dsh的动作 RPC 频道与@tetherline/pwa的静态前缀都叫/tetherline,装到一起时 dsh 直接起不来——两个包各自 测都对,只有装配层同时认识它们,断言因此落在test/webserver-paths.test.ts。 - 已完成:bundle manifest 与 patch 层(五行,装完即可启动、无占位符)、装配层结构测试 (依赖表 ↔ patch 行双射,10 用例)、安装脚本(shellcheck 通过,含解析自检)、 录制事件回放 e2e(3 用例,迁移后仍全绿)、真机冒烟 checklist、 经真机订正的飞书能力/权限清单与踩坑表。
- 下一步:
- ~~安装链路已实测,网页配置未实测~~:2026-09-01 后半程也走完了,装进本机真实
webprofile → 浏览器「牵星」页填配置 → 拨开关 → 手机收发 → 热重连,全过。 当场抓到一个缺陷(写入被拒时没有任何原因留痕),已修; - ~~尚未发布到 npm~~:2026-09-01 首发完成(七个包,channel-feishu 起于 0.1.1,
其余 0.1.0),第 2 节 A 路径已用干净的临时
DSH_HOME实跑验过——一条dsh plugin add @tetherline/agent把六个包扁平化装进 profile 根、bundle 层登记正确、--dump-config四行插件齐全。但第一次必然失败于allowBuilds,已写进第 2 节。 加上@tetherline/pwa之后是七个包、五行插件,这一版还没实跑过 A 路径—— 因为@tetherline/pwa还没发到 npm。发布 PR(changesets 的「发布版本」PR)一合并它 就会以0.1.0上 npm,那之后要重跑一次 A 路径才算数; - ~~从 tarball 的一行安装 e2e 尚未做~~:2026-09-06 实跑完成(见上方现状)。用本地
pnpm pack的七个 tarball 装进临时DSH_HOME,七个包解析得到、五行插件齐全、第一次照旧卡在allowBuilds。它仍不是自动化门禁——需要 dsh CLI 与网络,本仓库单测跑不动这个形状, 目前是一条手工验收路径,要不要固化成 CI 作业没定; - 非白名单账号的拒绝路径真机未验(keyless e2e 已覆盖,真机需要第二个飞书账号);
/use <目录>未实跑(其余四个斜杠命令已验);- 尚未验证的长尾场景:群聊与话题(当前只验过私聊)、多会话并发、长时间运行后的连接迁移。
- ~~安装链路已实测,网页配置未实测~~:2026-09-01 后半程也走完了,装进本机真实
真机验收踩过的坑(照着装的人会遇到同样的)
| 现象 | 根因 | 出处 |
| --- | --- | --- |
| 渠道一直连不上,日志里什么都没有 | channel-feishu 漏把 credentials 声明进 inject,建连时凭证服务还没就绪 | 已修 |
| agent 报 has no provider/model | backend-dsh 的「沿用 dsh 部署默认」是空的,dsh 不会自己补默认 | 已修 |
| 流式卡片发不出,日志里一个 400 | 飞书应用没开 cardkit:card:write(code 99991672 会直接告诉你缺哪个 scope) | 见第 1 节 |
| 审批卡片发不出(400),agent 停在等审批 | 卡片声明 schema 2.0 却用了 1.0 的 tag: action 容器(code 230099) | 已修 |
| 点审批按钮弹「该应用尚未配置卡片回调」 | 开放平台需要一键配置卡片回调,配完要重新发布应用版本 | 见第 1 节 |
| 代码块渲染成行内代码、工具行和正文粘一起 | 方言转换被逐片作用在流式增量上 | 已修 |
| 启动即 Cannot find package '@tetherline/router' | 只装 bundle 不够,workspace 包要各自装进 profile(只影响本地 link 这条路) | 见第 2 节 |
| 设置页里飞书一直显示「未安装」,命令明明跑过了 | bundle 层是启动时合成的,装完要重启 dsh;仍不出现就看 dsh --profile <名> --dump-config 里有没有 tetherline-channel-feishu 那一行 | 见第 2 节 |
| 从 npm 装时 [ERR_PNPM_IGNORED_BUILDS] protobufjs,且包装上了却不加载 | dsh 装 profile 用 pnpm 11,它把「忽略 build script」升成错误退出,连带跳过 bundle 层登记 | 见第 2 节 A |
| 问「列一下当前目录」,回来的是工具调用标记原文(<||DSML||tool_calls> 或 <tool_req>) | 建会话时没加入 agent preset,agent 的工具集是空的——preset 决定 model-facing 插件集,而它要 meta.agentPreset + mount() 两步 | 已修(backend-dsh 0.1.1) |
| 修复已发布,装完却还是老样子 | pnpm 11 的 minimumReleaseAge 默认拦下发布不足 24 小时的版本,静默退回更早的一版 | 见第 2 节 A |
| 审批收束后卡片一片空白,只有「请升级至最新版本客户端」 | card.update 是整体替换,卡片 JSON 漏了 schema: '2.0' | 已修 |
另有一条不是 bug 而是设计,第一次遇到容易困惑:杀掉 dsh 再启动后,之前那条 pending
审批会被判失效(收到「因进程重启已失效,请重新发起任务」),而不是重发一张可点的卡片。
原因是 agent 侧挂在 waterfall 上的审批等待方随进程一起消失了——dsh 的会话能 resume,
等待方不能,重发的卡片点下去只会得到「回包失败,会话不在线」。详见
architecture §5.3。
