File contents DeepSeek Harness (dsh) 运维与扩展
When to Use
安装/启动/重启 dsh(DeepSeek Harness)服务,或排查其故障
配置 dsh 的模型 provider、搜索 provider、credentials
为 dsh 写自定义插件(尤其 web seam 搜索 provider)
在慢速/不稳定代理下 clone 大仓库
对照 dsh 与 Hermes 的 agent harness 架构差异
DeepSeek AI 的开源 agent harness(npx @deepseek-ai/dsh web,默认 http://127.0.0.1:3080)。架构信条:**everything is a plugin**,基于 vendored 的 Cordis 框架(vendor/cordis/,仅 9 个 TS 文件 2693 行)。本机部署于 F:\AI\npm\dsh(版本锚定 0.1.1-rc.2 ,2026-08-16 从 rc.6 升级;升级后以源码/README 重新核对),profile 在 ~/.dsh/profiles/web/,源码 clone 于 F:\AI\Hermes\Sessions\GitHub\deepseek-harness。rc.6→rc.2 已知差异 :web_search 参数 query:string→queries:string[]、credentials 事件改名 credentials/reference-updated、client UI 新增 dsh.client.external 字段(typert/patch/loader/pnpm link: 均无变化)。
安装与启动
npm install -g @deepseek-ai/dsh;启动 dsh web(后台常驻),验证 curl http://127.0.0.1:3080 返回 200。
npm 语义边界 :npm install -g 只用于 dsh 本体 安装/更新(官方路径,见 dsh-install-update 技能);profile 内(~/.dsh/profiles/web)严禁 npm install ——npm 会在 profile 树复制 @deepseek-ai 副本导致双实例崩溃、删 junction 时跟随清空全局树。profile 插件安装一律 pnpm install / dsh plugin(官方唯一入口,转发 pnpm)。
npm script-shell 坑 :本机 .npmrc 配置了 script-shell = D:\Git\usr\bin\bash.exe,npm 用 bash.exe 执行 lifecycle scripts 时崩溃(exit 0xC0000142,DLL 初始化失败)。对策:安装时临时覆盖 --script-shell "C:\Windows\System32\cmd.exe",不要改全局配置。
端口残留 :kill 托管进程后 node 子进程常占 3080。netstat -ano | grep ":3080" | grep LISTEN 找 pid,git-bash 的 taskkill 参数转换有问题,用 powershell.exe -NoProfile -Command "Stop-Process -Id <pid> -Force"。
配置(模型与搜索)
~/.dsh/settings.yaml:模型 provider 配置(llm-pi-ai.providers.<name> + agent-default-model)。会话级模型选择优先于默认值 ——改了 agent-default-model 后必须新建会话才生效。
~/.dsh/.credentials.yaml:密钥存储(YAML 键值)。credentials-local 用 chokidar watcher 观察文件 ——外部编辑自动热重载整快照(支持轮转);resolve(ref) 是同步内存查询(values Map + env 继承 + dotenv fallback),不是每次重读文件。key 解析走 ctx.get('credentials') seam。
本机模型:opencode-go provider(OPENCODE_GO_API_KEY),模型含 deepseek-v4-flash 等;搜索 key 已迁入:EXA/FIRECRAWL/TAVILY/PARALLEL。
架构速览(源码级事实)
技能机制差异(dsh vs Hermes) :dsh 技能目录是会话内动态替换 ——技能文件变更 → Chokidar 失效目录 → skills/change → 下轮注入 update: true 完整新目录("replaces every earlier available-skills list in this session"),无需新会话即生效 ;Hermes 的技能目录在系统提示中固化,需新会话。改 dsh 技能后当前会话即可验证。
概念
要点
Cordis
插件=Service 对象;Context 是 Proxy;extend()/isolate()/intercept() 创建作用域子上下文;事件 5 种派发(emit/parallel/serial/bail/waterfall);注册是可逆 effect,卸载自动 unwind
Fiber
插件实例;状态机 PENDING→LOADING→ACTIVE→UNLOADING→DISPOSED;_refresh() 把 inject 服务的 fiber uid 拼成 epoch,epoch 变化即重载——加载顺序完全由依赖声明驱动
Session log
追加式 SessionEvent[],seq = log.length 连续;"Model-visible means logged" 运行时不变式;deriveMessages() 从日志派生模型历史(带缓存、deep-frozen)
Capability seam
三角色:Service Definition + Provider + Consumer(模型面对的工具)。换 provider 即换整个行为
web seam
ctx.web:search/fetch 两个命名空间,多 provider 注册但单选路由 (resolveProvider:配置 id → 唯一可用 → AMBIGUOUS 抛错),原生无 fallback
插件开发(核心机制)
详见 references/plugin-dev.md。关键点:
loader 按行 import :cordis.patch.yml 的一行 = 一个插件。name 支持裸包名或相对路径 (./plugins/xxx/index.js,按 ctx.baseUrl=profile 目录解析)——自写插件无需 pnpm/node_modules,放 profile 目录下即可。
patch 语法 :改已有行 = 同 id 匹配后逐键覆盖 (config 整体替换、无深合并;未提及的键保留原值;name 若给出必须与现有行一致,否则 warn 跳过);插新行必须用 - insert: [...] (直接加行会 warn: patch: entry not found 并静默跳过——插件不生效、无报错)。
ESM 目录 import 坑 :name 写目录会 ERR_UNSUPPORTED_DIR_IMPORT——必须写完整文件路径 ./plugins/xxx/index.js。
typert 贡献者导出坑 (实证):exports["./typert"] 指向的模块必须具名导出 TYPERT (export const TYPERT = {...} 或 export { TYPERT })——typert-loader 用 mod.TYPERT 读取;只有 export default TYPERT 时 mod.TYPERT === undefined → plugin tree failed to load: typert-loader ... has no TYPERT manifest object → 整棵插件树加载失败、dsh web 起不来 (桌面端表现为启动报错)。manifest 要求:package=包名、face:"host"、schemas 数组(zod v4 实例)、model.services/events/objects 数组(均可空)。修复后必须重启 dsh 进程。
WebSearchProvider 接口 :{ id, available(), search(request, signal) }。available() 是同步无网络 检查,语义=配置完备性(官方 DeepSeek provider 检查 apiKey 或 resolveApiKey 存在),不是值存在性 ——key 在 credentials 里时同步查不到,误判 unavailable 会导致 WEB_PROVIDER_CONFIGURED_UNAVAILABLE。
credentials 获取 :用 ctx.get('credentials')(未注入返回 undefined,不抛错),然后 await credentials.resolve(ref) 取 .value。不要直接属性访问 ctx.credentials。
UI 呈现链路 :工具结果 content → tool-web searchMetaFromValue() 映射为 meta.answer → presentResult 组装 card: 'web' 卡片 → client WebBlock 渲染(answer 在引用列表上方)。自定义 provider 的 content 会自动出现在聊天页工具卡片里 (模型同见)。
运行时 client UI 插件可行 :npm 安装版可挂载 client 插件——必须用包名挂载(profile package.json file: 依赖 + pnpm install ;严禁 npm install :npm 会在 profile 树复制 @deepseek-ai 副本导致双实例崩溃,且删除 junction 时跟随清空全局树,见排障 Pitfalls 双实例条目),手写 __ModuleLoader__ bundle(dsh.client 声明 + exports["./client"] + exports.inject 声明用到的 ctx 服务 )。四个坑:目录名 → ERR_UNSUPPORTED_DIR_IMPORT 整站黑屏;exports 缺 ./package.json → client-modules 静默跳过;__ModuleLoader__.load 的 id 必须等于包名 (写成别名如加 -ui 后缀 → bundle 加载成功但注册失败:loaded without registering via __ModuleLoader__.load——mock 执行 factory 才能抓到);dsh.client.inject 声明决定模块表内容 ——require("react")/primitives 能否解析由 inject 的包决定,缺声明 → factory 抛异常 → 同样静默不注册。细节以项目技能 dsh-client-ui-plugin-dev(F:\AI\DeepSeek Harness\dsh-plugins-yyhuang.dsh\skills\)为准 。验证:__DSH_BOOT__.entries 含包名 → GET /plugins/<包名>/client.js 200(不是 patch id,会 404 假阴性)。
host 侧 userQuestions.ask() 在 web 模式要求 agent-owned session (rc.2 实测):从 session/event 回调触发 ask 时必须传 { agent: ctx.agents.get(sessionId) } ,否则报 web user interaction requires an agent-owned session;早期"不传 agent 可绕过 DELEGATED_CALLER 校验"的说法在 rc.2 已失效。
agent.inject() 的 idle-pending 陷阱 :inject 只排队不唤醒——idle agent 的注入消息静默滞留直到下次 followup()/steer()。需要"立即执行"的场景用 agent.followup(msg) (唤醒并开新轮);inject 适合"下一轮自然生效"的场景。
消息级按钮挂点 = conversation.chat.assistant-actions 槽 :复制/分叉/点赞所在的 IconActions 行(官方 message-feedback 插件即此模式:Tooltip + primitives 图标 + 无边框 action 按钮);chain/list 槽的 select() 返回 match 数据或 null(decline),不要返回布尔 true (不可靠)。turnTail 槽是另一挂点但渲染在 IconActions 之前、仅完结 turn。
官方文档锚点(插件管理权威依据)
以下结论来自官方文档全库检索(npm install 在插件场景零出现)与逐条核对,是本技能 npm/pnpm 纪律的官方依据。文档路径相对源码克隆 F:\AI\Hermes\Sessions\GitHub\deepseek-harness\。
dsh plugin 是官方唯一插件管理入口 :apps/cli/README.md:14 —— "Manage a profile's plugins by forwarding to pnpm in the profile directory"(转发 pnpm,非 npm)
bundles 解析顺序 :apps/cli/README.md:39 —— bundles 先从 dsh 安装本体解析(@deepseek-ai/dsh-base 等),再查 profile 的 node_modules(pnpm 装的 out-of-tree 插件)。两棵树职责分离,@deepseek-ai 永远只有本体一份
官方 manifest 示例用 link: 协议 :docs/user/develop/basic/publish.md:85-101 —— "dsh-hello-plugin": "link:/path/to/hello-plugin"(本地插件官方示例即 link:,与双实例修复的 link: 指向全局同构)
无 dsh.bundle 声明 = 普通依赖 :publish.md:64 —— 安装但 "activates no layer"(只作库,不作插件)
profile manifest 由 dsh plugin 维护 :publish.md:73 —— "You never write a profile manifest by hand"
npm install 官方定位 :全库检索零出现于插件安装场景(仅构建/工具描述/sandbox 文档)——profile 内用它即脱离官方轨道
搜索 provider 生态与 fallback
官方包:web-search-deepseek(默认,需 DEEPSEEK_API_KEY,一次搜索=一次完整模型调用)、web-search-exa、web-search-perplexity。base bundle 默认 searchProvider: deepseek-official 且 tool-web.fetch: false。
fallback 聚合插件 (本机已部署,挂载为 @yyhuang/web-search-fallback 包名——源码在 ~/.dsh/profiles/web/plugins/web-search-fallback/index.js,package.json file: 依赖 + patch 引用包名):自写 WebSearchProvider,内部按优先级持有多个子 provider(exa→firecrawl→tavily→parallel),逐个尝试、失败记录并切换、全部失败抛 WEB_PROVIDER_ERROR;调用链格式化为多行 markdown 写进 result.content(显示为卡片 answer 区)。完整设计、4 个 API 格式、3 个实测修复见 references/fallback-search-plugin.md。
Hermes 侧搜索(对照参考):web/ddgs 插件(DuckDuckGo,免费无 key)是 Hermes 生效链路;原生 web_search/web_extract 未禁用(config.yaml 无 disabled_toolsets 键);.env 里 4 个搜索 key(EXA/FIRECRAWL/TAVILY/PARALLEL)已配但 web.search_backend 为空。
代理下大仓 clone 恢复
本机 7898 socks5 代理连 GitHub 极不稳定 (TLS 频繁 SSL_read: unexpected eof),7890 仅主页可达(codeload/API 403)。12K commits 大仓策略:
git clone --filter=blob:none(blobless:元数据完整、blob 按需)可能会 checkout 失败但元数据已完整 (Clone succeeded, but checkout failed)——不要重删重 clone!
恢复:git read-tree HEAD 重建 index(纯本地,不触发网络)→ 循环 git checkout-index --stdin(每轮取缺失文件前 250 个 + timeout 100 防挂起)直到文件齐全。脚本模式见 references/proxy-clone-recovery.md。
代理配置写仓库局部 config(git config --local http.proxy),加 http.version=HTTP/1.1 + http.postBuffer=524288000 降低断连率。
排障 Pitfalls
双实例 bug(两次事故后根治,详见 provenance) :profile 装第三方插件(如 dsh-better-sidebar,peerDependencies 声明 16 个 @deepseek-ai 包)时,任何包管理器都会在 profile 树引入 @deepseek-ai 副本 → dsh 本体(全局树 F:\AI\npm)与插件(profile 树)加载两个不同物理实例 → kScope = Symbol("dsh.scope") 不匹配 → 所有会话报 agent-presets: refusing to compose an unscoped context。正确配置 :
profile package.json 的 dependencies 显式声明 better-sidebar 的全部 peer 为 link: 协议指向全局树 :"@deepseek-ai/cordis": "link:F:/AI/npm/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/cordis"(16 个 @deepseek-ai + cordis)
pnpm-workspace.yaml 不要用 nodeLinker: hoisted (会复制 file: 依赖);用 pnpm 默认 isolated 模式(symlink)
只用 pnpm 管理 profile(dsh plugin 同源);严禁 npm install (npm 的扁平复制 + junction 跟随删除会破坏全局树)
不要用手工 junction 代替 link: (npm install 删除 junction 时会跟随链接删除全局树目录——实证:全局树 62 个包目录被清空)
验证三件套:node -e "fs.realpathSync('.../node_modules/@deepseek-ai/cordis')" 应指向 F:\AI\npm → 从 dshmarket 与全局 loader 视角 require cordis,Context === Context 应为 true → 重启 dsh 后新建+resume 会话
zod 版本分裂(双实例家族变体 3,2026-08-16 实测:typert 插件集体注册失败) :新装插件(如 dsh-vision-router)带进传递依赖 zod@3.x ,pnpm 将其 hoist 到 .pnpm/node_modules/zod;typert 插件的 import('zod') 从 store 位置向上解析时先命中 hoisted 根 (任意版本),而非 profile 顶层 link:(4.4.3)→ typert-loader 报 not backed by a zod v4 schema → 整棵插件树加载失败、dsh 起不来 。修复铁律 :所有 typert 插件的 package.json 必须显式声明 "zod": "link:F:/AI/npm/node_modules/@deepseek-ai/dsh/node_modules/zod"——让 pnpm 在插件自己 node_modules 建 4.4.3 链接,先于 hoisted 根命中;仅 profile 顶层 link: 不够(Node 向上解析先命中 hoisted 根)。通用模式:新装插件可能改变已有插件的共享依赖解析路径——"插件没改却突然报错"时先查依赖树变化(.pnpm/ 新条目 + package.json 最近修改时间) 。
改 host 插件代码后必须重启 dsh 进程 (web profile 的 hmr 插件行 disabled: true,Node ESM 缓存——实证);client.js 例外 (dsh-client-hmr 500ms 轮询自动热更新,无需重启)。
流程纪律 :涉及写代码/多文件改动时,先向用户给出方案 + 决策点再动手——用户会在"需求陈述被直接当成执行授权"时叫停("先讨论方案啊")。需求陈述 ≠ 执行授权。
搜索验证三件套:Node 直测 provider(key 有效性+响应解析)→ dsh --profile web --dump-config(确认插件行进树)→ 浏览器端到端(真实搜索看卡片)。
WebSearchResult.sources 有上限(tool-web searchMaxResults 默认 8);provider 超限由 seam 截断。
pnpm supply-chain 策略 :minimumReleaseAgeExclude 只接受精确版本 (不支持 1.3.x 通配,会报 ERR_PNPM_INVALID_MINIMUM_RELEASE_AGE_EXCLUDE);新装包发布时间太新会拦 ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION——加精确版本豁免即可。
参考文件
references/plugin-dev.md — loader/patch/接口/呈现机制细节 + 官方源码引用点
references/fallback-search-plugin.md — fallback 插件完整设计、4 API 格式、3 bug 修复记录、配置示例
references/proxy-clone-recovery.md — 代理不稳定下 blobless clone 恢复脚本模式
1 --- 2 name: dsh-ops 3 description: Use when 运维/扩展 dsh (DeepSeek Harness):安装配置、自定义插件、排障。 4 license: MIT 5 --- 6 7 # DeepSeek Harness (dsh) 运维与扩展 8 9 ## When to Use 10 11 - 安装/启动/重启 dsh(DeepSeek Harness)服务,或排查其故障 12 - 配置 dsh 的模型 provider、搜索 provider、credentials 13 - 为 dsh 写自定义插件(尤其 web seam 搜索 provider) 14 - 在慢速/不稳定代理下 clone 大仓库 15 - 对照 dsh 与 Hermes 的 agent harness 架构差异 16 17 DeepSeek AI 的开源 agent harness(`npx @deepseek-ai/dsh web`,默认 http://127.0.0.1:3080)。架构信条:**everything is a plugin**,基于 vendored 的 Cordis 框架(`vendor/cordis/`,仅 9 个 TS 文件 2693 行)。本机部署于 `F:\AI\npm\dsh`(**版本锚定 0.1.1-rc.2**,2026-08-16 从 rc.6 升级;升级后以源码/README 重新核对),profile 在 `~/.dsh/profiles/web/`,源码 clone 于 `F:\AI\Hermes\Sessions\GitHub\deepseek-harness`。**rc.6→rc.2 已知差异**:`web_search` 参数 `query:string`→`queries:string[]`、credentials 事件改名 `credentials/reference-updated`、client UI 新增 `dsh.client.external` 字段(typert/patch/loader/pnpm link: 均无变化)。 18 19 ## 安装与启动 20 21 - `npm install -g @deepseek-ai/dsh`;启动 `dsh web`(后台常驻),验证 `curl http://127.0.0.1:3080` 返回 200。 22 - **npm 语义边界**:`npm install -g` 只用于 **dsh 本体**安装/更新(官方路径,见 `dsh-install-update` 技能);**profile 内(`~/.dsh/profiles/web`)严禁 npm install**——npm 会在 profile 树复制 @deepseek-ai 副本导致双实例崩溃、删 junction 时跟随清空全局树。profile 插件安装一律 `pnpm install` / `dsh plugin`(官方唯一入口,转发 pnpm)。 23 - **npm script-shell 坑**:本机 `.npmrc` 配置了 `script-shell = D:\Git\usr\bin\bash.exe`,npm 用 bash.exe 执行 lifecycle scripts 时崩溃(exit 0xC0000142,DLL 初始化失败)。对策:安装时临时覆盖 `--script-shell "C:\Windows\System32\cmd.exe"`,不要改全局配置。 24 - **端口残留**:kill 托管进程后 node 子进程常占 3080。`netstat -ano | grep ":3080" | grep LISTEN` 找 pid,git-bash 的 taskkill 参数转换有问题,用 `powershell.exe -NoProfile -Command "Stop-Process -Id <pid> -Force"`。 25 26 ## 配置(模型与搜索) 27 28 - `~/.dsh/settings.yaml`:模型 provider 配置(`llm-pi-ai.providers.<name>` + `agent-default-model`)。**会话级模型选择优先于默认值**——改了 `agent-default-model` 后必须新建会话才生效。 29 - `~/.dsh/.credentials.yaml`:密钥存储(YAML 键值)。credentials-local 用 **chokidar watcher 观察文件**——外部编辑自动热重载整快照(支持轮转);`resolve(ref)` 是同步内存查询(values Map + env 继承 + dotenv fallback),不是每次重读文件。key 解析走 `ctx.get('credentials')` seam。 30 - 本机模型:opencode-go provider(`OPENCODE_GO_API_KEY`),模型含 deepseek-v4-flash 等;搜索 key 已迁入:EXA/FIRECRAWL/TAVILY/PARALLEL。 31 32 ## 架构速览(源码级事实) 33 34 > **技能机制差异(dsh vs Hermes)**:dsh 技能目录是**会话内动态替换**——技能文件变更 → Chokidar 失效目录 → `skills/change` → 下轮注入 `update: true` 完整新目录("replaces every earlier available-skills list in this session"),**无需新会话即生效**;Hermes 的技能目录在系统提示中固化,需新会话。改 dsh 技能后当前会话即可验证。 35 36 | 概念 | 要点 | 37 |---|---| 38 | Cordis | 插件=Service 对象;Context 是 Proxy;`extend()/isolate()/intercept()` 创建作用域子上下文;事件 5 种派发(emit/parallel/serial/bail/waterfall);注册是可逆 effect,卸载自动 unwind | 39 | Fiber | 插件实例;状态机 PENDING→LOADING→ACTIVE→UNLOADING→DISPOSED;`_refresh()` 把 inject 服务的 fiber uid 拼成 epoch,epoch 变化即重载——加载顺序完全由依赖声明驱动 | 40 | Session log | 追加式 `SessionEvent[]`,`seq = log.length` 连续;"Model-visible means logged" 运行时不变式;`deriveMessages()` 从日志派生模型历史(带缓存、deep-frozen) | 41 | Capability seam | 三角色:Service Definition + Provider + Consumer(模型面对的工具)。换 provider 即换整个行为 | 42 | web seam | `ctx.web`:search/fetch 两个命名空间,多 provider 注册但**单选路由**(`resolveProvider`:配置 id → 唯一可用 → AMBIGUOUS 抛错),**原生无 fallback** | 43 44 ## 插件开发(核心机制) 45 46 详见 `references/plugin-dev.md`。关键点: 47 48 1. **loader 按行 import**:`cordis.patch.yml` 的一行 = 一个插件。`name` 支持裸包名或**相对路径**(`./plugins/xxx/index.js`,按 `ctx.baseUrl`=profile 目录解析)——自写插件无需 pnpm/node_modules,放 profile 目录下即可。 49 2. **patch 语法**:改已有行 = 同 `id` 匹配后**逐键覆盖**(config 整体替换、无深合并;未提及的键保留原值;name 若给出必须与现有行一致,否则 warn 跳过);**插新行必须用 `- insert: [...]`**(直接加行会 `warn: patch: entry not found` 并静默跳过——插件不生效、无报错)。 50 3. **ESM 目录 import 坑**:`name` 写目录会 `ERR_UNSUPPORTED_DIR_IMPORT`——必须写完整文件路径 `./plugins/xxx/index.js`。 51 4. **typert 贡献者导出坑**(实证):`exports["./typert"]` 指向的模块必须**具名导出 `TYPERT`**(`export const TYPERT = {...}` 或 `export { TYPERT }`)——typert-loader 用 `mod.TYPERT` 读取;只有 `export default TYPERT` 时 `mod.TYPERT === undefined` → `plugin tree failed to load: typert-loader ... has no TYPERT manifest object` → **整棵插件树加载失败、dsh web 起不来**(桌面端表现为启动报错)。manifest 要求:`package`=包名、`face:"host"`、`schemas` 数组(zod v4 实例)、`model.services/events/objects` 数组(均可空)。修复后必须重启 dsh 进程。 52 5. **WebSearchProvider 接口**:`{ id, available(), search(request, signal) }`。`available()` 是**同步无网络**检查,语义=配置完备性(官方 DeepSeek provider 检查 `apiKey` 或 `resolveApiKey` 存在),**不是值存在性**——key 在 credentials 里时同步查不到,误判 unavailable 会导致 `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`。 53 6. **credentials 获取**:用 `ctx.get('credentials')`(未注入返回 undefined,不抛错),然后 `await credentials.resolve(ref)` 取 `.value`。不要直接属性访问 `ctx.credentials`。 54 7. **UI 呈现链路**:工具结果 content → tool-web `searchMetaFromValue()` 映射为 `meta.answer` → `presentResult` 组装 `card: 'web'` 卡片 → client `WebBlock` 渲染(answer 在引用列表上方)。**自定义 provider 的 content 会自动出现在聊天页工具卡片里**(模型同见)。 55 8. **运行时 client UI 插件可行**:npm 安装版可挂载 client 插件——必须用包名挂载(profile package.json `file:` 依赖 + **`pnpm install`**;**严禁 npm install**:npm 会在 profile 树复制 @deepseek-ai 副本导致双实例崩溃,且删除 junction 时跟随清空全局树,见排障 Pitfalls 双实例条目),手写 `__ModuleLoader__` bundle(`dsh.client` 声明 + `exports["./client"]` + **`exports.inject` 声明用到的 ctx 服务**)。四个坑:目录名 → `ERR_UNSUPPORTED_DIR_IMPORT` 整站黑屏;exports 缺 `./package.json` → client-modules 静默跳过;**`__ModuleLoader__.load` 的 id 必须等于包名**(写成别名如加 `-ui` 后缀 → bundle 加载成功但注册失败:`loaded without registering via __ModuleLoader__.load`——mock 执行 factory 才能抓到);**`dsh.client.inject` 声明决定模块表内容**——`require("react")`/primitives 能否解析由 inject 的包决定,缺声明 → factory 抛异常 → 同样静默不注册。**细节以项目技能 `dsh-client-ui-plugin-dev`(F:\AI\DeepSeek Harness\dsh-plugins-yyhuang\.dsh\skills\)为准**。验证:`__DSH_BOOT__.entries` 含包名 → `GET /plugins/<包名>/client.js` 200(不是 patch id,会 404 假阴性)。 56 9. **host 侧 userQuestions.ask() 在 web 模式要求 agent-owned session**(rc.2 实测):从 `session/event` 回调触发 ask 时**必须传 `{ agent: ctx.agents.get(sessionId) }`**,否则报 `web user interaction requires an agent-owned session`;早期"不传 agent 可绕过 DELEGATED_CALLER 校验"的说法在 rc.2 已失效。 57 10. **agent.inject() 的 idle-pending 陷阱**:inject 只排队不唤醒——idle agent 的注入消息静默滞留直到下次 followup()/steer()。需要"立即执行"的场景用 **`agent.followup(msg)`**(唤醒并开新轮);inject 适合"下一轮自然生效"的场景。 58 11. **消息级按钮挂点 = `conversation.chat.assistant-actions` 槽**:复制/分叉/点赞所在的 IconActions 行(官方 message-feedback 插件即此模式:Tooltip + primitives 图标 + 无边框 action 按钮);chain/list 槽的 `select()` 返回 **match 数据或 null(decline),不要返回布尔 true**(不可靠)。turnTail 槽是另一挂点但渲染在 IconActions 之前、仅完结 turn。 59 60 ## 官方文档锚点(插件管理权威依据) 61 62 > 以下结论来自官方文档全库检索(npm install 在插件场景零出现)与逐条核对,是本技能 npm/pnpm 纪律的官方依据。文档路径相对源码克隆 `F:\AI\Hermes\Sessions\GitHub\deepseek-harness\`。 63 64 - **`dsh plugin` 是官方唯一插件管理入口**:`apps/cli/README.md:14` —— "Manage a profile's plugins by **forwarding to pnpm** in the profile directory"(转发 pnpm,非 npm) 65 - **bundles 解析顺序**:`apps/cli/README.md:39` —— bundles 先从 dsh 安装本体解析(`@deepseek-ai/dsh-base` 等),再查 profile 的 node_modules(pnpm 装的 out-of-tree 插件)。两棵树职责分离,@deepseek-ai 永远只有本体一份 66 - **官方 manifest 示例用 `link:` 协议**:`docs/user/develop/basic/publish.md:85-101` —— "dsh-hello-plugin": "link:/path/to/hello-plugin"(本地插件官方示例即 link:,与双实例修复的 link: 指向全局同构) 67 - **无 `dsh.bundle` 声明 = 普通依赖**:`publish.md:64` —— 安装但 "activates no layer"(只作库,不作插件) 68 - **profile manifest 由 `dsh plugin` 维护**:`publish.md:73` —— "You never write a profile manifest by hand" 69 - **npm install 官方定位**:全库检索零出现于插件安装场景(仅构建/工具描述/sandbox 文档)——profile 内用它即脱离官方轨道 70 71 ## 搜索 provider 生态与 fallback 72 73 - 官方包:`web-search-deepseek`(默认,需 DEEPSEEK_API_KEY,一次搜索=一次完整模型调用)、`web-search-exa`、`web-search-perplexity`。base bundle 默认 `searchProvider: deepseek-official` 且 `tool-web.fetch: false`。 74 - **fallback 聚合插件**(本机已部署,挂载为 `@yyhuang/web-search-fallback` 包名——源码在 `~/.dsh/profiles/web/plugins/web-search-fallback/index.js`,package.json `file:` 依赖 + patch 引用包名):自写 WebSearchProvider,内部按优先级持有多个子 provider(exa→firecrawl→tavily→parallel),逐个尝试、失败记录并切换、全部失败抛 `WEB_PROVIDER_ERROR`;调用链格式化为多行 markdown 写进 result.content(显示为卡片 answer 区)。完整设计、4 个 API 格式、3 个实测修复见 `references/fallback-search-plugin.md`。 75 - Hermes 侧搜索(对照参考):`web/ddgs` 插件(DuckDuckGo,免费无 key)是 Hermes 生效链路;原生 `web_search`/`web_extract` 未禁用(config.yaml 无 `disabled_toolsets` 键);`.env` 里 4 个搜索 key(EXA/FIRECRAWL/TAVILY/PARALLEL)已配但 `web.search_backend` 为空。 76 77 ## 代理下大仓 clone 恢复 78 79 本机 7898 socks5 代理连 GitHub **极不稳定**(TLS 频繁 `SSL_read: unexpected eof`),7890 仅主页可达(codeload/API 403)。12K commits 大仓策略: 80 81 1. `git clone --filter=blob:none`(blobless:元数据完整、blob 按需)**可能会 checkout 失败但元数据已完整**(`Clone succeeded, but checkout failed`)——不要重删重 clone! 82 2. 恢复:`git read-tree HEAD` 重建 index(纯本地,不触发网络)→ 循环 `git checkout-index --stdin`(每轮取缺失文件前 250 个 + `timeout 100` 防挂起)直到文件齐全。脚本模式见 `references/proxy-clone-recovery.md`。 83 3. 代理配置写**仓库局部** config(`git config --local http.proxy`),加 `http.version=HTTP/1.1` + `http.postBuffer=524288000` 降低断连率。 84 85 ## 排障 Pitfalls 86 87 - **双实例 bug(两次事故后根治,详见 provenance)**:profile 装第三方插件(如 dsh-better-sidebar,peerDependencies 声明 16 个 @deepseek-ai 包)时,任何包管理器都会在 profile 树引入 @deepseek-ai 副本 → dsh 本体(全局树 F:\AI\npm)与插件(profile 树)加载**两个不同物理实例** → `kScope = Symbol("dsh.scope")` 不匹配 → 所有会话报 `agent-presets: refusing to compose an unscoped context`。**正确配置**: 88 1. profile `package.json` 的 dependencies 显式声明 better-sidebar 的全部 peer 为 **`link:` 协议指向全局树**:`"@deepseek-ai/cordis": "link:F:/AI/npm/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/cordis"`(16 个 @deepseek-ai + cordis) 89 2. `pnpm-workspace.yaml` **不要用 `nodeLinker: hoisted`**(会复制 file: 依赖);用 pnpm 默认 isolated 模式(symlink) 90 3. 只用 **pnpm** 管理 profile(`dsh plugin` 同源);**严禁 npm install**(npm 的扁平复制 + junction 跟随删除会破坏全局树) 91 4. **不要用手工 junction 代替 link:**(npm install 删除 junction 时会跟随链接删除全局树目录——实证:全局树 62 个包目录被清空) 92 5. 验证三件套:`node -e "fs.realpathSync('.../node_modules/@deepseek-ai/cordis')"` 应指向 F:\AI\npm → 从 dshmarket 与全局 loader 视角 require cordis,`Context === Context` 应为 true → 重启 dsh 后新建+resume 会话 93 - **zod 版本分裂(双实例家族变体 3,2026-08-16 实测:typert 插件集体注册失败)**:新装插件(如 dsh-vision-router)带进传递依赖 zod@3.x,pnpm 将其 hoist 到 `.pnpm/node_modules/zod`;typert 插件的 `import('zod')` 从 store 位置向上解析时**先命中 hoisted 根**(任意版本),而非 profile 顶层 link:(4.4.3)→ typert-loader 报 `not backed by a zod v4 schema` → **整棵插件树加载失败、dsh 起不来**。**修复铁律**:所有 typert 插件的 package.json **必须显式声明** `"zod": "link:F:/AI/npm/node_modules/@deepseek-ai/dsh/node_modules/zod"`——让 pnpm 在插件自己 node_modules 建 4.4.3 链接,先于 hoisted 根命中;仅 profile 顶层 link: 不够(Node 向上解析先命中 hoisted 根)。**通用模式:新装插件可能改变已有插件的共享依赖解析路径——"插件没改却突然报错"时先查依赖树变化(`.pnpm/` 新条目 + package.json 最近修改时间)**。 94 - **改 host 插件代码后必须重启 dsh 进程**(web profile 的 `hmr` 插件行 `disabled: true`,Node ESM 缓存——实证);**client.js 例外**(`dsh-client-hmr` 500ms 轮询自动热更新,无需重启)。 95 - **流程纪律**:涉及写代码/多文件改动时,先向用户给出方案 + 决策点再动手——用户会在"需求陈述被直接当成执行授权"时叫停("先讨论方案啊")。需求陈述 ≠ 执行授权。 96 - 搜索验证三件套:Node 直测 provider(key 有效性+响应解析)→ `dsh --profile web --dump-config`(确认插件行进树)→ 浏览器端到端(真实搜索看卡片)。 97 - `WebSearchResult.sources` 有上限(tool-web `searchMaxResults` 默认 8);provider 超限由 seam 截断。 98 - **pnpm supply-chain 策略**:`minimumReleaseAgeExclude` 只接受**精确版本**(不支持 `1.3.x` 通配,会报 `ERR_PNPM_INVALID_MINIMUM_RELEASE_AGE_EXCLUDE`);新装包发布时间太新会拦 `ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION`——加精确版本豁免即可。 99 100 ## 参考文件 101 102 - `references/plugin-dev.md` — loader/patch/接口/呈现机制细节 + 官方源码引用点 103 - `references/fallback-search-plugin.md` — fallback 插件完整设计、4 API 格式、3 bug 修复记录、配置示例 104 - `references/proxy-clone-recovery.md` — 代理不稳定下 blobless clone 恢复脚本模式
yyyyyhhhhh0639/agent-skills/tree/main/skills/deepseek-harness/dsh-ops commit 9da6e31c91
Frequently asked questions How do I install the Dsh Ops skill? Run npx skillmds@latest add yyyyyhhhhh0639/dsh-ops in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
What does the Dsh Ops skill do? Use when 运维/扩展 dsh (DeepSeek Harness):安装配置、自定义插件、排障。 It is listed under Coding & Dev Tools on SkillMD.
Is Dsh Ops safe to use? This skill has not completed SkillMD's automated safety review yet. Capability flags: makes network calls, reads secrets. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
Which AI agents work with Dsh Ops? This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Is Dsh Ops free to use? Yes. Installing skills from SkillMD is free. This skill is licensed under MIT.
Who published Dsh Ops? yyyyyhhhhh0639 (@yyyyyhhhhh0639) published this skill. Their other Agent Skills are listed on their SkillMD profile.