隔离云端规划与本地实现,写入前拦截越权操作
30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/10 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add jjmlovesgit/dsh-plugin-coding-delegate该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:待复验
是什么
dsh 原生插件 · ads
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 24 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 72 处,展示前 72 条)
- 读取本地文件experiments/delegation-ab/measure-session.cjs:57
entries = fs.readdirSync(dir, { withFileTypes: true }) - 读取本地文件experiments/delegation-ab/measure-session.cjs:65
const stat = fs.statSync(full) - 读取本地文件experiments/delegation-ab/measure-session.cjs:104
const buffer = fs.readFileSync(file) - 读取本地文件experiments/egress-benchmark/run-3-session-gate.cjs:133
for (const project of fs.readdirSync(root, { withFileTypes: true })) { - 读取本地文件experiments/egress-benchmark/run-3-session-gate.cjs:136
if (fs.existsSync(store)) hits.push(store); - 读取本地文件experiments/egress-benchmark/run-3-session-gate.cjs:200
const { text, frames } = decodeAllFrames(fs.readFileSync(file)); - 发起网络请求plugin/dist/delegation.js:588
const response = await fetch(url, init); - 读取密钥环境变量plugin/dist/index.js:225
parseInt(process.env.CONTEXT_TOKEN_THRESHOLD || '30000', 10); - 发起网络请求plugin/dist/index.js:561
const outcome = await service.request({ - 发起网络请求plugin/dist/index.js:607
const outcome = await service.request({ - 发起网络请求plugin/dist/index.js:635
const outcome = await service.request({ - 发起网络请求plugin/dist/index.js:1318
const outcome = await approvalService.request({ - 读取本地文件plugin/src/context.ts:80
const fileSize = fs.statSync(resolvedPath).size - 读取本地文件plugin/src/context.ts:94
raw = fs.readFileSync(resolvedPath, 'utf8') - 读取本地文件plugin/src/contracts.ts:13
return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex') - 读取本地文件plugin/src/contracts.ts:348
exists: (p: string) => boolean = (p) => fs.existsSync(p) - 写入本地文件plugin/src/contracts.ts:365
fs.mkdirSync(path.dirname(target), { recursive: true }) - 写入本地文件plugin/src/contracts.ts:368
fs.writeFileSync(temp, JSON.stringify({ version: 1, records }, null, 2), 'utf8') - 写入本地文件plugin/src/contracts.ts:393
fs.renameSync(from, to) - 写入本地文件plugin/src/contracts.ts:426
fs.mkdirSync(path.dirname(lockPath), { recursive: true }) - 读取本地文件plugin/src/contracts.ts:437
if (Date.now() - fs.statSync(lockPath).mtimeMs > REGISTRY_LOCK_STALE_MS) { - 删除本地文件plugin/src/contracts.ts:438
fs.rmSync(lockPath, { force: true }) - 删除本地文件plugin/src/contracts.ts:454
fs.rmSync(lockPath, { force: true }) - 读取本地文件plugin/src/contracts.ts:492
text = fs.readFileSync(resolveDelegatedRegistryPath(), 'utf8') - 读取本地文件plugin/src/declaration-egress-hook.ts:129
return fs.statSync(resolveScope(scope)).isDirectory() - 读取本地文件plugin/src/declaration-egress-hook.ts:202
if (fs.statSync(resolveScope(token)).isDirectory()) return true - 读取本地文件plugin/src/declaration-egress-hook.ts:215
return fs.existsSync(resolved) - 读取本地文件plugin/src/declaration-egress-hook.ts:232
const stat = fs.statSync(p) - 读取本地文件plugin/src/declaration-egress-hook.ts:239
entries = fs.readdirSync(p, { withFileTypes: true }) - 读取本地文件plugin/src/declaration-egress-hook.ts:261
text = fs.readFileSync(skeleton, 'utf8') - 执行外部命令固定命令plugin/src/delegation.ts:64
const out = child_process.execFileSync('git', ['-C', workspaceDir, 'rev-parse', 'HEAD'], { - 执行外部命令固定命令plugin/src/delegation.ts:97
child_process.execFileSync('git', ['-C', workspaceDir, ...command.args], { - 发起网络请求plugin/src/delegation.ts:765
const response = await fetch(url, init) - 读取本地文件plugin/src/delegation.ts:1102
if (!fs.existsSync(workspaceBase)) { - 写入本地文件plugin/src/delegation.ts:1104
fs.mkdirSync(workspaceBase, { recursive: true }) - 读取本地文件plugin/src/delegation.ts:1475
privateKeyPem = fs.readFileSync(privateKeyPath, 'utf8') - 读取本地文件plugin/src/emission.ts:262
if (!patchBlocks && fs.existsSync(resolvedPath)) { - 读取本地文件plugin/src/emission.ts:281
if (!fs.existsSync(resolvedPath)) { - 读取本地文件plugin/src/emission.ts:291
original = fs.readFileSync(resolvedPath, 'utf8') - 写入本地文件plugin/src/emission.ts:310
fs.mkdirSync(parentDir, { recursive: true }) - 写入本地文件plugin/src/emission.ts:311
fs.writeFileSync(resolvedPath, fileCode, 'utf8') - 读取本地文件plugin/src/guard.ts:396
const stat = fs.statSync(resolved) - 读取本地文件plugin/src/guard.ts:398
return fs.readFileSync(resolved, 'utf8') - 读取本地文件plugin/src/guard.ts:612
const sourceStat = fs.statSync(sourcePath) - 读取本地文件plugin/src/guard.ts:613
const declarationStat = fs.statSync(declarationPath) - 读取本地文件plugin/src/index.ts:488
if (typeof candidate === 'string' && candidate && fs.existsSync(candidate)) { - 读取本地文件plugin/src/index.ts:499
if (typeof value === 'string' && value && fs.existsSync(value)) { - 读取密钥环境变量plugin/src/index.ts:598
parseInt(process.env.CONTEXT_TOKEN_THRESHOLD || '30000', 10) - 发起网络请求plugin/src/index.ts:1005
const outcome = await service.request({ - 发起网络请求plugin/src/index.ts:1057
const outcome = await service.request({ - 发起网络请求plugin/src/index.ts:1094
const outcome = await service.request({ - 读取本地文件plugin/src/index.ts:1516
content = fs.readFileSync(skeleton, 'utf8') - 发起网络请求plugin/src/index.ts:1846
const outcome = await approvalService.request({ - 写入本地文件plugin/src/logging.ts:51
fs.mkdirSync(path.dirname(LOG_FILE), { recursive: true }) - 写入本地文件plugin/src/logging.ts:52
fs.appendFileSync(LOG_FILE, entry, 'utf8') - 读取本地文件plugin/src/paths.ts:57
const stat = fs.lstatSync(ancestor, { throwIfNoEntry: false }) - 读取本地文件plugin/src/paths.ts:94
if (fs.lstatSync(current, { throwIfNoEntry: false })) return { ancestor: current, tail } - 读取本地文件plugin/src/paths.ts:137
const stat = fs.lstatSync(current, { throwIfNoEntry: false }) - 写入本地文件plugin/src/sandbox.ts:303
fs.writeFileSync(shim, SANDBOX_PREFLIGHT_SOURCE, 'utf8') - 删除本地文件plugin/src/sandbox.ts:329
fs.rmSync(dir, { recursive: true, force: true }) - 读取本地文件plugin/src/savings-tracker.ts:351
if (!fs.existsSync(this.ledgerPath)) return null - 读取本地文件plugin/src/savings-tracker.ts:352
return JSON.parse(fs.readFileSync(this.ledgerPath, 'utf8')) - 读取本地文件plugin/src/savings-tracker.ts:381
if (fs.existsSync(this.ledgerPath)) { - 读取本地文件plugin/src/savings-tracker.ts:382
const raw = fs.readFileSync(this.ledgerPath, 'utf8') - 写入本地文件plugin/src/savings-tracker.ts:450
fs.mkdirSync(path.dirname(this.ledgerPath), { recursive: true }) - 写入本地文件plugin/src/savings-tracker.ts:451
fs.writeFileSync(this.ledgerPath, JSON.stringify(ledger, null, 2), 'utf8') - 写入本地文件plugin/src/verification.ts:495
fs.mkdirSync(dir, { recursive: true }) - 执行外部命令来自配置/变量plugin/src/verification.ts:498
result = child_process.spawnSync(binary, args, { - 读取本地文件plugin/src/verification.ts:525
return fs.readFileSync(file, 'utf8') - 删除本地文件plugin/src/verification.ts:533
fs.rmSync(file, { force: true }) - 写入本地文件plugin/src/verification.ts:663
fs.mkdirSync(path.dirname(rawLogPath), { recursive: true }) - 写入本地文件plugin/src/verification.ts:664
fs.writeFileSync(rawLogPath, output, 'utf8')
扫描于 2026/11/3(0 天前) · 范围 全包扫描 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 隔离云端规划与本地实现
- 写入前拦截越权操作
- 记录哈希绑定晋级校验
DeepSeek Harness 的预防式治理插件:云端规划器禁用写入与 shell,本地执行者在受限工作区内实现,晋级与机器校验或操作员背书的记录哈希绑定。五道边界,每道都失败即关闭。
README
由 DeepSeek 最新模型翻译生成DSH 编码委派技术控制:职责分离
一个 DeepSeek Harness(DSH)插件,把谁规定工作与谁实现工作分开,并且不允许前者写后者的代码。这是技术控制,不是策略:拦截发生在写入落地之前的工具调用钩子里,所以不依赖模型自觉遵守。
计量模型没有写入工具,也没有 shell。一个被完全提示词注入的架构师可以随便规划,却依然改不了你主机上的一个字节,因为权限从未授予。
工作原理:交出契约,拿回裁决
编码需要完成两种不同的工作,而它们想要两种不同的上下文。插件给各自一个窗口,只让两样东西在两者之间穿过。
架构师是计量思考模型。它主持设计对话,决定工作如何拆分,并为每个单元写一份契约:要构建什么、worker 可以写哪里、以及判定该单元是否通过的命令。它不写源码,也不回读源码。
worker 是你 GPU 上已经在跑的东西。它接收一个单元和该单元所需的一切——仅此而已——写代码,然后被丢弃。每个单元一个全新上下文,不花什么成本。
| 方向 | 穿过的东西 | 永不穿过的东西 |
| 架构师 → worker | 契约:指令、允许的路径、验证命令 | 设计对话、其他单元 |
| worker → 架构师 | 裁决:通过/失败计数、脱敏的失败结构、写入文件的文件名 | 代码 |
验证命令作为普通子进程运行,需操作员批准。
展开完整文档(README 剩余 1 节)
DSH 编码委派技术控制:职责分离
一个 DeepSeek Harness(DSH)插件,把谁规定工作与谁实现工作分开,并且不允许前者写后者的代码。这是技术控制,不是策略:拦截发生在写入落地之前的工具调用钩子里,所以不依赖模型自觉遵守。
计量模型没有写入工具,也没有 shell。一个被完全提示词注入的架构师可以随便规划,却依然改不了你主机上的一个字节,因为权限从未授予。
工作原理:交出契约,拿回裁决
编码需要完成两种不同的工作,而它们想要两种不同的上下文。插件给各自一个窗口,只让两样东西在两者之间穿过。
架构师是计量思考模型。它主持设计对话,决定工作如何拆分,并为每个单元写一份契约:要构建什么、worker 可以写哪里、以及判定该单元是否通过的命令。它不写源码,也不回读源码。
worker 是你 GPU 上已经在跑的东西。它接收一个单元和该单元所需的一切——仅此而已——写代码,然后被丢弃。每个单元一个全新上下文,不花什么成本。
| 方向 | 穿过的东西 | 永不穿过的东西 |
| 架构师 → worker | 契约:指令、允许的路径、验证命令 | 设计对话、其他单元 |
| worker → 架构师 | 裁决:通过/失败计数、脱敏的失败结构、写入文件的文件名 | 代码 |
验证命令作为普通子进程运行,需操作员批准。它的原始输出留在本地机器;回传的是失败结构,源码已被剥离。
状态:0.2.0, 机制可用且经过测试。它的局限有文档记录,而非粉饰,包括两个无法从进程内部关闭的——见本控制未覆盖的内容。决定它是否适合你的情况之前,先读那一节。
它做什么
五件事,按作用顺序排列:
1. 进程内,无 sidecar。 没有守护进程,没有额外端口,没有要监管的东西。闸门和作者身份守卫都在插件内运行。
2. 每个出站请求上的 DLP 闸门。 提示词在云端看到之前会被扫描凭据和高熵 token。命中就拒绝或改道到本地 worker,绝不传输。闸门会累积它见过的用户消息,所以早前轮次里的凭据会让它保持关闭,而不是滚出视野。
3. 双向的代码守卫。 云端编写的源码写入和删除会被拒绝或送审——回读 worker 写过的文件也一样,因为那会把委派的代码进入上下文委派的存在,正是为了把它挡在外面。Shell 形式也覆盖了,包括内联程序文本(python -c、node -e)。
4. delegate_worker。 架构师调用这个工具,把一单元实现交给本地 OpenAI 兼容服务器(LM Studio、Ollama、vLLM、llama.cpp、远程网关)。文件写入被限制在工作区内,验证输出在传输前会脱敏为结构,验证命令本身需要批准。
5. 一单元是被判定的,不是被报告的。 契约用 contractFiles 钉住——在 worker 运行前哈希,拒绝作为写入目标,运行后重新哈希,所以任何改动都会让判定作废——一单元如果写了文件却没验证,就报告 UNVERIFIED。整文件输出可以创建文件,不可以修改文件。然后 coherenceVerification 运行项目自己的命令,有权作废一单元——即使它自己的测试通过了,但整棵树没有。
运作方式
架构师可以推理,但不能撰写。worker 可以撰写,但不能越界。两者未经同意都不得执行。源代码不离开本机。
这里的每条规则都是这句话的一个实例——除了规则 8,它讲的是源代码可以去哪里——而且每条都失败关闭:如果一条规则无法评估,或者批准服务联系不上,答案就是否。
| # | 规则 | 由谁执行 |
| 1 | 凭据不得到达云端 | DLP 门:拒绝,或钉在本地 |
| 2 | 架构师不得撰写或删除源代码 | 代码守卫:拒绝,或走批准门 |
| 3 | 架构师不得读回它委派出去的东西 | settled 规则:一单元通过后未改动的文件静默读取,失败、未验证以及此后被编辑过的文件会询问;delegateReadPolicy: 'allow' 放宽这一切,对所有智能体生效,并且会说明 |
| 4 | worker 不得写到工作区之外 | 对每条输出路径做限制 |
| 5 | worker 的代码不得在服务器内运行 | 只用子进程;进程内回退关闭 |
| 6 | 架构师提出的命令不得未经检查就运行 | 批准接缝 |
| 7 | 委派的结果是判定,不是声明 | 写了文件却没验证的,报告 UNVERIFIED |
| 8 | 源代码不得到达云端 | 携带围栏源代码的云端请求默认拒绝;由 sourceEgress 决定 |
规则 8 是启发式的:它寻找带源代码语言标签、至少三行的围栏代码块,所以没打标签粘贴的源代码、用散文描述的源代码,或者拆成短块的源代码,都不会被检测到。它也是唯一一条能拒绝你自己输入的请求的规则。
强制的不变量
五条边界,每条都说明它不覆盖什么,因为边界计数只有在每一项都是它所声称的那种边界时才有意义。
| # | 边界 | 由谁执行 | 如何失败关闭 |
| 1 | 变更完整性 | tools/pre-execute 拦截云端写入与 shell 执行 | 规划层在物理上无法在宿主机上写入或运行 |
| 2 | 出口隐私 | tools/post-execute 通过 sourceReadEgress: 'declarations' 提供 .d.ts 桩文件 | 若声明缺失或早于其源文件,读取会被拒绝 |
| 3a | 工作区根目录 | workspaceDir + emitAllowlist | 无条件生效 —— worker 无法逃出沙箱目录 |
| 3b | 任务单元范围 | targetFiles、unitScope: 'enforce' | 有条件生效 —— 未声明的路径会被拒绝,但什么都不声明的单元在整个工作区内不受限制 |
| 4 | 不稳定因素隔离 | runVerification 默认跑 3 轮 | 非确定性漂移会记录 UNIT_FLAKY 并中止晋级 |
| 5 | 晋级套接字 | scripts/check-promotion.cjs | 裁决之后的任何编辑都会把退出码翻成 1 |
其中三项带限定条件,而这些限定条件比表格本身更重要:
- 第 3b 行是可选启用的。 工作区根目录是硬边界;单元边界只在架构师声明了它时才存在,而且没有任何机制强制声明。把两者混为一谈,就会让一张表读起来像五条无条件边界,而其中一条其实是契约属性。
- 第 2 行是来源追溯,不是验证。 读取被重定向到构建产出的声明文件,过期与否靠 mtime 判断 —— 这只证明发生过重新构建,一次 touch、一次 checkout 或时钟偏移就能骗过它。tsc 是被信任的,不是被证明的。
- 第 5 行约束的是内容,不是对某个人的身份认证。 一条 OPERATOR_ATTESTED 记录保存一个 SHA-256 和两个自报字符串。编辑文件会把套接字翻成 1,但没有任何机制验证署名操作者确实做了评审。这是防篡改证据,不是密码学意义上的不可否认性 —— 没有密钥,没有签名,也没有第三方。
面向企业评审
企业评估混合框架时,卡住的地方通常是结构,而不是硬件效率或 token 成本。反复出现的反对意见是:一个能访问宿主机工具的云端智能体,靠的是提示词指令(「不要修改范围外的文件」)或被动弹出的审批框 —— 这种许可是暂停,不是边界;而且作者自己给自己的作品打分并提交。
这里有三点直接回应了这个问题,每一点旁边都写明了它的局限,因为一个隐藏了局限的控制项,正是企业评审存在的意义所在。
1. 职责分离是机制性的,不是靠指令。 云端模型是架构预言机。它在 tools/pre-execute 处被拦下,无法改动文件、无法执行 shell —— 在 control-bypass.test.cjs 的十五条路径中,四条被直接拒绝,十一条被挂起等待审批,没有一条能在未获授权的情况下触达源码。变更权限归属于本地 worker,且被限制在受约束的工作区内。
限制: 工作区根目录是无条件边界,但按文件的 targetFiles 限制以声明为条件——不声明任何内容的单元在整个工作区不受限制。强制声明的策略是合理诉求,但尚未内置。
2. 晋升是防篡改的,而不是凭感觉检查。 Git 显示提交,但不显示字节由远程模型还是经认证的本地单元产出。晋升套接字把文件绑定到 SHA-256,该哈希在盒上共识期间注册(三次运行,非确定性漂移记录为 UNIT_FLAKY)或经人工审查(OPERATOR_ATTESTED)。裁决后的任何编辑都会使其失效,套接字以 1 退出。promotion-socket.oracle.mjs 覆盖全部十二种判定情形,并且在 CI 中运行——缺失的裁决分支正是它要捕获的 bug,因为落入通用拒绝与正确的严格性无法区分。
限制: 注册表是每机器本地的,不在仓库中,所以套接字是本地 pre-commit 门禁,而非 CI 门禁;并且 operator 是自报字符串,没有密钥也没有签名。那是防篡改,不是密码学不可否认性。
3. 出口受构建来源约束。 使用 sourceReadEgress: 'declarations' 时,对 .ts 文件的读取以编译器生成的 .d.ts 存根应答,因此架构师获得类型和签名,而不摄入实现体。在本仓库上实测:所服务的声明中 for/while、if/switch 和 fs. 调用为零,而源码中分别为 16、80 和 2。
限制: 构建是受信任的,而非经验证的——陈旧性通过 mtime 检查,touch、检出或时钟偏移都能击败它;并且该选项默认关闭,需要重启插件。
企业审查会发现缺失什么
在此陈述,而非在采购对话中被发现:
- 注册表是本地、未签名且每机器独立的。 审计记录不能只存在于笔记本电脑上;它们需要用真实身份签名,并发送到中央清单或证明存储。
- 证明不是持久的。 后续委派触及同一文件会取代该记录——语义正确,因为裁决描述的是内容,但这意味着人工证明必须重新建立,不是永久证书。
- 任务范围界定是选择加入的,如上所述:没有任何东西强制架构师声明 targetFiles。
- 0.2.0 的个人项目。 API 未冻结。诚实的限制被记录下来而非粉饰,这是评估应当权衡的加分特质,因为它太罕见了。
主张不是这能削减计量账单——实测,那大约值十五美分。而是本地硬件成为有记录支撑的边界,并且指定工作的模型可证明不是编写工作的那个模型。
这项控制不覆盖什么
有效性声明是控制的一部分,不是它的附录。以下是各项限制,也正是你该读这个文件、而不是读功能列表的原因。
- 它拦的是写入,不是意图。 守卫看到的是工具调用和 shell 文本。模型在对话中、在一次计算中、经由第二条工具路径、或经由本插件之外的任何插件或 MCP 服务器产出的源码,它都看不见。这是范围边界,不是缺陷;也就是说,这项控制的覆盖范围是本插件的写入路径,而不是「模型没有写代码」。
- 记录器与被记录者跑在同一个进程里。 守卫、worker 和账本都活在同一个 DSH 进程内,所以下面的记录是「发生了什么」的证据,不是「没有别的事情发生」的证明。哈希对 worker 约束得很好;这里没有任何东西约束 记录器。要堵上这一点,需要一个进程无法反向触及的外部接收端,那超出了本插件的范围。
- 它不会让编码更便宜、更私密或更长久。 本插件最初正是靠这三条主张立足。每一条都做了测试,每一条都不成立;测量结果放在 已撤回的主张 里,而不是删掉,因为一位作者公开了反证的控制,比一份干净的功能列表更有用。
- 守卫并不精确。 一条 shell 命令只要点到了某个目录,而该目录包含被委派的文件,就会被拒绝,哪怕它只是列个名字;删除动词只要和某个源文件名出现在同一行,就会被拒绝,哪怕目标毫不相干。两者都是失败即关闭——代价是,一旦有委派落地,日常的清理维护就跑不动了。已记录,尚未修复。
- 审计记录覆盖的是委派,不是全部。 账本里架构师侧的计数器(totalCloudTokens、architectTurns、totalSpendUSD)在结构上恒为零:本该喂给它们的主机事件不带用量。计量模型读过什么,记录在 SOURCE_READ 追踪里,那是日志文件,不是受追踪的记录。所以这条线索能回答「委派了什么、结果如何」,对其余部分保持沉默。
- 这里没有任何防篡改。 注册表和账本都是普通的本地 JSON。任何对数据目录有写权限的人都能改。
已撤回的主张,以及原因
每一行都是在本仓库自己的工作上测出来的。保留它们,是因为一项控制的公信力来自它披露了什么,也因为每一条都是给下一个做类似东西的人埋的坑。
| 主张 | 测量结果说明了什么 |
| 「你的计划负责思考,GPU 负责打字」 | 作为机制成立,作为节省几乎一文不值。329 次委派中,架构师计费了 666,756,560 个输入 token;本地 worker 的全部输出是 548,106 —— 占账单的 0.08%。委派无法有效削减一个它只贡献 0.08% 的总量。 |
| 「跑在你自己的机器上,所以是私密的」 | 被推翻。 架构师必须读源码才能针对它做工程,而它读过的所有内容都会在之后的每一轮重新发送 —— 实测每轮约 8.5 次读取事件。留在本地的是打字,不是对所打内容的了解。 |
| 「本地路由省钱」 | 被推翻,而且文件底部从未这样声称。 委派输出折合云端等价价值 $0.1047;硬件回本大约是这个工作负载的 19,000 倍。 |
| 「有容量,所以计划能撑住」 | 无证据支持。 会话长度由上下文窗口填满决定,而填满它的主要是架构师的读取,插件并不减少这部分。能证明它延长会话的实测证据:没有。 |
| 再加一层,在本地读仓库,把架构师挡在代码之外 | 建过,测过,已退役。 这一层的窗口放不下;某个按模型设置的推理参数无法覆盖;卸载打字换回的收益远少于它交还的读取量;没有任何由 lead 编写的契约被派发并验证过。src/index.ts 的拆分后来让这四条中的第一条退役,所以这个决定建立在其余三条上。见 docs/ROADMAP.md。 |
更普遍的教训,比这个插件活得更久:你无法通过改变谁来打字来削减会话的计费成本。你要削减的是规划模型必须持有的东西。 任何以「写作很贵」为前提的架构,都用错了成本模型。
证据链
一次委派之后,审查者能查到的东西,全部在本地:
| 记录 | 它回答什么 |
| contractFiles 的哈希,前后各一份 | 实现期间规格是否被改动?一旦改动,判定作废(CONTRACT_MODIFIED) |
| 委派注册表 | 某个单元写了哪些文件、它们的 sha256、是新建还是打补丁,以及判定结果和时间戳 |
| 节省账本 | 每一次委派调用:token、耗时、结果 |
| SOURCE_READ 追踪 | 架构师读了哪些源码,含路径、范围和哈希 |
| CONTEXT_QUALITY 追踪 | 每次调用的提示词大小、峰值、压缩次数、回收的 token,以及这些数字属于哪条路由 |
判定有四种,不是两种。 UNIT_PASSED、UNIT_FAILED、UNIT_UNVERIFIED、UNIT_FLAKY —— 委派一次编辑却没给验证命令,这是最常见的情况,它既不算通过也不算失败,把它算作失败会污染它本就是为了产出的那个数字。通过的单元没改动的文件会静默读回;失败、未验证、不稳定以及此后被改过的文件则会追问。
UNIT_FLAKY 之所以存在,是因为非确定性判定器——属性测试、并发测试框架——可能在缺陷没触发的那一轮,让一个坏实现通过;而「通过」这个结论,正是敲定一个文件的东西。runVerification 默认跑三次:三次结论一致,说明契约稳定;只要出现分歧,就记为 flaky——不算通过,不算敲定,并且点名判定器而不是代码。这是这条控制项以前唯一可能发出的假绿灯,而且它是静默的。
三倍的开销是刻意的,在你动手测量任何东西之前值得先知道。你已经测出是确定性的测试套件,可以传 verificationRepeats: 1 把时间省回来;快路径才是需要你做决定的那条。循环只在出现分歧时短路,因为那是唯一一个再跑也改变不了的结果——所以一个 flaky 单元花两次运行,干净通过或干净失败都要花满三次。
环境要求
- Node.js 22+
- 本地 OpenAI 兼容服务器,如果你想用本地路由或 delegate_worker (默认是 127.0.0.1:1234 上的 LM Studio;其他服务用 localEndpoint 指定)
- 云服务商,供架构师角色使用(默认 DeepSeek)
不需要容器运行时。 验证以普通子进程运行。容器是可选的加固手段——见 Verification。
安装
插件位于 plugin/。DSH 加载的是配置档 bundle,所以把它注册进你在用的配置档:
The CLI profile is web; the desktop app boots tauri.
.\scripts\register-plugin.ps1 -Profile web
或者手动来,脚本做的也就是这些:
dsh plugin --profile <name> add ./plugin
then add "dsh-plugin-coding-delegate" to dsh.profile.bundles in
~/.dsh/profiles/<name>/package.json
配置
插件自带的默认值指向 127.0.0.1:1234 上的 LM Studio,模型是 qwen/qwen3.8-27b。在你的配置档 cordis.patch.yml 里覆盖它们;plugin/cordis.patch.example.yml 是一份完整可用的示例,涵盖插件设置、一个 LM Studio 服务商和一个 DeepSeek 服务商。
大家最常需要改的两项设置:
- id: local-router
config:
localModel: 'Qwen/Qwen3-Coder-30B-A3B-Instruct'
localEndpoint: 'http://192.168.1.50:8000/v1' # vLLM, Ollama, llama.cpp, a gateway
有一项设置决定一个单元能否自我验证。 委托的验证命令会被拒绝,除非程序在允许列表里,或者已授予批准;这属于运维配置,不是工具参数——刻意如此,这样模型就没法把它关掉:
verificationAllowlist: ['node'] # a delegated node … command runs unattended
它匹配的是程序,不是参数,所以把 node 加进允许列表,也就等于允许 node -e "<anything>"。优先用它,而不是 verificationApproval: 'allow',后者的范围严格更宽。
完整配置参考、服务商设置和所有安全密钥:plugin/README.md。
那才是真正的文档,本文件只是摘要。
第一个单元
委派在架构师这边只是一次工具调用。单元声明要构建什么、可以碰哪些文件、给 worker 看什么,以及用哪条命令判定它是否通过:
{
"taskName": "parse-iso-duration",
"instruction": "Implement parseIsoDuration(text) in src/duration.ts. Return milliseconds; throw a TypeError on malformed input.",
"targetFiles": ["src/duration.ts"],
"contextFiles": [{ "path": "src/duration.ts", "startLine": 1, "endLine": 40 }],
"contractFiles": ["tests/duration.test.ts"],
"runVerification": "npx vitest run tests/duration.test.ts"
}
代码永远不会回来。回来的是判定结果——以下只是示例,但每个字段名和措辞都是插件实际打印的内容:
Task 'parse-iso-duration' completed. Wrote 1 file(s):
- src/duration.ts (84 lines, 2410 bytes, patched in place with 1 hunk(s))
Workspace: C:\Projects\app (resolved via caller-supplied workspaceDir)
Verification Results: Passed 12, Failed 0.
Coherence check: Passed 305, Failed 0.
Contract: 1 declared file(s), unchanged.
把它读成四条断言,因为它们分别接受检查:
- Wrote 1 file(s) —— 写入落地了,在工作区之内,而且只落在单元声明的路径上。
- patched in place with 1 hunk(s) —— worker 编辑的是给它看的那个文件,而不是替换它。
这个动词就是边界:整文件写入可以创建文件,绝不能修改文件。
- Passed 12, Failed 0 —— 你的验证命令通过了。它没有任何部分靠信任采信;命令本身经过批准,而 Coherence check 是项目级闸门,即使单元自己的测试通过,它也能让单元作废。
- Contract: 1 declared file(s), unchanged —— 判定该单元的那份测试,与你钉住的那份逐字节一致。如果不是,判定就作废。
回执里缺失的东西才是发现。写了文件却没验证的单元报告 UNVERIFIED;测试通过但代码树不一致的报告 INCOHERENT。两者都不是 SUCCESS,也都不会按 SUCCESS 报告——委派结果是判定,不是声明。
操作要求:自验证委派需要顶层会话
委派单元无法从子代理内部验证自己,这是设计使然——不是要绕开的 bug。 DSH 把子智能体的 approvalPolicy 钉死为 'never',所以审批接缝不存在,验证命令会被确定性地拒绝。你会看到 VERIFICATION_NOT_APPROVED,原因是 "approval was not granted";如果你的提示词禁止架构师自己跑测试,单元就会一直迭代,永远不收敛。
两件事能让循环闭合:
1. 从顶层会话运行委派,而不是子代理或嵌套作用域。
2. 放行程序。 委托的验证命令会被拒绝,除非它的程序在允许列表里,或者已获得批准。这是运维侧的配置,不是工具参数——这是有意为之,模型无法把它关掉:
- id: local-router
config:
verificationAllowlist: ['node']
允许列表匹配的是程序,不是它的参数,所以放行 node 也等于放行 node -e "<anything>"。优先用它,而不是 verificationApproval: 'allow',后者范围严格更宽。
两者都没有时,架构师会退回到自己运行裁判——在一个任务上实测,这要花验证循环 179,840 token,加上周边 440,500 token,而循环留在契约内时只需一次委托调用。
检查委托的工作是否经过验证
仓库可以只凭裁决结果做门禁,插件完全不碰 git:
node scripts/check-promotion.cjs --changed # pre-commit: nothing changed since its verdict
node scripts/check-promotion.cjs --path src/x.ts # one file
退出码 0 表示已定案;1 表示未定案;2 表示问题无法回答——注册表读不出来就是拒绝,绝不算通过。它会重新计算每个文件的哈希,因为裁决描述的是内容:一个文件通过之后又被编辑,它就不是任何验证所针对的版本了。它覆盖委托的 worker 写过的文件;架构师写的或手写的文件没有记录,会报告为未知。
这里不声称能做到的事
- 补丁必须精确匹配,过期的补丁会失败。 worker 返回的要么是整份文件,要么是搜索/替换增量,逐字节对照它当时看到的内容匹配。没有模糊匹配,所以对不上就拒绝,不会近似处理。这里没有任何机制让 worker 发现代码;它只能编辑交给它的东西。
- 整文件输出是新建,不是修改。 写到已存在的路径会被拒绝,无论新内容有多大。整份文件仍然可以整体替换,办法是发一个补丁,其搜索文本就是该文件当前的全部内容——这需要架构师把内容注入进去,因为 worker 读不到它。
- 读保护分不清智能体谁是谁。 DSH 不向插件暴露智能体的血缘,所以保护对所有智能体一律失败关闭,而不是区分架构师和子代理。智能体是什么不再决定它能读什么——已定案的规则才决定。
- 委托单元无法在子代理内部自我验证。 DSH 把子智能体的 approvalPolicy 钉死为 'never',所以批准这一环不存在,验证会被确定性地拒绝。自我验证的委托需要顶层会话。这是实测结果,不是本插件能覆盖的。
- 它不会让本地模型变得和云端模型一样强。
- 服务商传输、工具 schema 强制、会话存储都在 DSH 里,不在这里。
仓库结构
| path | what it is |
| plugin/ | 插件本体、测试和完整文档 |
| experiments/delegation-ab/ | 产出已废弃结论的 A/B 协议,包括那次无法执行的运行以及原因 || experiments/contract-first/ | 一份无法满足的契约,留作证据,以及它修正后的兄弟版本 |
| docs/findings.md | 发现的缺陷,以及每个缺陷的代价 |
| docs/experiment.md | 测量数据,包括那些对插件不利的结果 |
| docs/control-effectiveness.md | 五点设计对照实际强制、实际测试、或仅停留在文档上的部分 |
| docs/verification-architecture.md | 为什么这个循环必须把验证放在本机,以及唯一一个这套推理不成立的层级 |
| docs/ROADMAP.md | 构建了什么、测量了什么、废弃了什么 |
| scripts/check-promotion.cjs | 晋升接口:根据委派的工作是否已验证,决定是否放行一次提交 |
| scripts/measure-reading-cost.cjs | 架构师实际读了什么,数据来自插件自己的审计轨迹 |
开发
cd plugin
npm run build # tsc
npm run test # vitest, unit
npm run test:oracles # node --test, contract oracles
npm run test:all
在仓库根目录:
node scripts/check-readme-consistency.cjs # the two policy tables must agree
node scripts/check-dist-in-sync.cjs # rebuilt dist must be staged
node scripts/check-contract.cjs --spec # specification-conformance suite
安全
插件经过外部审查,对这次审查的回应——包括发现的缺陷和未修复的缺陷及原因——都在 SECURITY-REVIEW.md 里。
许可证
MIT —— 见 LICENSE。包内自带一份副本,位于 plugin/LICENSE,因为发布出去的产物是独立的。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
jjmlovesgit/dsh-plugin-coding-delegate
隔离云端规划与本地实现,写入前拦截越权操作
相关插件 · 营销 / 广告
复古广告弹窗Nagi-ovo/dsh-ads
把 DSH 变成 2005 年门户网站|Parody ads, fake games, and popups for…
★ 679已汉化DSH
67.4
精酿竖版宣传片Finderchangchang/brewreel
精酿 BrewReel:让 DeepSeek 这类便宜模型也能做出好看的竖版宣传片。写一份产品简报,AI…
★ 166已汉化DSH
62.6
工作区分组树KannaKuron/dsh-better-workspace
给 DeepSeek Harness DSH 的侧边栏「工作区」列表装上一套两层文件夹系统——磁盘层按目录路径嵌套官方…
★ 31已汉化DSH
48.1
跨窗提醒Aisland-SJL/dsh-reminder
🔔 DeepSeek Harness 跨窗口完成与审批通知——弹窗提醒把你拉回 DSH,Codex/WorkBuddy 风格。
★ 18已汉化DSH
40.4
推理档位自动选择justhalfbit/dsh-plugin-jev-effort-selector
让 Jev System One 模型替你决定每条消息该用多深的推理。
★ 7已汉化DeepSeek 客户端
37.1
模型价格看板vitas/dsh-model-pricing
在选择模型之前比较 LLM API 价格——并在使用之后查看你实际支付了多少。 这是一个
★ 2已汉化DeepSeek 客户端
36.5
这是你的仓库?认领条目