🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

持久任务编排 dreamfarer-space/dsh-reins

DeepSeek Harness
dreamfarer-space31 天前有提交已提供中文文档扫描:无法判定

用一句话下达目标,自动跨会话推进直到自检通过

30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/4 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dreamfarer-space/dsh-reins
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:需留意
是什么
dsh 原生插件 · chat
装得上吗
实装验证未通过(dependency_conflict),装前请到仓库确认最近更新与 issue
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 31 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
⚠︎ 实装验证未通过(dependency_conflict · 2026/10/4) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
⚪ 扫描:无法判定尚无人工评估
展开查看扫描说明与证据 ↓

已尝试扫描但取不到可分析的源码:仓库或包不存在、已删除,或为非 JS/TS 实现、未随包发布源码。无法判定,不代表安全。

  • · 取源不可用(Error: github tarball HTTP 404 @ https:
  • · api.github.com
  • · repos
  • · dreamfarer-space
  • · dsh-reins
  • · tarball)
扫描于 2026/11/3(0 天前)

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/dsh@deepseek-ai/cordis@deepseek-ai/dsh-agent@deepseek-ai/dsh-session@deepseek-ai/dsh-goal@deepseek-ai/dsh-llm@deepseek-ai/dsh-tools@deepseek-ai/dsh-user-approval
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 一句话下达目标
  • 跨会话推进任务
  • 自检通过才停止

DeepSeek Harness 插件:用 /reins 说出你想要什么,它陪你一起规划,并持续跑后台任务,直到它自己执行的检查通过

README

由 DeepSeek 最新模型翻译生成
English · 简体中文 = 22.13"> Harness 负责执行,Reins 负责编排。 用一句话告诉 Reins 你要什么:它和你一起规划,跨 Task、会话、审批、CI、故障和重启持续推进,并且只有它亲自运行的检查真正通过才算完成。 安装 ·快速上手 ·命令 ·配置 ·架构 ·安全模型 ·测试 ·开发 为什么需要 Reins 一个 Agent 会话擅长“现在就把这件事做完”,但真实的工程目标往往要跨越很长时间:改代码、跑测试、等人审批推送、等 CI、修 CI 失败、等 Review……中间还可能遇到进程崩溃、机器重启。 如果让 Agent 循环一直空转等待,既浪费资源,又容易把“模型说完成了”误当成“真的完成了”。Reins 把这些事交给一个持久的编排层: - 用自然语言说目标,当前对话的模型和你一起规划,遇到真正需要你决定的地方会先问你。 - 等待是持久状态,不是空转的 Agent 循环;事件到达后用新的 Attempt 继续。 - 敏感操作无人审批即拒绝,批准后只授权那一次精确的调用。 - Task 完成必须通过验收门:Goal 完成、Attempt 成功都不算数,测试由 Reins 亲自运行,不采信 Agent 的说法。 - 所有状态都在 SQLite 里,崩溃重启后保守对账,不确定的状态永远不会被当成成功。
展开完整文档(README 剩余 1 节)
English · 简体中文

= 22.13">

Harness 负责执行,Reins 负责编排。
用一句话告诉 Reins 你要什么:它和你一起规划,跨 Task、会话、审批、CI、故障和重启持续推进,并且只有它亲自运行的检查真正通过才算完成。

安装 ·快速上手 ·命令 ·配置 ·架构 ·安全模型 ·测试 ·开发

为什么需要 Reins

一个 Agent 会话擅长“现在就把这件事做完”,但真实的工程目标往往要跨越很长时间:改代码、跑测试、等人审批推送、等 CI、修 CI 失败、等 Review……中间还可能遇到进程崩溃、机器重启。

如果让 Agent 循环一直空转等待,既浪费资源,又容易把“模型说完成了”误当成“真的完成了”。Reins 把这些事交给一个持久的编排层:

- 用自然语言说目标,当前对话的模型和你一起规划,遇到真正需要你决定的地方会先问你。
- 等待是持久状态,不是空转的 Agent 循环;事件到达后用新的 Attempt 继续。
- 敏感操作无人审批即拒绝,批准后只授权那一次精确的调用。
- Task 完成必须通过验收门:Goal 完成、Attempt 成功都不算数,测试由 Reins 亲自运行,不采信 Agent 的说法。
- 所有状态都在 SQLite 里,崩溃重启后保守对账,不确定的状态永远不会被当成成功。

工作方式

/reins 给笔记工具加个删除功能

1. 你输入这句话的那个对话里的模型先看项目。
2. 需求里有真正需要你决定的地方时,它会问你(DSH 的选项卡片);它自己能查清楚的事不会问。
3. 现有测试覆盖不到这次改动时,它会先写好检查,并确认这个检查现在会失败。
4. 它调用 reins_create_objective,提交 Task、执行顺序和每个 Task 的检查命令。Reins 按哈希钉住检查文件:做 Task 的 Agent 改不了自己的考卷。
5. Reins 自己的 Agent 在后台、在独立的会话里干活。需要审批时,/reins-todo 会列出来,/reins-approve <编号> 之后 Task 从停下的地方继续。
6. Reins 亲自运行检查,退出码为 0 是唯一的“完成”。

