Files
openclaw-config/docs/bk02-dsh-openclaw-ACP集成.md
T
yangxuan 43cd42a563 feat(acp): 接入 DSH 作为 ACP harness,打通 微信→OpenClaw→DSH 派发链路
- openclaw.json
  - plugins.allow 放行 acpx(限制性白名单,缺了后端不会加载)
  - 新增 acp 策略段:enabled/dispatch、backend=acpx、defaultAgent=dsh、
    allowedAgents=[dsh]、stream.deliveryMode=live
  - 新增 plugins.entries.acpx.config:permissionMode=approve-all、
    timeoutSeconds=900、cwd、agents.dsh = dsh --profile acp(绝对路径)
  - 新增 OpenClaw agent dsh(runtime.type=acp → harness dsh),否则
    sessions_spawn 会报 dispatch_failed: Unknown agent id "dsh"
- workspace-dsh/:该 agent 的身份文件(AGENTS/SOUL/IDENTITY/USER/BOOTSTRAP.md)。
  其中的空 .git 由 `openclaw agents add` 的标准 provisioning 生成(非任何 agent 自建),
  会让父仓库把它当 gitlink,已移除后纳入版本控制
- docs/bk02-dsh-openclaw-ACP集成.md:部署/配置/验证证据/排错/回滚全文
- docs/dsh-acp-smoke.mjs:ACP 独立冒烟(initialize→session/new→prompt→close)
- docs/dsh-lang-check.mjs:全局中文指令验证(英文提问看是否回中文)
- skills/delegate-to-dsh:让「交给 dsh」稳定走 ACP 派发;缺此技能时模型会
  静默 fallback 到内嵌 subagent(已复现「假成功」并写入判据)
- agents/main/agent/workshop-skills/acp-backend-triage:ACP 后端排查技能
- plugin-skills/acp-router:acpx 插件自带技能(symlink,与既有渠道一致)
- .gitignore:workspace-dsh 沿用 workspace/ 白名单(只版本化顶层 *.md);
  新增忽略 acpx/ 插件运行产物

验证:ACP 三条途径均通过(独立冒烟、显式 sessions_spawn、自然语言「转给 dsh」),
DSH 侧会话留痕与产物落地见 docs 文档 §5 与 §7。
2026-09-16 17:40:53 +08:00

