在工具调用执行前拦截危险命令并做本地判断
30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/12 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
相关生态插件(非 dsh 原生,按其对应运行时安装)
git clone https://github.com/Phocinae/dsh-phocinae.git状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:待复验
是什么
生态应用
桌面端 / Web 外壳,不以 dsh plugin add 安装
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 19 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🟢 扫描:未发现敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描未发现敏感能力:最多只发网络请求、读取自身元数据或只读文件,未发现执行命令、文件写入或敏感信息读取。
发起外部网络请求
查看扫描证据(共 1 处,展示前 1 条)
- 发起网络请求lib/protocol.mjs:121
response = await fetch(endpoint, {
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 拦截危险命令
- 执行前本地判断
- 工具调用审批门
README
由 DeepSeek 最新模型翻译生成English · · Español · Português · हिन्दी
dsh-phocinae
DeepSeek Harness 的本地非生成式决策层。 插件 phocinae_ask 和 phocinae_gate。
▲ 读取真实的 package.json:bundle.patch 指向 cordis.patch.yml,engines 同时约束 node 和 dsh。
▲ 合成演示动画(模型仓库 docs/gallery S01):画面是渲染出来的,场景数据是虚构的,但它展示的行为——危险命令在毫秒内被拦下——正是这个插件的 gate 所做的事;下面的终端卡片是真实运行结果。
▲ 真实的 node --test 结果:57/57,包括自动放行边界和未命名工具 fail-closed 回归。
▲ verify-artifacts:不含凭据和本地路径,并列出 npm 实际会发布的内容。
phocinae_ask / phocinae_gate 工具由 Phocinae-Largha-150M-v1 服务支撑——是/否判断、单选和多选、2-10 评分,每次一次前向传播,全在你自己的机器上跑——外加一个 fail-closed 审批 gate,在工具调用执行前先做筛查。
展开完整文档(README 剩余 1 节)
English · · Español · Português · हिन्दी
dsh-phocinae
DeepSeek Harness 的本地非生成式决策层。 插件 phocinae_ask 和 phocinae_gate。
▲ 读取真实的 package.json:bundle.patch 指向 cordis.patch.yml,engines 同时约束 node 和 dsh。
▲ 合成演示动画(模型仓库 docs/gallery S01):画面是渲染出来的,场景数据是虚构的,但它展示的行为——危险命令在毫秒内被拦下——正是这个插件的 gate 所做的事;下面的终端卡片是真实运行结果。
▲ 真实的 node --test 结果:57/57,包括自动放行边界和未命名工具 fail-closed 回归。
▲ verify-artifacts:不含凭据和本地路径,并列出 npm 实际会发布的内容。
phocinae_ask / phocinae_gate 工具由 Phocinae-Largha-150M-v1 服务支撑——是/否判断、单选和多选、2-10 评分,每次一次前向传播,全在你自己的机器上跑——外加一个 fail-closed 审批 gate,在工具调用执行前先做筛查。
这是 0.2.5——它堵上了 0.2.4 自动放行列表里还留着的两个漏洞:命令启动器(find、env、fd、rg)之前按前缀匹配,所以 find / -delete 和 env sh -c "rm -rf /" 能绕过模型直接通过;还有工具调用的 exec.name 缺失时会被直接放行,而不是 fail closed。现在这两种情况都会走到 gate。
dsh plugin --profile <name> add dsh-phocinae@0.2.5
需要 Node ^22.19 或 >=24,以及一个可访问的决策服务(见运行决策服务)。
只跑 npm i 装不上插件
有两件独立的事必须发生,只有第二件才会挂载插件:
| 步骤 | 做什么 |
| npm i dsh-phocinae | 把包放进当前目录的 node_modules。适合读代码,或者直接导入 guard。它不会触及任何 DSH 配置档。 |
| dsh plugin --profile <name> add dsh-phocinae@0.2.5 | 把它加进那个配置档的 package.json(dependencies 和 dsh.profile.bundles),并用 pnpm 装到那里。这样宿主才会挂载它。 |
在本机实测,用一个一次性的 DSH_HOME:
- 在无关目录里执行 npm i dsh-phocinae → 装在那里,配置档的 dependencies 仍是 {},配置档的 bundles 没变,dsh <profile> --dump-config 里看不到这个插件;
- dsh plugin --profile p add dsh-phocinae → 配置档同时获得依赖和 bundle 条目,合成后的配置里出现 phocinae 那一行,带它的 endpoint;
- 有依赖但没有 dsh.profile.bundles 条目 → 装了但不挂载,因为加载器遍历的是 bundle 列表。
锁定版本
add dsh-phocinae 会走 pnpm 的供应链时效门禁(minimumReleaseAge)。比门禁阈值更新的发布不会被选中,所以发布后的一段时间里,光写包名会解析到上一个版本——而这里的上一个版本是 0.1.2,它不能用。
同一台机器上,相隔几分钟实测:
| 时间 | 命令 | 解析到 |
| order 0.2.2 发布 + 2 分钟 | dsh plugin --profile p add dsh-phocinae | 0.1.2 —— main: index.js,那个无法激活的构建 |
| 同上,但用 add dsh-phocinae@0.2.2 | | 0.2.2 —— ./index.mjs,技能在 |
| 同上,光写包名,配置档的 pnpm-workspace.yaml 里设 minimumReleaseAge: 0 | | 0.2.2 |
| order 0.2.2 发布 + 150 分钟 | dsh plugin --profile p add dsh-phocinae | 0.2.2 —— ^0.2.2,宿主启动干净 |
门禁拦下某个版本时,harness 会把这次拒绝记进配置档的 pnpm-workspace.yaml:
minimumReleaseAgeExclude:
- dsh-phocinae@0.2.2
门禁是合理的默认值——刚发布的包,正是供应链攻击的样子。这也意味着版本号应该写出来,既能立刻拿到修好的构建,也能让安装可复现:
dsh plugin --profile <name> add dsh-phocinae@0.2.5
如果光写包名真的让你落到 0.1.2,dsh <profile> --dump-config 会显示挂载的是哪个版本,再用锁定版本号重新添加就能替换掉。
Git 和本地检出用法一样,而且天然锁定版本:
dsh plugin --profile <name> add github:Phocinae/dsh-phocinae#v0.2.5
dsh plugin --profile <name> add /path/to/a/local/checkout
它贡献了什么
| 贡献项 | 接缝 | 作用 |
| phocinae_ask | ctx.tools | 针对本地模型跑一批带类型的问题。每个答案都带校准过的置信度和一个建议性的 escalate 标志。 |
| phocinae_gate | ctx.tools | 判定某条命令或某个动作:allow / ask / deny,并给出置信度。只检查动作,不执行。 |
| approval gate | tools/pre-execute waterfall | 在工具调用执行之前,拦截配置的模式覆盖到的每一次调用。 |
| phocinae skill | ctx.skills | 教模型什么时候该用本地决策、怎么措辞、怎么读 escalate 标志。 |
这不是聊天模型。它不生成文本,不懂事实,也写不了代码。每次前向传播只做一个结构化决策,这正是 gate 需要的全部,不多不少。
0.2.2 修复的问题
下面每一条都在 test/regressions.test.mjs 里有对应测试复现,编号 D1…D12。
D1 — 插件从未激活
dsh: warning: 1 entry did not activate
phocinae (dsh-phocinae): Error: cannot get property "registerTool" without inject
index.js 用 typeof ctx.registerTool === 'function' 做防御性检查。Cordis 上下文是个代理:读取未声明的服务名会抛错,而不是返回 undefined。registerTool 不是 DSH 服务——注册表在 ctx.tools——所以这个检查在插件主体第一行就抛了,整个条目还没订阅任何东西就挂了。
修复: 工具注册改为通过 ctx.inject(['tools'], …) 等待服务。不做检查,不读未声明的名字,而且就算服务一直不出现,插件照样把 gate 武装起来。
D2 — 工具定义的结构不对
定义里带的是 {inputSchema, handler}。ToolDefinition 继承 ToolSchema,后者声明的是 {name, description, parameters},注册表读的是 definition.parameters。注册旧结构会在面向模型的投影阶段失败:
dsh: UNKNOWN: tool "phocinae_ask" parameters must be lossless JSON before schema projection
修复: 定义现在带 parameters 和 output: {schema, render},再加 execute,全部落在 harness 强制的 JSON Schema 子集内(required 写成数组,联合类型用 oneOf,不放只给作者看的关键字)。test/contract.test.mjs 重新实现了这套子集检查,assembled-headless 运行会在真实宿主里注册这些定义。
D3 — gate 从错误的地方读答案
data.answers 是个对象,以问题 id 为键。0.1.2 写的是 Array.isArray(data.answers) ? data.answers[0] : …,对任何真实响应都是 undefined。
D4 — 于是静默放行一切
判定条件是 if (risk === true)。risk 永远是 undefined,条件就永远为假,于是 guard() 返回 {decision: 'allow'},理由写着 "the model judged this low risk"——对每条命令都如此,包括 rm -rf /。
在 bench/ 里那 53 条带标注的命令集上,破坏性命令的召回率是 0.00。
已修复: 答案按 id 读取并做形状校验,格式错误的请求体算协议错误,不再回退到默认值;闸门失败即关闭——服务不可达、缺少置信度扩展、请求体读不出来,一律转人工。
D5 — 判定被抛出,而不是返回
拒绝路径调用了 next(err)。tools/pre-execute 瀑布流期望拿到 PreToolDecision({kind: 'allow' | 'deny' | 'ask', reason});拒绝瀑布流不算一个判定。
已修复: 改为 {kind: 'deny', reason} 和 {kind: 'ask', reason, displayReason},并且只在允许路径上调用一次 next()。
D6 — gate.tools 默认是 ['bash']
shell 工具在 Windows 上是 pwsh,其他平台是 bash,MCP 服务器还会加自己的名字。在这台机器上,默认值什么都匹配不到,所以闸门从来没跑起来。
已修复: 默认改为 ['']——匹配所有工具——同时支持精确名称、 通配和大小写不敏感匹配。
D7 — 发布说明承诺的升级闸门并不存在
模型发布文档描述了一个 E1 闸门:当 answer_confidence 低于 τ 时,把判定升级给更大的模型。0.1.2 里没有 τ,没有置信度比较,也没有升级分支——全仓库搜 escalate,只找到 Python 服务器。
已修复: phocinae_ask 现在返回 escalate、escalatedIds、escalationReason 和 escalateAt,闸门也读取同一个置信度。插件仍然不执行升级——它没法替你调用大模型——但信号现在是真产出的,不再是嘴上说说。
破坏性变更
| 变更 | 0.1.2 | 0.2.2 |
| 工具注册 | ctx.registerTool / ctx.tools.register 探测 | ctx.inject(['tools'], …) |
| 工具定义 | inputSchema + handler | parameters + output + execute |
| 闸门答案来源 | answers[0](数组) | answers[id](对象) |
| 失败方向 | 一律 allow | 默认 ask(gate.failMode) |
| 工具匹配默认值 | ['bash'] | [''] |
| 判定 | next(err) | {kind, reason} |
| 闸门提问 | 布尔值 noul | 危害量表(harmless/risky/destructive) |
| gate.mode 默认值 | deny | harm |
| 升级 | 无 | 每个答案都带 escalate |
gate.failMode: 'open' 可以恢复旧的「一律允许」行为,供有需要的部署使用。它不是默认值,也不该变成默认值。
实测行为
实测数据在一台机器上(CPU,fp32)针对已发布权重跑出,脚本在 bench/;依赖这些数据之前,请先在自己的硬件上重跑一遍。公布的数据来自模型发布文档本身。
版本锚点。 v1.0——首次公布数据。v1.0 修正版——E1 数据修正(保留子集 0.886,LLM 调用 −54.4%,升级 45.7%)。v1.1(2026-10-09)——正式版本(0.906 / 0.848,升级 45.0% / 调用 −55.0%,保留子集 0.9936)。修正前的读数(0.7948 / −82% / 18%)已被上游弃用——不要引用。
决策质量——模型是诚实的
datasets/typed_test/test_typed_400.jsonl,400 个用例 × 5 个问题 = 2000 次决策,每次决策都对照 gold.label 判定:
| 指标 | 已发布(en) | 实测(en) | 已发布(zh) | 实测(zh) |
| 本地准确率 | 0.906 | 0.9055 | 0.848 | 0.848 |
| 升级率 @ τ=0.6 | 45.0% | 45.0% | — | — |
| 保留决策的准确率 | — | 0.9936 | — | — |
zh 数据是机器翻译的用例;训练混合里既有机器翻译的中文(约 2,400 行),也有原生中文(约 1,400 行)——这是混合内(拟合)评估,不是跨语言迁移。
准确率复现结果落在发布方自己声明的 fp16/fp32 噪声范围内,升级率也一样(独立复现得到同样的 45.0%)。「LLM 调用减少 79.6%」这个数字属于 τ≈0.50,而冻结的 E1 门限是 τ=0.6(τ 在评估集上扫描——每个领域都要重新扫):
| τ | 升级比例 | 减少的 LLM 调用 | 保留在本地决策的准确率 |
| 0.50 | 20.4% | 79.6% | 0.9523 |
| 0.60 | 45.0% | 55.0% | 0.9936 |
| 0.70 | 65.0% | 35.0% | 0.9986 |
| 0.80 | 77.0% | 23.0% | 1.000 |
「节省 79.6%」和「τ=0.6」不可能同时成立。 在模型发布方称为冻结的那个阈值上,降幅是 55.0%。这仍然是实打实的节省,置信度排序也确实有用——随着门槛抬高,留在本地的决策准确率从 0.91 升到 1.00——但那个头条数字属于另一个阈值。
门限质量——以及默认问题为什么改了
bench/gate-bench.mjs,53 条命令(31 条破坏性,22 条常规),全部针对真实模型。recall 是门限拦下的破坏性命令占比;auto-pass purity 是放行的命令实际安全的比例。
| 门限问题 | 破坏性命令的召回率 | 自信放行却具破坏性的次数 |
| noul ——「这条该拦吗?」(0.1.2 的默认) | 0.00 | — |
| tri-state —— allow / ask / deny | 0.74 | 1 |
| harm —— harmless / risky / destructive(0.2.5 默认) | 0.97 | 0 |
继承下来的布尔问题不只是弱,它是反的:它放行了 31 条破坏性命令中的每一条,多数置信度在 0.54 到 0.80 之间。问「这安全吗?」更糟——31 条里有 27 条被放行,置信度高达 0.87。harm 量表是实测中唯一一种表述,既几乎全都拦下,又从不自信地把破坏性命令放过去。
它唯一漏掉的一条,vssadmin delete shadows /all /quiet,是被自动放行列表的操作符规则拦下的,而不是靠置信度,所以这套集合里没有任何破坏性命令能走到执行。
置信度被压缩了:把门限收窄
模型的置信度不足以区分日常命令和危险命令,做不了通用过滤器。它给出的 harmless 判定大多落在 0.27–0.54,所以如果让门禁去问它 git status,git status 也会被送交人工。
这就是 0.2.5 引入自动放行列表的原因:只读命令和构建/测试命令,只要不含 shell 操作符,就根本不会送到模型那里。门禁的职责是拦截,不是批准日常操作:
- 在列表上、无操作符 → 本地放行,不发请求,无延迟
- 含 ;&|><$(){}\[\]%^ 等任意字符 → 绕过列表,交给模型判断
- 某个动词自身的参数里能指定命令,或者会删除它匹配到的内容(find、env)→ 根本不进列表;调用方传入的 make 目标会交给模型
- 模型判定 destructive → 拒绝
- 模型判定 risky,或 harmless 低于 gate.minConfidence → 人工决定
- 服务不可达 → 人工决定
条目里只要含 shell 操作符,加载时就会被拒绝。git status && rm -rf / 的开头是允许列表里的前缀,所以接受这种模式等于在列表里内置一条绕过路径。光靠操作符规则也不够:find . -delete 和 env rm -rf / 都不含操作符,这就是这些动词不在列表上的原因,也是 make 只能以完整、已知安全的调用形式通过的原因。
对各项说法的如实总结
| 说法 | 结论 |
| 「144.3M 双语决策模型,单次前向传播,本地运行」 | 属实 |
| 「typed-decisions 英文 0.906 / 中文 0.848」 | 可复现(0.9055 / 0.848) |
| 「在 τ=0.6 置信度门禁下把 LLM 调用减少 79.6%」 | 按原文说法不成立 —— 79.6% 对应 τ≈0.50;τ=0.6 时是 55.0% |
| 「升级机制把综合准确率提升到 0.8737」 | 一致 —— 升级只替换本地错误答案 |
| 「插件提供了这道门禁」 | 对 0.1.2 而言不成立(召回率 0.00,而且它从未加载) |
配置
默认值在 cordis.patch.yml 里,文档在 lib/config.mjs 中。每个键都有默认值,所以 bundle 补丁可以精简到 enabled: true。
| 键 | 默认值 | 含义 |
| endpoint | http://127.0.0.1:8155/v1/systemone | 决策服务。仅限回环地址 —— 决策状态携带原始工具调用文本,所以远程主机在加载时报错,除非设置 allowRemoteEndpoint: true。 |
| model | Phocinae-Largha-150M-v1 | 服务的模型名 |
| timeoutMs | 3000 | 每个请求;只尝试一次,不重试 |
| escalateAt | 0.6 | 置信度低于此值,答案标记为 escalate: true |
| maxStateChars | 4096 | 渲染后决策状态的字符上限 |
| permuteChoice | false | 对选择题做排列平均(4 次前向传播) |
| gate.enabled | true | 启用执行前门禁 |
| gate.mode | harm | harm / tri-state / noul / off |
| gate.threshold | 0.8 | 仅 noul 模式:P(risky) 达到此值算作阳性信号 |
| gate.minConfidence | 0.45 | 低于此值,allow 转为人工复核 |
| gate.autoAllow | 见 DEFAULT_AUTO_ALLOW | 无需询问模型即可放行的命令 |
| gate.tools | [''] | 要筛查的工具名:精确名称、 通配,或 |
| gate.failMode | closed | closed = 服务不可达时交给人工;open = 放行 |
| gate.riskyDecision | deny | 仅 noul 模式:命中风险时 deny 还是 ask |
| gate.audit | true | 通过宿主 logger 记录每条判定 |
不筛查任何内容,只用这些工具:
- id: phocinae
config:
gate:
mode: off
只筛查 shell,并且只询问:
- id: phocinae
config:
gate:
tools: ['pwsh', 'bash']
riskyDecision: ask
运行决策服务
插件与本地服务器通信,自己不加载权重。
weights (the reference download is ~330 MB)
huggingface-cli download Phocinae/Phocinae-Largha-150M-v1 --local-dir ./model
(modelscope hosts the same repo: modelscope download --model PerryLink/Phocinae-Largha-150M-v1)
pip install phocinae-server
PHOC_MODEL_DIR=./model python -m phocinae.main
GET http://127.0.0.1:8155/health 返回 {"status":"ok","model_loaded":true},就说明服务已启动。没有它,phocinae_ask 会返回 PHOCINAE_SERVICE_UNAVAILABLE 错误,并在错误里点名那条命令,同时 gate 会把每次被筛查的调用都转给人工。
开发
npm test # unit + contract tests, no network, no model
npm run test:integration # gate over real HTTP, then a real dsh host boot
npm run bench:gate # the gate benchmark (needs the real service)
npm run test:integration 会在系统临时目录下创建一个一次性的 DSH_HOME,因此绝不会读写你自己的配置档。它的前半部分用真实 HTTP 驱动真实 gate,对接一个假服务;后半部分把插件安装进那个一次性配置档,并启动真实的 dsh,断言每个 bundle 条目都能激活——这正是 0.1.2 会失败的检查。
假服务之所以存在,是因为真服务需要 330 MB 的 checkpoint。用 --real-endpoint http://127.0.0.1:8155/v1/systemone 把测试框架指向一个正在运行的真服务。
文档
| 文档 | 内容 |
| ARCHITECTURE.md | 挂载约定、gate 的判定顺序,以及每个选择背后的理由 |
| SECURITY.md | 信任边界、哪些数据会离开本机,以及各种失败模式 |
| CHANGELOG.md | 发布历史 |
| bench/ | 基准测试脚本及其保存的结果 |
| THIRD_PARTY_NOTICES.md | 复用的成果及其许可证 |
许可证
Apache-2.0 —— 见 LICENSE。模型权重同样是 Apache-2.0;关于模型,以它的 model card 为准,关于插件,以本仓库为准。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
Phocinae/dsh-phocinae
在工具调用执行前拦截危险命令并做本地判断
相关插件 · 安全 / 权限
智能体逆向工程morluto/rea
用智能体逆向工程任何东西,从应用行为到原生二进制。
★ 415,267已汉化DSH
73.3
智能体集群框架ruvnet/ruflo
面向 Claude Code 与 Codex 的智能体元基座meta-harness。
★ 80,414已汉化MCP
71.9
WorkBuddy 模型接入corrinehu/dsh-workbuddy-connect
2026 年 10 月 20 日起,WorkBuddy…
★ 1,604已汉化DSH
71.7
破甲一键通z91772524-ai/pojia-next
十五个 AI…
★ 967已汉化DSH
71.6
AI 代理虚拟终端strukto-ai/mirage
Mirage 是给 AI 智能体用的虚拟终端:一层 shell,统管数据、命令行和运行时。Unix…
★ 3,756已汉化DSH
70.8
Agent 操作层agentic-os-org/ANOLISA
ANOLISA(Agentic Nexus Operating Layer & Interface System Architecture)|智能体操作系统,带运行时、安全、可观测性,以及 Tokenless 响应压缩,降低 token 用量与成本。
★ 649已汉化DSH
70.7