这套流程在 DSH Web 中用真实的 DeepSeek 模型验证过:要求“给笔记工具加个删除功能”,模型先问“按下标删除还是按内容删除”,写好 check-delete.mjs 并确认它会失败,然后创建 Objective;Reins 的 Agent 实现了按下标删除,Reins 运行被钉住的检查,退出码 0,验收通过。

核心特性

| | 能力 | 说明 |
| 💬 | 自然语言规划 | /reins <你想要什么>:当前对话的模型和你一起规划,有真正的分歧就问你,先写检查,再创建 Objective |
| 🧭 | Task DAG | 一个 Objective 包含多个有依赖的 Task;只有上游通过验收,下游才会 ready |
| 🔁 | Attempt 边界 | 每次执行都是一个不可变的 Attempt;终态 Attempt 永不原地复活,恢复总是创建新 Attempt |
| ✋ | 持久审批 | 敏感工具无人审批时拒绝并 park;批准后生成一次性授权,绑定 Task / Attempt / Session / Goal / 工具 / 参数 / 策略版本 / 有效期,原子消费一次 |
| ⏳ | 持久等待与事件 | awaiting_approval / awaiting_ci / awaiting_timer / awaiting_external_event;不可变事件、幂等消费、重复事件抑制 |
| 📡 | 轮询 Watcher | GitHub Checks、PR Review、本地/远程进程;退避、超时、限流、离线、陈旧结果拒绝;凭据不落库、不进日志 |
| 🧩 | 安全重规划 | graph revision;只在安全点修改 DAG,旧 revision 上的 Attempt 在数据库层面无法被验收 |
| 🌿 | 并行 worktree | 并行 Task 各用独立 git worktree;孤儿对账、分支冲突规避、合并与冲突处理、验收绑定工件指纹 |
| 🤖 | Subagent / Session fork | 按 Task 路由到普通 Session、只读 Subagent 或 Session fork;工具可见性 ≠ 授权 |
| 🧪 | 验收门 | 冻结的验收准则 + 确定性校验器(文件类、reins. 持久事实类,以及由 Reins 亲自运行测试命令的 reins.command_succeeds),写入 pass 收据才能完成 |
| 📉 | 无进展检测 | 确定性失败指纹;连续相同失败、验收停滞、重复拒绝、重复工具错误都会挂起 Task 等人处理 |
| 💥 | 崩溃恢复 | 租约 + epoch 围栏;必须有绑定 owner / objective / epoch / execution 的退出证明才能接管;同一数据库只允许一个存活的 Reins 进程 |

安装

前置条件

- Node.js ≥ 22.13(需要内置的 node:sqlite)
- DeepSeek Harness @deepseek-ai/dsh@0.2.0-rc.1(Reins 的 peer 依赖固定到这个版本)

npm install -g @deepseek-ai/dsh@0.2.0-rc.1

下文用 <DSH_HOME> 表示 Harness 的数据目录(环境变量 DSH_HOME,未设置时为 ~/.dsh),用 web 表示要安装到的 profile。

1. 准备一份固定版本的插件目录

推荐从一个固定的快照目录安装,这样之后继续修改源码不会影响已安装的版本:

git clone --branch v0.4.0 https://github.com/dreamfarer-space/dsh-reins.git reins-0.4.0

2. 安装

Reins 是一个 DSH 插件包(bundle),两种装法任选其一:

- 在网页里装:DSH Web 侧边栏 插件 → 添加插件,填入快照目录的绝对路径(例如 D:/plugins/reins-0.4.0),安装完成后点 立即启用。
- 用命令行装:

dsh plugin --profile web add file:/absolute/path/to/reins-0.4.0

装好后,Reins 出现在 插件 → 已安装 里,带图标和说明,右侧开关就是 启动 / 关闭。不需要手动编辑 cordis.patch.yml。

3. 在插件页里设置

点开 Reins 任务编排,设置直接写在这个页面里,保存后立即生效(不用重启):

| 设置 | 说明 |
| 审批策略 | 严格:所有工具都要批准;推荐(默认):读写编辑文件无需批准,运行命令、联网、子 Agent、未知工具需要批准;自定义:在推荐基础上勾选额外信任的工具(pwsh、bash、web_fetch…),并可列出始终需要批准的工具;全部信任:除“始终需要批准”的工具外全部放行(只在可回滚的目录里用) |
| 自动继续 | 事件到达、重试或上一个 Task 完成后不等 /reins-tick 就继续(Objective 也要允许:在 /reins 里说明,或 /reins-goal --auto) |
| 网页审批等待时间 | 审批在对话里等多久后释放 Agent,转为 /reins-todo 里的审批请求 |
| 默认工作目录 | 没有指定 workspace 的 Objective 在这里工作 |
| 桌面通知 / 对话中提醒 | 需要你处理或任务完成时怎么提醒你 |

这些设置保存在 profile 的 cordis.patch.yml 里(- id: reins 下的 config:),也可以手动编辑;其余配置项见配置。

4. 确认

在对话里输入 /reins,返回的用法说明会列出所有命令;输入 /reins 时 DSH 的命令菜单里也能看到它们。Reins 只约束它自己发起的 Attempt;你平时直接和 Agent 的对话不受影响。