448 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# bk02 · DSH × OpenClaw ACP 集成说明
> **用途**:本机 DeepSeek Harnessdsh)的部署与「OpenClaw → DSH」ACP 协同链路的完整说明。新会话/新人接手读本文件即可,无需历史对话。
> **配套**`bk02-openclaw-系统说明.md`OpenClaw 本体)、`openclaw-升级与维护.md`
> **主机**bk02 / `xuan-asus-nj` · Debian 12 bookworm · 首次落地 2026-09-16
>
> ⚠️ 本目录**不含明文口令**,凭据位置见 §8。
---
## 0. 30 秒速览
| 项 | 值 |
|---|---|
| DSH 版本 | `@deepseek-ai/dsh` **0.1.5-rc.1**nvm Node **v26.8.2**,全局安装) |
| DSH ACP 入口 | `dsh --profile acp` —— 官方 `@deepseek-ai/dsh-acp`**ACP v1 stdio server** |
| DSH Web 服务 | `dsh-web.service`**用户级** systemd),监听 `127.0.0.1:18787` |
| Web 访问入口 | **`https://bk02.baiji-algieba.ts.net:18787/?token=<启动时打印>`**tailscale serve,仅 tailnet 内可达) |
| OpenClaw 侧 | `@openclaw/acpx` 插件 2026.9.4harness 别名 **`dsh`**,已注册为 OpenClaw agent `dsh` |
| 链路 | **微信/飞书 → OpenClaw agent → `sessions_spawn(runtime:"acp", agentId:"dsh")` → `dsh --profile acp` → 干活 → 结果回传** |
| DSH 状态目录 | `~/.dsh`profiles/ sessions/ .env / AGENTS.md |
| 已装插件 | web profile`skillhub-plugin` 0.2.16 + `superpowers-dsh` 0.1.1**acp profile:仅 `superpowers-dsh`**skillhub 的启动自检会往 stdout 打印,污染 ACP 协议流,见 §3.1) |
| 全局指令 | `~/.dsh/AGENTS.md` —— 交互与思考一律中文,所有 profile / 所有会话生效 |
**最常用的四条命令**
```bash
export PATH=$HOME/.nvm/versions/node/v26.8.2/bin:$PATH # 每次登录先做
systemctl --user status dsh-web # DSH Web 状态
journalctl --user -u dsh-web -f # DSH Web 日志(含带 token 的访问 URL)
journalctl --user -u openclaw-gateway -f # OpenClaw 网关日志(ACP spawn 记录在这里)
node ~/.openclaw/docs/dsh-acp-smoke.mjs # DSH ACP 独立冒烟(不经过 OpenClaw
```
---
## 1. 架构与职责边界
```
微信 / 飞书 / 其他渠道
┌──────────────────────────────┐
│ OpenClaw Gateway (18789) │ ← 渠道接入、路由、会话、结果投递
│ @openclaw/acpx 插件 │ ← ACP 客户端侧(acpx 0.13.2 内嵌)
└──────────────────────────────┘
│ ACP v1 over stdio(JSON-RPC)
┌──────────────────────────────┐
│ dsh --profile acp │ ← 每会话一个子进程;ACP server 侧
│ @deepseek-ai/dsh-acp │ 模型 deepseek-official / deepseek-v4-flash
│ dsh-base(工具/沙箱/持久化) │ 权限 preset workspace-write
└──────────────────────────────┘
真实干活:读写文件、跑命令、再回传
```
职责分工(官方定位):
- **OpenClaw 拥有**:渠道、路由、后台任务状态、投递、绑定、策略。
- **DSH 拥有**provider 登录、模型目录、文件系统行为、原生工具、会话持久化。
> 关键认知:**ACP 是 stdio 直连,不需要任何网络暴露**。DSH Web(18787)是给人用的浏览器界面,与 ACP 链路彼此独立。
---
## 2. 为什么不是「把 DSH 装成一个别的 ACP 工具」
DSH 自带官方 ACP 服务端,无需自研桥接:
- npm 包 `@deepseek-ai/dsh-acp``Automation-only Agent Client Protocol server for driving DeepSeek Harness agents over JSON-RPC stdio`
- dsh ≥0.1.5 内置 `acp` profile 模板(bundles = `@deepseek-ai/dsh-base` + `@deepseek-ai/dsh-acp-app`),`dsh --profile acp` 开箱即用。
- 默认组合已配好 `provider: deepseek-official` / `model: deepseek-v4-flash`
- ACP 能力:`initialize / session/new / session/list / session/resume / session/close / session/prompt / session/cancel / session/set_config_option``session/update` 语义流、`session/request_permission` 权限询问、stdio 与 HTTP MCP。
- 明确不支持(写代码时别指望):`session/load`、删除、fork、附加目录、SSE MCP、计划/终端/客户端文件系统操作、elicitation。
---
## 3. DSH 侧安装(可复现步骤)
```bash
# 1) 用 nvm 的 Node(系统 /usr/bin/node 是 22.x,不用)
export PATH=$HOME/.nvm/versions/node/v26.8.2/bin:$PATH
# 2) 全局安装 dsh(必须显式放行 install scripts,否则 node-pty / spawn helper 不生成)
npm i -g --allow-scripts=@deepseek-ai/dsh-subprocess-local,koffi,node-pty,@google/genai,protobufjs \
@deepseek-ai/dsh@0.1.5-rc.1
# 3) 初始化 ACP profile(首次执行自动创建 ~/.dsh/profiles/acp
dsh --profile acp --help
# 4) 写入模型凭据(600 权限;凭据来源优先级见 §8)
printf 'DEEPSEEK_API_KEY=<见 §8>\n' > ~/.dsh/.env && chmod 600 ~/.dsh/.env
# 5) 独立冒烟:不经过 OpenClaw,直接与 dsh 的 ACP server 对话
node ~/.openclaw/docs/dsh-acp-smoke.mjs
```
`~/.dsh/profiles/acp/` 由 CLI 自动生成,**不要手改**(除非要换模型):
```json
{ "dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-acp-app"],
"patchReload": "startup" } } }
```
要改模型/provider,在 `~/.dsh/profiles/acp/cordis.patch.yml` 里按 id 覆盖 `acp` 行:
```yaml
- id: acp
config:
provider: deepseek-official
model: deepseek-v4-pro # 默认是 deepseek-v4-flash
```
### 3.1 插件与全局中文指令(2026-09-16 追加)
**前置:`dsh plugin` 是 pnpm 的转发器,先装 pnpm**(本机原先没有):
```bash
export PATH=$HOME/.nvm/versions/node/v26.8.2/bin:$PATH
npm i -g pnpm # → pnpm 12.4.2,落在 ~/.nvm/versions/node/v26.8.2/bin/pnpm
```
**装插件**(插件是否属于某个 profile,取决于 `dsh --profile <name>`,所以要分别装):
```bash
# Web 界面用的 profile:两个都装(HTTP 传输,不受 stdout 约束)
dsh plugin --profile web add skillhub-plugin superpowers-dsh
# ACP 自动化 profile:只装 superpowers-dsh —— 详见下方「stdout 纪律」
dsh plugin --profile acp add superpowers-dsh
```
装完 profile 的 `package.json` 会自动登记(`dsh plugin` 会按已装状态 reconcile 层列表):
```json
{ "dependencies": { "skillhub-plugin": "^0.2.16", "superpowers-dsh": "^0.1.1" },
"dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app",
"skillhub-plugin", "superpowers-dsh"] } } }
```
校验(不需要起服务):
```bash
dsh --profile web --dump-config | grep -iE "skillhub|superpowers"
```
#### ⚠️ stdout 纪律:`skillhub-plugin` 不能装进 acp profile
`skillhub-plugin` 0.2.16 在插件加载时跑一个 fire-and-forget 启动自检,**用 `console.log` 往 stdout 打印**
```
[skillhub] self-check ok: 插件分页两页零重复(5+5 条, total=2714)
```
而 ACP 的 stdout **只允许承载协议流量**`dsh-acp` 文档原话:*Stdout carries only protocol traffic, so keep logging off it*)。该行会被 ACP 客户端当成非法 JSON 帧。实测:装进 acp profile 后,ACP 冒烟脚本每轮都会捕获到这条非 JSON 输出(脚本用宽容解析才没崩,acpx 的行为不作保证);从 acp profile 移除后立即干净:
```bash
dsh plugin --profile acp remove skillhub-plugin
node ~/.openclaw/docs/dsh-acp-smoke.mjs # 输出里不应再出现「非 JSON 的 stdout 输出」
```
该输出硬编码在 `lib/host.js``selfCheckPluginPaging()` 里(同函数另外两处用 `console.error` → stderr,无害),**没有配置开关**。若将来非要在 ACP 侧用技能市场,需要给该行打补丁改走 stderr,而不是直接安装。
#### 全局中文指令(交互 + 思考)
`~/.dsh/AGENTS.md`DSH_HOME 级,**所有 profile、所有会话生效**;权限 600):
```markdown
# 用户全局指令(User-Global Instructions
以下约定适用于所有 DSH 会话、所有项目目录,作为全局行为准则,优先于工具描述中的一般性提示:
## 语言
- 交互:与用户的所有交流(回复、提问、澄清、说明)一律使用中文。
- 思考:内部推理与思考过程也一律使用中文。
```
与 wit01 上 `~/.dsh/AGENTS.md` 内容一致。验证方式(脚本 `dsh-lang-check.mjs`,走 ACP 提问,故意用英文):
| 提问 | DSH 回复 | 判定 |
|---|---|---|
| `Answer in ONE short sentence, English only: what is 2+2?` | `2 + 2 = 4.` | 用户显式指定 English → 遵从用户(预期行为) |
| `Reply with a single short sentence: name one benefit of unit tests.` | `单元测试能在改动代码时快速发现回归缺陷。` | ✅ 未指定语言时输出中文,**指令生效** |
> 判定中文指令是否生效,要用**未指定语言**的英文提问;像第一行那样显式要求 English only 时,模型服从当次用户指令是正确行为,不算指令失效。
---
## 4. OpenClaw 侧配置(4 处,缺一不可)
配置全部落在 `~/.openclaw/openclaw.json`(用 `openclaw config patch` 写入,勿手改 JSON 后不校验)。
### 4.1 插件清单必须放行 `acpx`
`plugins.allow` 是**限制性白名单**,不在里面 = 插件被阻止加载(`/acp doctor` 会报缺失 allowlist 项):
```json5
"plugins": { "allow": ["deepseek","memory-core","ollama","searxng","openclaw-weixin",
"dingtalk-connector","openclaw-lark","feishu", "acpx"] }
```
### 4.2 acpx 插件里注册 DSH harness
```json5
"plugins": { "entries": { "acpx": { "enabled": true, "config": {
"permissionMode": "approve-all", // 非交互会话必需,否则写/执行被拒(见 §9 安全)
"timeoutSeconds": 900,
"cwd": "/home/yangxuan/.openclaw/workspace",
"agents": {
"dsh": {
"command": "/home/yangxuan/.nvm/versions/node/v26.8.2/bin/dsh",
"args": ["--profile", "acp"]
}
}
} } } }
```
> `agents.<id>` 是 acpx 插件自带的扩展点(schema`{ command, args? }`),**用绝对路径**,因为网关服务的 PATH 未必包含 nvm 目录。
### 4.3 开启 ACP 策略
```json5
"acp": {
"enabled": true,
"dispatch": { "enabled": true },
"backend": "acpx",
"defaultAgent": "dsh",
"allowedAgents": ["dsh"],
"stream": { "deliveryMode": "live" }
}
```
### 4.4 把 `dsh` 注册成一个 OpenClaw agent(关键,易漏)
只做 4.2 + 4.3 会得到 `dispatch_failed: Unknown agent id "dsh"`ACP spawn 会拼出子会话键 `agent:dsh:acp:<uuid>`Gateway 用 `listAgentIds()` 校验该 agent 必须**真实存在**。
```bash
openclaw agents add dsh --workspace /home/yangxuan/.openclaw/workspace-dsh --non-interactive
# 再用 config patch 给它接上 ACP 运行时:
# agents.entries.dsh.runtime = { type: "acp", acp: { agent: "dsh", backend: "acpx" } }
```
最终 `agents.entries.dsh`
```json5
{
"name": "DSH",
"workspace": "/home/yangxuan/.openclaw/workspace-dsh",
"agentDir": "/home/yangxuan/.openclaw/agents/dsh/agent",
"runtime": { "type": "acp", "acp": { "agent": "dsh", "backend": "acpx" } }
}
```
改完配置:`openclaw gateway restart`(插件类改动必须重启;纯 `agents.entries` 改动可热加载)。
---
## 5. 验证(2026-09-16 实测,均已通过)
| 检查 | 命令 | 结果 |
|---|---|---|
| ACP 独立冒烟 | `node ~/.openclaw/docs/dsh-acp-smoke.mjs` | `initialize``deepseek-harness-acp 0.0.1``session/new` → sessionId + 模型下拉(deepseek-v4-flash/pro…);`session/prompt` → 助手回「1+1 等于 2。」;`stopReason: end_turn` |
| 后端就绪 | 网关日志 | `embedded acpx runtime backend registered (cwd: /home/yangxuan/.openclaw/workspace)``backend ready` |
| 端到端派发 | `openclaw agent --agent main --session-key acp-e2e -m "请调用 sessions_spawn(runtime=acp, agentId=dsh, task=在 /tmp/dsh-e2e/ 创建 proof2.txt …)"` | 子会话 `agent:dsh:acp:ce3e4ea5…`runId `b175b303-…`,约 5 秒完成 |
| DSH 侧留痕 | `~/.dsh/sessions/--home-yangxuan-.openclaw-workspace-dsh--/<uuid>/session.v3.jsonl.zstd` | 含 `permission/preset: workspace-write``sandbox/mode: workspace-write``approval/policy: ask` 及用户消息原文 |
| 产物落地 | `ls /tmp/dsh-e2e/` | `proof2.txt` / `proof3.txt` = `dsh-acp-e2e-ok` |
| Web 跨机访问 | `curl -L "https://bk02.baiji-algieba.ts.net:18787/?token=<token>"` | 303 → Set-Cookie → **200**27724 字节 HTML |
| 自然语言派发(**部署 skill 前**) | `openclaw agent --agent main -m "这个活我不想自己干,请转给 dsh 执行:…"` | ❌ **假成功**:回复称「已交给 dsh」,但 DSH 侧无新会话 —— 实际是默认 `subagent` + `exec` 干完再复述(工具轨迹 10 次调用 / 4 次失败) |
| 自然语言派发(**部署 skill 后**) | 同上措辞(用户不必知道参数) | ✅ DSH 侧新增会话 `332ef715-28e4-4dfb-812e-4152f3c33afd``cwd=workspace-dsh``permission/preset: workspace-write`),任务描述被自动补全验收标准,产物 `nl2.txt` = `skill-ok` |
> 证据判据:**别只看 OpenClaw 的回复文本**。回复正确可能来自 OpenClaw 内嵌运行时(`executionTrace.runner="embedded"`)。要确认真的走了 DSH,看两处:网关日志里的 `agent:dsh:acp:<uuid>` 子会话,以及 `~/.dsh/sessions/--home-yangxuan-.openclaw-workspace-dsh--/` 下是否新增会话目录。
---
## 6. DSH Web 界面(给人用,与 ACP 无关)
`~/.config/systemd/user/dsh-web.service`
```ini
[Unit]
Description=DeepSeek Harness Web (dsh web on 18787, tailscale)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Environment=PATH=/home/yangxuan/.nvm/versions/node/v26.8.2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Environment=DSH_HOME=/home/yangxuan/.dsh
ExecStart=/home/yangxuan/.nvm/versions/node/v26.8.2/bin/node \
/home/yangxuan/.nvm/versions/node/v26.8.2/bin/dsh web \
--host 127.0.0.1 --port 18787 --no-open \
--trusted-host bk02.baiji-algieba.ts.net --trusted-host bk02.baiji-algieba.ts.net:18787 \
--trusted-host localhost:18787 --trusted-host 127.0.0.1:18787
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
```
对外暴露(仅 tailnet 可达,https,复用 tailscale 证书):
```bash
sudo -n tailscale serve --bg --https=18787 http://127.0.0.1:18787
sudo -n tailscale serve status # https://bk02.baiji-algieba.ts.net:18787 (tailnet only)
```
**访问必须带 token**(每次启动随机生成、只打印一次):
```bash
journalctl --user -u dsh-web --no-pager | grep -o 'token=[A-Za-z0-9]*' | tail -1
# → 浏览器打开 https://bk02.baiji-algieba.ts.net:18787/?token=<上面那串>
```
不带 token 访问返回 `401 dsh web authentication required; reopen the URL printed by dsh web.`;带 token 会用 303 + `Set-Cookie: dsh-auth-*` 落地,之后正常浏览。
两个已知坑:
1. `--host` **只接受 `127.0.0.1` 或 `0.0.0.0`**;写 `--host 100.115.195.192` 会在启动时报
`ValidationError: $.host expected "127.0.0.1" | "0.0.0.0" but got "100.115.195.192"`
所以要「tailscale 上的 18787」就用 `127.0.0.1` + `tailscale serve`(本方案),不要指望直绑 tailscale IP。
2. 首次启动会初始化 `~/.dsh/profiles/web`,约 10–40 秒才监听端口;systemd 里已在等待逻辑外,需自行 `ss -ltn | grep 18787` 确认。
---
## 7. 微信 → DSH 的接法(2026-09-16 实测结论)
**结论:微信 / 飞书 / 钉钉只能走「助手按需派活」,渠道级 ACP 绑定在这三家都不存在。**
### 7.1 渠道能力实测(为什么不能直接绑定会话)
OpenClaw 判定一个渠道能否「把当前会话绑到 ACP 会话」的代码是:
`getChannelPlugin(id)?.conversationBindings?.supportsCurrentConversationBinding === true`;持久 `bindings[] type="acp"` 还需要适配器提供 `bindings.compileConfiguredBinding` / `matchInboundConversation`(Telegram 适配器就是这么实现的)。
体检本机三个渠道插件的结果:
| 渠道 | 插件 | `conversationBindings` | `supportsCurrentConversationBinding` | `threadBindings` |
|---|---|---|---|---|
| 微信 | `@tencent-weixin/openclaw-weixin` 2.4.8 | 0 处 | 0 处 | 0 处 |
| 飞书 | `~/.openclaw/extensions/openclaw-lark` | 0 处 | 0 处 | 0 处 |
| 钉钉 | `@dingtalk-real-ai/dingtalk-connector` | 0 处 | 0 处 | 0 处 |
| Telegram(对照组,官方内置) | `dist/channel-*.mjs` | 有 | `true``bindingStore: "adapter"` | 有 |
因此:
-**`/acp spawn dsh --bind here`** 在微信/飞书/钉钉不可用(会得到 "Conversation bindings are unavailable …")。
-**持久 `bindings[] type="acp"`** 同样不可用(适配器没有绑定钩子)。
- ✅ **助手派活**是最短可用路径(也是本机唯一路径)。
### 7.2 唯一可用路径:助手把活派给 DSH(已部署)
链路:**微信消息 → OpenClaw `main` 助手 → `sessions_spawn(runtime:"acp", agentId:"dsh")``dsh --profile acp` → 干活 → 结果回传微信**。
为了让它**稳定**,已部署技能 `~/.openclaw/skills/delegate-to-dsh/SKILL.md`:触发词为「交给 dsh / 让 dsh 干 / 用 DSH 跑 / 转给 dsh」,并写明纪律(必须带 `runtime:"acp"`、禁止自己用 `exec` 或默认 subagent 兜底、派发失败要如实报错、结果原样回传)。
> ⚠️ **不加这个技能不可靠**:实测 17:19 那次用户说「转给 dsh 执行」,`main` 声情并茂地回了"已交给 dsh 并执行完成",但工具轨迹里是 `sessions_spawn`(默认 `subagent`+ `exec`DSH 侧**没有任何新会话** —— 即它由 OpenClaw 内嵌运行时干完,然后复述成 dsh 的口气。技能上线后同一句话(17:21)DSH 侧新增会话 `332ef715…``cwd=/home/yangxuan/.openclaw/workspace-dsh`,产物落地。
### 7.3 怎么确认真的交给了 DSH
```bash
# ① DSH 侧会话数是否 +1(最硬的证据)
ls -lt ~/.dsh/sessions/--home-yangxuan-.openclaw-workspace-dsh--/ | head
# ② 网关日志里的 ACP 子会话 / 后端就绪
journalctl --user -u openclaw-gateway --since "10 minutes ago" | grep -iE "agent:dsh:acp:|acpx runtime backend"
# ③ 会话内容(含 cwd 与 permission preset
L=$(ls -t ~/.dsh/sessions/--home-yangxuan-.openclaw-workspace-dsh--/ | head -1)
zstd -dc ~/.dsh/sessions/--home-yangxuan-.openclaw-workspace-dsh--/$L/session.v3.jsonl.zstd | head -5
```
判据:回复文本**不算证据**(见 7.2 的反例),要看 ①/③。
### 7.4 其他说明
- `agents.entries.dsh.runtime.type="acp"` **不会**让发给 `dsh` 的普通消息自动走 ACP(实测 `openclaw agent --agent dsh` 仍是 `executionTrace.runner="embedded"`)。它的作用只有一个:让 `dsh` 能作为 `sessions_spawn(runtime:"acp", agentId:"dsh")` 的合法目标。
- 想换成「某个微信账号的消息全量交给 DSH」,在当前渠道能力下做不到「消息直通」;可行的近似做法是把该账号绑定的 agent 换成 `dsh`,但那只会让 OpenClaw 内嵌运行时换个身份干活,不是 ACP。
- 若将来 OpenClaw 侧或渠道插件补上 `conversationBindings`,再考虑 `/acp spawn --bind here`
---
## 8. 凭据位置(本项目文档不含明文)
| 凭据 | 位置 | 说明 |
|---|---|---|
| DSH 模型 API Key | `~/.dsh/.env`600)里的 `DEEPSEEK_API_KEY` | DSH 凭据解析优先级:**继承的进程环境** > `~/.dsh/.credentials.yaml` > 调用目录 `.env` > `~/.dsh/.env`。写 `.env` 最省事;Web 的 Models 页写入则会落到 `.credentials.yaml` 并覆盖 `.env` |
| DSH Web 访问 token | 运行时随机生成 | 见 §6,从 `journalctl` 取;不落盘 |
| OpenClaw 各类密钥 | `~/.openclaw/.env` / `~/.openclaw/openclaw.json` | 见 `bk02-openclaw-系统说明.md` §6 |
---
## 9. 排错清单(按遇到概率排序)
| 症状 | 原因 | 处置 |
|---|---|---|
| `dispatch_failed: Unknown agent id "dsh"` | 只注册了 acpx harness 别名,没建同名 OpenClaw agent | 做 §4.4`openclaw agents add dsh` + `runtime.type="acp"` |
| `ACP runtime backend is not configured` | `@openclaw/acpx` 没装 / 被 `plugins.allow` 拦 / 网关没重启 | `openclaw plugins install @openclaw/acpx`;把 `acpx` 加进 `plugins.allow`;重启网关 |
| 回复看着对但 DSH 侧没有新会话 | 其实是 OpenClaw 内嵌运行时干的 | 看 `executionTrace.runner`;确认 `agent:dsh:acp:<uuid>` 子会话与 `~/.dsh/sessions/...-workspace-dsh--` |
| `PermissionPromptUnavailableError: Permission prompt unavailable in non-interactive mode` | 非交互 ACP 会话撞上写/执行门禁 | `plugins.entries.acpx.config.permissionMode = "approve-all"`(或 `nonInteractivePermissions="deny"` 降级) |
| `dsh: profile "acp" does not exist` | dsh 版本 < 0.1.5(无内置 acp 模板) | 升级 dsh,或 `dsh plugin --profile acp add @deepseek-ai/dsh-acp` 后手写 profile |
| 网关日志 `security warning: dangerous config flags … permissionMode=approve-all` | 预期告警 | `approve-all` 等于把 bk02 上的写/执行权限交给 DSH 会话,见下方安全说明 |
| Web 打开 401 | 没带 token | 从 `journalctl --user -u dsh-web` 取带 token 的 URL |
| Web 启动即崩、日志报 `$.host expected …` | `--host` 传了具体 IP | 改 `127.0.0.1`+ tailscale serve |
| `dsh` 命令找不到 | 该 shell 没加载 nvm 的 Node | `export PATH=$HOME/.nvm/versions/node/v26.8.2/bin:$PATH` |
| `dsh plugin …``pnpm not found on PATH` | 本机没装 pnpm`dsh plugin` 只是 pnpm 转发器) | `npm i -g pnpm` |
| ACP 会话报 JSON 解析错 / 客户端把某行当非法帧 | acp profile 里装了会往 **stdout** 打印的插件(已知:`skillhub-plugin` 自检行) | `dsh plugin --profile acp remove skillhub-plugin`;装任何插件后都跑一遍冒烟脚本确认无「非 JSON 的 stdout 输出」 |
**安全说明**`permissionMode=approve-all` 让 DSH 会话在 bk02 上自动获得写文件/执行命令的许可(这是「让 DSH 干活」的代价)。可收紧为 `approve-reads`,但写/执行类任务会需要交互确认,非交互会话可能直接失败。DSH 自身仍带 `workspace-write` 沙箱 preset 与 `approval/policy: ask` 记录,可在 `~/.dsh/sessions/*/session.v3.jsonl.zstd` 审计。
---
## 10. 回滚
```bash
# OpenClaw:配置回滚到接入前(备份在 ~/.openclaw/openclaw.json.bak-<ts>-pre-dsh-acp
cp ~/.openclaw/openclaw.json.bak-20260916-170416-pre-dsh-acp ~/.openclaw/openclaw.json
openclaw gateway restart
# DSH Web:停用 + 撤掉 tailscale 暴露
systemctl --user disable --now dsh-web
sudo -n tailscale serve --https=18787 off
# DSH 本体
npm rm -g @deepseek-ai/dsh # 配置与历史留在 ~/.dsh(可整体删除)
```
---
## 11. 变更记录
| 时间 | 变更 |
|---|---|
| 2026-09-16 17:01 | bk02 安装 `@deepseek-ai/dsh@0.1.5-rc.1`nvm v26.8.2),自动初始化 `acp` profile |
| 2026-09-16 17:02 | 写入 `~/.dsh/.env``DEEPSEEK_API_KEY`ACP stdio 冒烟通过 |
| 2026-09-16 17:04 | 安装 `@openclaw/acpx``plugins.allow` 放行;写入 `acp` 段与 acpx `agents.dsh`;网关重启 |
| 2026-09-16 17:11 | 创建 OpenClaw agent `dsh`workspace-dsh)并接 ACP runtime |
| 2026-09-16 17:12 | 端到端 ACP 派发成功(proof2/proof3 由 DSH 创建) |
| 2026-09-16 17:15 | `dsh-web.service` 落地(127.0.0.1:18787+ `tailscale serve --https=18787`;跨机访问 200 |
| 2026-09-16 17:20 | 查明微信/飞书/钉钉渠道**均无** `conversationBindings` 能力(仅 Telegram 等内置渠道有)→ 排除 `/acp spawn --bind here` 与持久 `bindings[]` 两条路;部署技能 `~/.openclaw/skills/delegate-to-dsh/SKILL.md` 并重启网关 |
| 2026-09-16 17:21 | 自然语言派发验证通过(DSH 侧新会话 `332ef715…`),「微信 → OpenClaw → DSH → 结果回传」链路可用 |
| 2026-09-16 17:23 | 装 pnpm 12.4.2web profile 增装 `skillhub-plugin` + `superpowers-dsh`;写 `~/.dsh/AGENTS.md` 全局中文指令(交互 + 思考) |
| 2026-09-16 17:24 | 发现 `skillhub-plugin` 自检用 `console.log` 污染 ACP stdout → 从 acp profile 移除(保留 `superpowers-dsh`),复测协议流干净;语言复测中文生效 |
| 2026-09-16 17:25 | 端到端回归:OpenClaw 派发仍正常(DSH 会话 6→7,产物 `nl3.txt` |