更新、回滚与卸载

- 更新:准备一份新的快照目录,再执行一次 dsh plugin --profile web add file:<新目录>,或在插件页重新添加。
- 回滚:安装前先备份 <DSH_HOME>/profiles/web 下的 package.json、pnpm-lock.yaml、cordis.patch.yml;需要回滚时拷回这些文件,再执行 dsh plugin --profile web install。
- 关闭 / 卸载:在插件页关掉开关即停止 Reins(数据库和设置保留);卸载按钮会移除插件包。
- 从 0.3.0 之前的版本升级:旧版本需要在 cordis.patch.yml 里手写 - insert: - id: reins ...。现在由插件包自己声明这个条目,请把旧的 insert 块改成只写设置的覆盖:- id: reins 加 config:,否则会出现重复的 reins 条目。
- 从 0.3.x 升级:每个 /reins <子命令> 都变成了独立命令(/reins todo → /reins-todo,/reins dag <id> → /reins-status <id>……)。旧写法仍然可用,并会提示新名字;/reins 后面跟其它内容时,现在按自然语言需求处理。

快速上手

直接说你要什么

/reins 登录接口有空指针异常,修一下 /reins 给报表页加一个 CSV 导出,保留现在的 JSON 导出

当前对话的模型会按工作方式里的流程和你一起规划。在任何对话里都能用,包括新对话的第一条消息。如果项目不在当前对话的目录里,在需求里写上目录。

一句话 + 自己指定检查

已经知道用哪条命令证明做完了,就跳过规划:

/reins-goal 修复 clamp 的边界情况 /reins-goal 让 lint 通过 --test "npm run lint" --workspace D:/work/app /reins-goal 修复 clamp --dry-run # 只预览,不创建 /reins-goal 修复 clamp --auto # 允许事件 / 重试后自动继续(还需配置 unattendedEnabled)

不加 --test 时,Reins 自动识别测试命令(package.json 的 test 脚本、Node 测试文件、Cargo、Go、pytest),识别不到会提示你用 --test 指定。

之后只需要两条命令

/reins-todo # 所有等你处理的事:待审批、被挂起的 Task(附最后的错误)、等待 tick 的 Task /reins-approve 3f9c1a2b # 用 todo 里显示的短编号批准

批准后 Task 会在原来的会话里继续(Agent 记得之前做到哪),不需要再执行 /reins-tick。读写编辑文件默认信任;运行命令(pwsh / bash)、联网、子 Agent 仍需审批(见配置中的 trustPreset)。

在 DSH Web 里

- Reins 的会话就在侧边栏:每个 Task 的 Agent 会话出现在侧边栏的“未分组”里(以 Task 内容命名),可以点进去看 Agent 做了什么。
- 审批两种方式都行:需要审批时,那个会话会标记为“待审批”,点进去直接点“允许一次 / 拒绝”即可;如果你没去看,等待 interactiveApprovalTimeoutMs(默认 5 分钟)后它会释放 Agent,转成 /reins-todo 里带短编号的审批请求。DSH 自身的审批(例如 pwsh 请求放宽沙箱)也按同样规则处理。
- 主动提醒:有事需要你时(待审批、Task 被挂起、Objective 完成或失败),Windows 会弹出系统通知;你下一次和任意 DSH 对话说话时,Agent 也会先简短告诉你,并附上要执行的命令。
- 强制关闭 DSH 后:没有任务在执行时,重启后 Reins 自动接管。如果关闭时某个 Objective 正有 Attempt 在执行,Reins 无法证明它已停止,就只把这个 Objective 挂起(不接管、不改动它),其余照常运行;/reins-todo、对话提醒和系统通知都会提示。确认旧的 DSH 已经关闭后执行 /reins-recover <id> --confirm:未完成的 Attempt 记为 interrupted,结果不确定的副作用交给你确认,然后继续。

手写多 Task:让 PR 达到可合并状态

需要完全掌控 Task、依赖和验收准则时,用 /reins-objective 手写 Task 依赖图:先实现修复,再跑测试,最后等 CI 通过。

/reins-objective {"id":"pr-7","summary":"让 PR #7 达到可合并状态","workspace":"D:/work/app","tasks":[
{"id":"fix","title":"实现修复","criteria":[{"kind":"file_contains","spec":{"path":"src/fix.js","substring":"FIXED"}}]},
{"id":"tests","title":"运行测试","dependsOn":["fix"],"criteria":[{"kind":"reins.command_succeeds","spec":{"argv":["node","--test"],"timeoutMs":300000}}]},
{"id":"ci","title":"等待 CI 通过","dependsOn":["tests"],"criteria":[{"kind":"reins.wait_satisfied","spec":{"waitKind":"ci","eventType":"ci.passed"}}]} ]}

接下来会发生:

1. Reins 为 fix 创建 Attempt。Agent 收到一份结构化交接(目标、当前 Task、验收准则、上次失败原因、预算……),完成后调用 task_complete。
2. 验收门检查准则:通过才把 fix 置为 completed,tests 随之 ready。tests 的验收由 Reins 自己运行 node --test,只看退出码,不采信 Agent 写的任何测试报告。
3. 在 ci 中,Agent 推送后调用 reins_wait(kind: "ci")。当前 Attempt 结束、Agent 停止,Watcher 开始轮询。
4. 推送是敏感操作:无人在场时会被拒绝并 park。用 /reins-todo 查看待审批请求,用 /reins-approve <短编号> 批准;Task 随即在原会话中继续。
5. CI 失败时,新 Attempt 会带着精简的失败上下文(失败的检查项、失败指纹)继续修复;CI 通过后完成验收。

随时查看全貌:

/reins-status pr-7

[!IMPORTANT]
无人值守执行默认关闭。 由事件、计时器、自动重试或“上一个 Task 验收后开始下一个 Task”触发的 Attempt 属于无人值守执行,默认不会自动启动;/reins-todo 会列出它们,执行 /reins-tick <objectiveId> 推进。你批准过的审批不在此列:批准即授权,Task 会自动继续。若希望 Reins 全程自动继续,请同时设置配置 unattendedEnabled: true 与 Objective 字段 "unattendedOptIn": true(/reins-goal 用 --auto;用 /reins 时在需求里说明)。

命令

每个操作都是独立的顶层命令,DSH 的命令菜单可以直接列出和补全。approve、deny、resolve 可以使用 /reins-todo 显示的短编号(唯一前缀,至少 4 位,有歧义时拒绝)。

| 命令 | 作用 |
| /reins <你想要什么> | 自然语言需求:当前对话的模型和你一起规划并创建 Objective。不带内容时显示用法 |
| /reins-goal <一句话> [--test "<命令>"] [--workspace <目录>] [--id <id>] [--auto] [--dry-run] | 一句话创建单 Task Objective,测试命令退出码为 0 才算完成 |
| /reins-todo | 所有等你处理的事:待审批(带短编号)、正在 DSH 对话里等你回答的审批、结果不确定的副作用、被挂起的 Task 及最后的错误、等待 tick 的 Task、强制关闭后被挂起的 Objective |
| /reins-status [objectiveId] | 不带 id:全部 Objective,未完成的在前;带 id:Objective、Task DAG、当前 Attempt、验收、等待、审批、Watcher、预算、租约、最近事件 |
| /reins-approve <id> [理由] | 批准一个持久审批请求 |
| /reins-deny <id> [理由] | 拒绝,并把原因告诉 Agent |
| /reins-pause <objectiveId> | 暂停:正在执行的 Attempt 停下,状态保留 |
| /reins-resume <objectiveId> | 恢复暂停的 Objective;也会解除进行中 Objective 里被挂起的 Task(无进展、连续审批、等待超时),并重新开始计算无进展 |
| /reins-cancel <objectiveId> | 取消;未使用的授权先作废 |
| /reins-tick [objectiveId] | 立即执行一次调度(视为有人在场) |
| /reins-recover [objectiveId] [--confirm] | 强制关闭 DSH 后:列出被旧进程占住的 Objective;--confirm 在确认旧进程已不存在后释放它们(Reins 没能启动时也可用) |
| /reins-objective <json> | 用手写的 Task 依赖图创建 Objective(字段见下表) |
| /reins-replan <objectiveId> <json> | 安全点重规划:{"add":[…],"remove":[…],"modify":[…],"reopen":[…],"reason":"…"} |
| /reins-event <json> | 注入一个外部持久事件 |
| /reins-resolve <id> done\|not_done | 人工确认一个结果不确定(ambiguous)的副作用是否发生 |
| /reins-merge <objectiveId> <taskId> | 把已验收 Task 的 worktree 合并回当前分支 |

Objective 与 Task 字段

| 字段 | 说明 |
| id | Objective id(可选,默认随机) |
| summary | 目标描述 |
| workspace | 该 Objective 的工作目录(绝对路径,必须存在);Agent 在此工作,文件类准则和验收命令也在此执行。默认取配置 evidenceRoot / repoRoot |
| tasks[] | Task 列表,至少一个 |
| tasks[].id / title / description | Task 标识、标题、详细说明 |
| tasks[].dependsOn | 依赖的 Task id 列表(不允许成环) |
| tasks[].required | 是否为完成 Objective 所必需,默认 true |
| tasks[].criteria[] | 验收准则:{"kind","spec","required"} |
| tasks[].capability | 执行路由,见下方“能力路由” |
| tasks[].maxAttempts / maxWallTime | Task 级预算 |
| maxAttempts / maxWallTime | Objective 级预算;maxWallTime 默认 7 天,包含等待时间 |
| maxActiveMs | Objective 实际执行时间上限,不含等待时间 |
| maxParallel | 同一 Objective 的最大并行 Attempt 数(>1 需要 worktree 隔离) |
| noProgressLimit | 无进展判定阈值(默认取配置 noProgressAttemptLimit) |
| unattendedOptIn | 是否允许无人值守继续 |

能力路由 capability

| 字段 | 说明 |
| route | session(默认,普通 Agent + Goal)、subagent(进程内子 Agent)、fork(从上一次 Attempt 的会话派生) |
| readOnly | subagent / fork 默认 true:敏感工具一律拒绝 |
| isolation | none 或 worktree;会写代码的 Subagent 必须使用 worktree |
| provider | Subagent 提供方名称(默认 spawn,fork 为 fork) |
| model / modelProvider | 指定模型与模型提供方 |
| tools.allow / tools.deny | 限制可见工具。可见不等于授权,敏感调用仍需审批 |
| maxGoalRounds | 每个 Attempt 的 Goal 轮数上限,默认 3 |

验收准则类型

| kind | spec | 说明 |
| file_exists / file_absent | {"path"} | 文件存在 / 不存在 |
| file_contains | {"path","substring"} | 文件包含文本 |
| file_sha256_equals | {"path","sha256"} | 文件哈希相等 |
| json_field_equals | {"path","pointer","expected"} | JSON 字段相等,pointer 为点分路径(如 a.b) |
| marker_count_equals | {"path","marker","count"} | 标记出现次数 |
| reins.wait_satisfied | {"waitKind","eventType"} | 该 Task 最近一次等待已被指定类型的持久事件满足,且当前 Attempt 晚于该事件 |
| reins.output_contains | {"substring"} | Subagent 的输出包含指定文本 |
| reins.no_ambiguous_side_effects | {} | 该 Task 不存在结果不确定的副作用 |
| reins.command_succeeds | {"argv","cwd","timeoutMs","expectExitCode","pins","env"} | 验收时由 Reins 亲自运行命令,退出码等于 expectExitCode(默认 0)才通过 |

文件路径一律相对于 Attempt 的工作目录(Objective 的 workspace、evidenceRoot 或 worktree),禁止绝对路径和 ..。

[!WARNING]
文件类准则检查的是工作目录里的文件,Agent 自己也能写这些文件。真模型测试中出现过:测试失败后,模型把 test-report.json 改写成 {"passed":true},json_field_equals 因此通过。凡是“测试通过”“构建成功”这类结论,请用 reins.command_succeeds,不要用报告文件。

reins.command_succeeds 的规则:

- argv 是参数数组,不经过 shell;argv[0] 为 node 时使用运行 Harness 的同一个 Node。Windows 上的 .cmd / .bat 不能直接运行,请写成 ["node", "<脚本路径>"]。(/reins 和 /reins-goal 会把你给的命令行自动包成 cmd /d /s /c 或 sh -c。)
- cwd 为工作目录内的相对路径(默认 .);timeoutMs 默认 10 分钟、最长 1 小时,超时会结束整个进程树并判为失败。
- 子进程只拿到最小环境变量(PATH、系统目录、临时目录等)加上 env 中显式声明的值,不继承其它环境变量,没有标准输入。
- 命令会执行工作目录里的代码,而 Agent 可能改过这些代码。用 pins([{"path","sha256"}])钉住测试文件:哈希不符时直接判失败,命令根本不会运行。/reins 会钉住规划时写下的检查文件。
- task_complete 预校验时也会运行一次,失败输出的末尾会反馈给 Agent;最终结论仍以验收门重新运行的结果为准。

Agent 侧工具

| 工具 | 谁用 | 说明 |
| reins_create_objective | 你输入 /reins 的那个对话 | 创建规划好的 Objective。每次 /reins 只能用一次,且只在那个对话里可用,Reins 自己的会话不能用;校验 Task、顺序、检查命令,并钉住 checkFiles。dryRun 只展示计划、不创建 |
| task_complete | Reins 的 Agent | 声明 Task 已完成;Reins 先做预校验,最终由验收门决定 |
| reins_wait | Reins 的 Agent | 结束当前 Attempt 并持久等待,事件到达后以新 Attempt 继续 |

reins_wait 的 kind:

| kind | 参数 | 恢复事件 |
| ci | repo(owner/name)、sha、可选 requiredChecks | ci.passed / ci.failed |
| pr_review | repo、number | review.approved / review.changes_requested / review.commented / pr.merged / pr.closed |
| process | exitFile 或 pid、可选 name | process.succeeded / process.failed / process.lost |
| remote | url、可选 name(主机必须在配置 remoteWatcherHosts 中) | remote.succeeded / remote.failed / remote.cancelled |
| timer | delayMs | timer.fired |
| external_event | subject、eventTypes | 通过 /reins-event 注入的事件 |

所有等待都可以带 timeoutMs(默认 24 小时);超时后 Task 挂起等人处理,不会盲目重试。

外部事件示例(subject 需加 ext: 前缀,与 reins_wait 的 subject 对应):

/reins-event {"source":"deployer","sourceEventId":"deploy-42","type":"deploy.done","subject":"ext:deploy-42","objective":"pr-7","payload":{"env":"staging"}}

source + sourceEventId 是事件的稳定身份:重复投递会被抑制;同一身份但内容不同会被标记为冲突,不会覆盖原事件。

单 Task 命令(Phase 2/3,供脚本使用)

这些命令只能以 /reins <子命令> 的形式使用:create | <task title>、criteria-add <objectiveId> <taskId> <kind> <json-spec>、criteria-list <objectiveId> <taskId> [revision]、criteria-revise <objectiveId> <taskId>、accept <objectiveId> <taskId> [attemptId]、receipts <objectiveId> <taskId>。/reins-status、-pause、-resume、-cancel 对这类 Objective 同样有效。新工作请用 /reins 或 /reins-goal。

配置

所有配置都经过严格校验:未知键、非法数值、云端 URL、UNC 网络路径都会被拒绝。

| 配置项 | 默认值 | 说明 |
| databasePath | ~/.dsh/dsh-reins/reins.sqlite | 本地 SQLite 路径(目录不存在时自动创建)。同一数据库只允许一个存活的 Reins 进程 |
| evidenceRoot | null | 未指定 workspace 的 Objective 的默认工作目录;与 repoRoot 都未设置时,编排型 Objective 必须自带 workspace |
| approvalRequiredTools | [] | 始终需要审批的工具 |
| trustPreset | workspace | 内置信任集合。workspace:read、glob、grep、write、edit、str_replace_editor、todo_write、get_goal、update_goal 以及 Reins 的 task_complete、reins_wait;运行命令、联网、子 Agent 和未知工具仍需审批。none:只信任 trustedTools |
| trustedTools | [] | 在预设之外额外信任的工具(例如 ["pwsh"]) |
| trustUnlistedTools | false | 是否信任未列出的工具。默认不信任:未知副作用一律需要审批 |
| unattendedEnabled | false | 是否允许无人值守执行(还需 Objective 同时 unattendedOptIn) |
| defaultMaxAttempts | 5 | 单个 Task 默认最大 Attempt 数 |
| defaultMaxWallTimeMs | 300000 | 单 Task Objective 的默认墙钟预算 |
| orchestratedDefaultWallTimeMs | 7 天 | 编排型 Objective 的默认墙钟预算(包含等待) |
| noProgressAttemptLimit | 3 | 无进展判定阈值 |
| maxConcurrentAttempts | 1 | 全局并发 Attempt 上限(最大 16;>1 需要 worktree 隔离) |
| repoRoot / worktreeBaseDir | null | git 仓库根目录与 worktree 存放目录;设置 repoRoot 后才启用并行 worktree |
| approvalRequestTtlMs | 24 小时 | 待审批请求的有效期 |
| allowanceTtlMs | 15 分钟 | 批准后一次性授权的有效期 |
| subagentWallTimeMs | 600000 | 单个 Subagent Attempt 的时间上限 |
| watcherCredentials | {} | 凭据引用名 → 环境变量名,例如 {"github": "GITHUB_TOKEN"}。只写变量名,绝不写密钥 |
| githubApiBase | https://api.github.com | GitHub API 地址(可指向 GitHub Enterprise) |
| notifyDesktop | true | 需要你时弹出系统通知(Windows;其他系统需配置 notifyCommand) |
| notifyInConversation | true | 你下一次和 DSH 对话时,由 Agent 简短提醒 |
| notifyCommand | [] | 自定义通知命令(参数数组),通过环境变量 REINS_TITLE / REINS_BODY 接收内容 |
| interactiveApprovalTimeoutMs | 5 分钟 | DSH 对话里的审批等待多久后释放 Agent、转为 /reins-approve 请求(0 表示一直等) |
| minTimerWaitMs | 60 秒 | Agent 用 reins_wait 等待计时器的最短时间;更短的会被拒绝,Agent 继续工作 |
| remoteWatcherHosts | [] | remote 等待允许访问的主机("host" 或 "host:port")。为空时禁用 remote 等待 |
| orchestratorTickMs / watcherPollMs / watcherTimeoutMs | 2000 / 1000 / 15000 | 调度间隔、轮询间隔、单次查询超时 |
| leaseDurationMs / heartbeatIntervalMs | 10000 / 3000 | 租约时长与心跳间隔(每个租约期至少续约 3 次) |

预算口径:maxWallTime 是墙钟时间,包含等待 CI、审批的时间;maxActiveMs 只统计 Attempt 的实际执行时间;每个 Attempt 的轮数由 maxGoalRounds 约束。没有可靠的 token 遥测,因此不提供 token / 费用预算。

架构

Reins 是一个 Cordis 插件,只使用 DeepSeek Harness 的公开 API,不修改 Harness 核心:

- 命令与规划(src/commands/):/reins 与 /reins- 命令;/reins <需求> 通过 agent.followup 把需求交给当前对话的模型,reins_create_objective 把它的计划变成 Objective。
- 编排层(src/orchestrator/):调度 Ready Task,维护 DAG 与 graph revision,管理持久等待与事件、Watcher、持久审批、worktree、能力路由、失败指纹与交接。
- 执行运行时(src/orchestration/runtime.js):原子准入、租约围栏、外层结算(disarm → cancel → idle → flush → 终态收据)、工具与步骤守卫。每个 Attempt 内,只有 Harness 的 goal-round-driver 负责续跑。
- 验收门(src/acceptance/):唯一能让 Task 进入 completed 的路径;数据库触发器保证没有 pass 收据就无法完成。
- 存储(src/store/ + migrations/):SQLite(node:sqlite),迁移 001–015,只增审计,关键不变量由触发器强制。

一次恢复是怎么发生的

CI、定时器、PR Review、外部事件都走同一条路径:等待 → 持久事件 → 幂等消费 → 新 Attempt。

安全模型

Reins 的每一项能力都服从下面这些不变量,测试和真实 Harness 验证都围绕它们展开:

- Harness 负责执行,Reins 负责编排。 只用公开 API,不导入私有模块,不修改 Harness 核心。
- 一个 Attempt 内只有 goal-round-driver 负责续跑;一个 Objective 同时只有一个有效写者(租约 + epoch 围栏)。
- 终态 Attempt 永不原地复活;被 park 的 Attempt 恢复时总是创建新 Attempt。
- 敏感操作无人审批即拒绝;拒绝之后还必须完成 park / cancel 与静止证明。
- 审批不等于验收,Goal 完成不等于 Task 完成。 审批不能绕过沙箱、守卫、租约、预算或验收门。
- 等待状态不保留 Agent 循环。
- 结果不确定的副作用不自动重放;授权一旦消费,即使工具失败或进程崩溃也不会自动恢复。
- SQLite 与 Harness 会话日志是两个独立的一致性域:只有拿到 flush 证明才算静止;重启后不会把任何不确定状态当成成功。
- 崩溃后接管需要退出证明:证明必须绑定 owner / objective / epoch / execution;没有证明时只挂起该 Objective(LEASE_BUSY),绝不改动它。
- 一个数据库只对应一个存活的 Reins 进程:另一个存活进程持有数据库时以 DATABASE_IN_USE 拒绝启动;被接管的进程立即停止调度和轮询。
- “测试通过”由 Reins 亲自验证:reins.command_succeeds 在验收时运行冻结的命令,被钉住的文件改动过就不运行。
- 规划出的 Objective 只能来自你:reins_create_objective 只在你于该对话输入 /reins 之后可用、只能用一次,Reins 自己的会话不能用;规划时写下的检查文件在任何 Agent 开始之前就被钉住。
- 强制关闭后保守接管:强制关闭留下的租约,只有在“旧进程已记录于本机且确认不存在、且没有执行中的工作”时自动接管;有执行中的 Attempt 时必须由你通过 /reins-recover --confirm 确认,确认记录只增不改。
- 审批后在原会话中继续:被审批 / 等待 park 的 Attempt,下一个 Attempt 重新打开同一个 Harness 会话(仍是新 Attempt、新 Goal,授权仍只放行那一次精确调用);失败、崩溃、重规划后使用新会话。连续 10 次审批 park 仍未完成的 Task 会被挂起,交给你决定。
- Agent 指定的 URL 只能访问白名单主机:remote 等待在注册时和每次轮询前都检查 remoteWatcherHosts,拒绝重定向,从不携带凭据。

测试与验证

npm ci # 安装依赖
npm run typecheck # 公开 Harness 接口的类型检查
npm test # 主回归(约 70 秒)
npm run test:runner # 场景驱动与验证器回归(含验证器反例)
npm run test:live # 真实 DeepSeek Harness 场景(约 4 分钟)
npm run test:live:phase3 # Phase 3 单 Task 真实 Harness 场景(约 2 分钟)
node scripts/build-diagrams.mjs # 重新生成中英文 README 示意图

当前结果:

| 套件 | 结果 |
| npm test | 499 / 499 |
| npm run test:runner | 48 / 48(含 Phase 4 验证器反例 28 个) |
| npm run test:live | 7 / 7 |
| npm run test:live:phase3 | 11 / 11 项门禁 |
| npm run typecheck | 通过 |

真实 Harness 场景(npm run test:live)

在隔离的 DSH_HOME 中启动真实的 dsh --profile 进程:真实的 Cordis、Agent、agent loop、goal-round-driver、会话持久化、工具管线和无头审批,只有模型换成确定性 fixture。用户操作走真实注册的 /reins- 命令;GitHub 由一个在进程重启之间保持状态的本地替身提供。

| 用例 | 验证内容 |
| e2e | “让 PR 可合并”五个 Task:无人审批 → park → 批准 → 新 Attempt 精确推送一次;CI 失败 → 修复 → 再次审批推送 → CI 通过;处理 Review;全部验收后 Objective 完成 |
| crash-running | Attempt 运行中被 SIGKILL,带退出证明重启:对账为 interrupted,重试并完成 |
| crash-consumed | 授权已消费、推送已发生但结果未回报时被杀:副作用标记 ambiguous,不重放,人工确认后完成 |
| crash-awaiting-ci | 等待 CI 时被杀:Watcher 恢复轮询并驱动后续 Attempt 完成 |
| no-proof | 被杀后没有退出证明:只挂起该 Objective(LEASE_BUSY),不改动它,Reins 其余部分照常运行 |
| second-process | 第一个进程运行 e2e 期间,在同一数据库上启动第二个进程:第二个进程以 DATABASE_IN_USE 拒绝启动、不执行任何命令,第一个进程照常完成 |
| subagent | 真实 Subagent 与 Session fork:只读子 Agent 的敏感调用被识别并拒绝、零物理副作用;子会话的 parentSession 等于 Reins 锚点会话;fork 重试确实从上一次会话派生 |

独立验证器 scripts/phase4-harness-verify.mjs 只读取 SQLite(只读打开)、物理副作用日志、监督进程记录和真实会话日志,每个用例做 6–59 项检查,不采信场景插件自己的结论;tests/runner/phase4-verifier-negative.test.js 用真实运行快照逐项篡改,证明验证器会判 FAIL。

[!NOTE]
上述真实 Harness 场景使用确定性模型;GitHub 由本地替身提供。真实大模型(DeepSeek)另外在 DSH Web 中验证过:/reins 规划(先看项目、就真正的分歧提问、写好并钉住检查、创建;随后 Reins 的 Agent 完成工作,被钉住的检查通过);/reins-goal;正常修复、被拒后纠正、审批、无法完成、默认工具策略、伪造测试报告,以及用 reins.command_succeeds 验收的场景(伪造的报告文件存在时,Reins 自己运行测试得到 exit=1,Task 不会完成)。

项目结构

reins/
├── src/
│ ├── plugin.js # 插件入口(Cordis apply) │ ├── config.js # 配置 schema 与严格校验 │ ├── live-config.js # 插件页保存的设置无需重载即生效 │ ├── commands/ # /reins 与 /reins- 命令,/reins 规划(plan.js) │ ├── orchestrator/ # 编排层:DAG、等待/事件、Watcher、审批、worktree、路由、指纹、交接 │ ├── orchestration/ # 执行运行时:准入、预算、无进展、外层结算 │ ├── acceptance/ # 验收门、准则冻结、校验器、收据完整性 │ ├── approval/ # 关系型审批(Phase 2) │ ├── store/ # SQLite 存储、事务、迁移执行、实例锁 │ ├── reconcile/ # 启动对账 │ ├── harness/ # Harness 公开 API 适配 │ └── tools/ # task_complete 工具 ├── client/client.js # DSH Web 插件页上的设置页 ├── migrations/ # 001–015,只新增、不改写 ├── scripts/ # 真实 Harness 场景驱动、独立验证器、示意图生成 ├── tests/ # 按功能分组的回归测试(见 tests/README.md) ├── docs/assets/ # README 图片(中英文) └── smoke/public-api.ts # 公开 API 类型检查样例

开发

开发规则

1. 新功能或缺陷修复先写能复现问题的测试,再修改实现。
2. 不删除或放宽安全断言来让测试变绿。身份绑定、租约围栏、审批与验收默认拒绝的语义必须保留。
3. Task / Objective 完成必须走验收门,不能只凭模型说“完成”或直接改数据库。
4. 不把 API Key、真实用户配置、真实数据库、日志或运行证据提交到仓库。
5. 每次改动后运行 typecheck、test、test:runner;涉及框架生命周期、恢复或工具调用时,还要运行 test:live。

约定

- 数据库结构变更只能新增迁移文件,不能改写已有的 001–015。
- 新的用户操作就是新的顶层命令 /reins-<操作>(src/commands/register.js 中 COMMANDS 的一行);真实 Cordis 卸载测试固定了完整的命令集合。listValidatorKinds() 固定为 6 种文件校验器;新的验收类型走独立的 reins.* 校验器注册表。
- 测试临时文件统一使用 tests/fixtures/scratch.js,不要写死本机路径。
- 文本文件统一使用 LF(由 .gitattributes 强制)。
- 中英文示意图由 scripts/build-diagrams.mjs 从同一份描述生成。

发布内容

package.json 的 files 只包含插件本身(src、migrations、client、locale、插件包 patch、图标、许可证与 README);测试、脚本和开发依赖不进入发布包。

已知限制

- 文件类验收准则信任工作目录中的文件,而 Agent 能写这些文件;涉及“测试 / 构建通过”的结论请使用 reins.command_succeeds。
- reins.command_succeeds 会运行工作目录中的代码;没有被 pins 钉住的文件,Agent 都可能改过。它不在沙箱中运行。
- /reins 的规划依赖当前对话模型的判断:它可能自己决定了你本想被问到的事,或者选了一个比你期望更弱的检查。请看它的总结;/reins-status <id> 会显示每个 Task 的具体检查。/reins-goal 不会钉住测试文件;需要更强保证时,用 /reins(会钉住它写的检查)或带 pins 的 /reins-objective。
- 真实大模型覆盖了 /reins 规划、/reins-goal 和单 Task 场景;CI / PR Watcher、崩溃恢复、并行 worktree、Subagent / fork、重规划只用确定性模型在真实 Harness 中验证过。
- 只在 Windows 上测试过;Linux / macOS 未验证。
- 系统通知目前只在 Windows 上内置;其他系统需自行配置 notifyCommand。
- DSH 的沙箱会阻止 node --test 这类需要启动子进程的命令,Agent 可能为此请求放宽沙箱;验收由 Reins 在沙箱外自己运行测试,不受影响。
- 单 Task Objective(/reins create)同一时间只能执行一个,且未设置 evidenceRoot 时在 DSH 进程的启动目录中工作;编排型 Objective 通过独立的执行槽位并行。

许可证

MIT。
预览分享卡片长什么样 ↓dreamfarer-space/dsh-reins
分享 · DeepSeek Harness Hub
dreamfarer-space/dsh-reins

用一句话下达目标,自动跨会话推进直到自检通过

这是你的仓库?认领条目

回到顶部