Khorsheed/dsh-plugins--packages-mission ↗★ 0

@khorsheed/dsh-mission

通用任务与状态机管理系统 适合需要批量创建、跟踪和控制复杂任务生命周期的用户。

包名
@khorsheed/dsh-mission
兼容性
待验证
Harness 依赖范围
^0.1.0-rc.6
Cordis 依赖范围
^4.0.1
版本
0.1.0-rc.1
许可证
MIT
最近更新
2026年9月28日

安装

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:Khorsheed/dsh-plugins#548eeabe331c121621b2d08deab4278ad81d6312&path:packages/mission

dsh-mission

English | 中文

dsh 生态的通用任务管理:mission 是一个工作项——状态、标签、计划数据(依赖 / 一次性定时)、attempt、不透明资源引用、产物索引、append-only 命名空间注解;run 是从模板批量创建的一批 mission。模板声明状态机,run 创建时冻结它,每个 mission 强制它:声明即强制——未声明的转移一律 fail loud,guard 是确定性的,且任何代码路径都不做自动转移。

M1 交付:store、带三种内置 guard 的状态机、run 模板 lint、五桶投影、服务面(ctx.mission)、十二个模型工具、dsh-mission CLI;那十二个工具自 M4'③ 起不由本包注册——定义仍在本包(src/tool.ts),注册与 tool:mission 提示词段归伴生行 @khorsheed/dsh-mission-tool,由 agent preset 按会话授予(见模型工具);M2 补上 /mission slash 面与带闸的 bundle 导出(slash 的注册自 preset 可见性收口起归伴生行——落进 preset scope 层,只有授予会话可见;handler 与定义留在本包);M4 补上 Typert Remote 数据面驱动的 web 会话 tab。

工作方式

  • Run——一个 JSON 文件(runs/.json),装冻结的状态机、全部 mission 及其 attempt、全部 annotation。runs//data/… 是追加式运行数据树,submit 的产出落在这里。

  • Mission——一个工作项,id 在 run 内唯一。labels 承载任意坐标(如 layer=dwd),dependsOn / scheduledAt 是计划数据。计划数据只改变投影归属——mission 永不点火;发起仍是人 / agent / 外部编排。

  • attempt 与 checkpoint 不混——attempt 是整格重跑(retry 必须给自由文本 reason 与通用类别 infrastructure | operator | outcome,新 attempt 与其 history 都记录原因,原 attempt 不可变保留);checkpoint 是 attempt 内的连续推进点。两者在数据模型与 API 上不可混用。

  • Guard——转移前置条件,内置三种,不开 seam:file-check(期望文件位于相对该 attempt 运行数据目录的目录下——无插值;以 / 结尾的目录项必须递归包含至少一个常规文件)、schema-check(指定的 JSON 输入通过子集校验)、attested(外部脚本或人经 attest 登记的 key)。

    schema-check 的 inputFrom 指定校验输入(默认 submission):

    inputFrom校验对象用途
    submissionsubmit 登记的 payload(submit 时预先校验)要结构约束的产出
    run-metarun 的 meta 对象在最早的转移上钉住场景数据——如要求数据集快照字段(datasetId/commit),快照变了就过不去(meta 侧的 refs.fingerprint 同类物)

    mission 只认 JSON Schema——具体字段是模板里声明的场景数据,不进插件词汇。

    当前状态只有一条以 submission 为输入的 schema-check 出边时,submit 仍自动预校验它;有多条时必须传意向 to,只预校验该边。transition 始终重新执行实际边的 guard,to 不会代替状态转移。

  • 五桶投影——队列视图的筛选维度,从状态机形状加计划数据派生:终态(无出边)→ done;dependsOn 未满足 → blocked;scheduledAt 未到 → scheduled;初始态(无入边)→ ready;其余 → active。

  • 可释放状态——releasableStates 非空即声明「本 run 有资源要释放」。is-releasable 回答 mission 持有的资源可否销毁,lint 负责闸的完整性(见下)。run status 的「持有 resource 未 releasable」警示只对位于闸上游状态的 mission 触发——可释放状态经声明 transitions 可达的下游状态(如 released)视为已了结、不报警,不可变的 refs 记录照常保留。

安装与加载

包的唯一身份是 @khorsheed/dsh-mission,在 dsh-plugins monorepo 开发并从那里发布到 npm:

npm install @deepseek-ai/dsh                            # 宿主(dsh web / dsh CLI)
dsh plugin --profile web add @khorsheed/dsh-mission     # 本插件

包声明了 dsh.bundle,add 会把它的 cordis.patch.yml 行(裸 mission 挂载)合入 profile 的 bundles 层——无需手改 cordis.yml。一个 composition 只能挂载 mission 行 id 一次;向可能已挂载该 id 的 composition 添加前,先 dsh --profile web --dump-config | grep mission 确认。源码方式:clone monorepo,包在 packages/mission(pnpm install && pnpm run build)。

配置(全部可选):dataDir——宿主实例的数据根(默认 $DSH_HOME/state/mission,否则 /.dsh-mission)。tools 不再是本行的键:模型工具的分组配置搬到了伴生行 @khorsheed/dsh-mission-tool,见模型工具。

存储与并发

  • 宿主实例数据根:插件 dataDir > $DSH_HOME/state/mission/ > /.dsh-mission。
  • CLI 数据根:--data-dir > $DSH_MISSION_DATA_DIR > $DSH_HOME/state/mission/ > /.dsh-mission。实例 patch 里的 dataDir 对宿主外 CLI 不可见;CLI 与实例要共用数据时,必须用 --data-dir 或 DSH_MISSION_DATA_DIR 显式指向同一路径。
  • runs/.json——状态与索引(run、mission、attempt、annotation);一个 run 一个文件。
  • runs//data//attempt-/…——运行数据本体。submit 追加写入:同路径同字节是幂等 no-op,同路径不同字节 fail loud。file-check 的 dir 相对 attempt 目录解析。
  • 并发写在 per-run 锁文件上串行(手写 wx 创建 + stale-pid 回收——无依赖);每次变更都是 锁 → 读 → 改 → 临时写 → 原子改名,宿主内服务与宿主外 CLI 写同一 store 也安全。查询走 JSON 索引,不翻目录。

run 模板(JSON)

{
  "name": "content-pack-daily",
  "states": ["queued", "active", "done", "failed"],
  "transitions": [{ "from": "queued", "to": "active" }, { "from": "active", "to": "done" }, { "from": "active", "to": "failed" }],
  "missions": [
    { "id": "ods-extract", "labels": { "layer": "ods" } },
    { "id": "dwd-clean", "labels": { "layer": "dwd" }, "dependsOn": ["ods-extract"] }
  ]
}

状态机也可以嵌在 stateMachine 键下,两种写法归一化后相同。模板必须恰有一个初始态。内置 simple 模板(queued → active → done | failed)支撑零配置隐式 run:不带 run 的 mission_create 都落在这里(知道会话时按会话隔离,CLI 落 default)。

Lint(dsh-mission run lint,建 run 时同样强制执行——error 拒绝建 run):

  • error:releasableStates 非空时,凡进入可释放状态的转移必须带 guard——没有 guard 的释放许可是空闸;
  • warning:存在不经任何可释放状态即可到达的终态(资源可能泄漏);
  • error:schema-check 的 schema 缺失、不可解析、或超出支持的子集(type / required / properties / items / if / then / const / enum / additionalProperties——手写校验器,不依赖 ajv)。

simple 模板的 releasableStates 刻意为空:日常工作项不持有可销毁资源,给每个 active → done 挂 guard 会让零配置路径不可用。

模型工具

本包不再注册任何模型工具(BREAKING):工具与 tool:mission 提示词段归伴生行 @khorsheed/dsh-mission-tool,由 agent preset 按会话授予。迁移两步:把伴生包作为依赖安装,并在目标 preset 的 agent.cordis.yml 里加两行——- id: mission-tool 与 name: '@khorsheed/dsh-mission-tool'(该行可带 config: { tools: read })。下面的清单、分组表与行为描述自此描述的是伴生行的工具面;服务面、CLI、slash 与 tab 仍归本包。

mission_run_create / mission_run_list / mission_run_status / mission_create / mission_list / mission_get / mission_transition / mission_submit / mission_annotate / mission_attest / mission_retry / mission_is_releasable。mission_submit.to 与服务面的意向边语义一致;mission_retry 必须带 reason 与 category。写工具走标准 tools/pre-execute 审批管线;调用方会话 id 以 tool: 记入 history。配套系统提示词段(tool:mission)向模型简述用法。export 刻意不做成工具——分享 run bundle 是发起类人决定(仅 CLI/slash/tab,带泄题闸)。

工具按组注册——挂载时用 tools 选一组,因为 preset 只能在已注册的工具里挑,挑不掉 profile 层注册的工具;写不写得动,必须在注册处决定。

tools注册的工具
all(默认)上面 12 个,行为与本配置项加入前完全一致
readmission_run_list / mission_run_status / mission_list / mission_get
none无

read 是给「写由别人做」的挂载用的:run 由服务面的调用方、CLI 或 tab 前的人推动,模型只读队列。mission_is_releasable 虽然只读,仍留在 all——它回答的是「持有的资源可否销毁」,属于持有资源的那一侧,而 read 挂载按定义不是那一侧。系统提示词段(tool:mission)按档位走:all 用原文案,read 换成只讲这四个工具、并说明写由谁做的文案,none 干脆不注册这一段。服务面、CLI、slash、tab 三档都不受影响——工具面是唯一被裁的面。

服务面

其他插件经 ctx.get('mission') 消费——进程内合约,绝不为子进程或手写 JSON 文件。除工具对应的全部方法外,细粒度方法有 setRefs(资源标识 / 环境指纹 / sessions)、addArtifact、addCheckpoint(ref 只由资源持有方填;它与 submit 登记的同名无 ref checkpoint 合并——绝不产生重复条目)、annotate、isReleasable。

CLI

dsh-mission (或 node lib/cli.js);所有命令接受 --data-dir DIR。退出码:0 成功 / 可释放,1 失败 / 不可释放 / lint error,2 用法错误。从 PATH 或 pnpm 的 .bin 软链调用与直连 lib/cli.js 等价:入口守卫先把 argv[1] 解析成真实路径再比对,软链路径不会让它静默空跑。

dsh-mission run create --template t.json [--id ID] [--meta JSON]
dsh-mission run lint --template t.json        # error 拒绝;有 error 时退出码 1
dsh-mission run list
dsh-mission run status RUN_ID                 # 五桶投影表
dsh-mission create [--run ID] [--id ID] [--title T] [--label k=v]... [--depends-on a,b] [--scheduled-at MS]
dsh-mission list [--run ID] [--bucket B] [--label k=v]...
dsh-mission get MISSION_ID [--run ID]
dsh-mission transition MISSION_ID TO [--note N] [--run ID]
dsh-mission submit MISSION_ID [--file SRC[:DEST]]... [--json JSON | --json-file F] [--to TO] [--checkpoint NAME] [--run ID]
dsh-mission annotate MISSION_ID --ns NS --payload JSON [--run ID]
dsh-mission attest MISSION_ID --key K [--note N] [--run ID]
dsh-mission retry MISSION_ID --reason TEXT --category infrastructure|operator|outcome [--run ID]
dsh-mission set-refs MISSION_ID [--resource R] [--fingerprint F] [--session S]... [--run ID]
dsh-mission add-artifact MISSION_ID --path P --kind K [--run ID]   # P 必须已存在于该 attempt 的运行数据目录
dsh-mission add-checkpoint MISSION_ID --name N [--ref R] [--artifact A]... [--run ID]
dsh-mission is-releasable MISSION_ID [--run ID]   # 退出码 0/1,供销毁脚本使用
dsh-mission export RUN_ID --out DIR [--snapshot-dir DIR] [--snapshot-repo R --snapshot-commit C [--snapshot-dataset ID]]
         [--layer NAME]... [--guarded NAME]...   # 自包含 bundle(泄题闸见下)

slash 命令

一个 /mission 命令加子命令,是同一服务内核上的薄封装(面向人,与面向 agent 的模型工具、面向脚本的 CLI 并列):

/mission queue [--run ID] [--bucket ready|scheduled|blocked|active|done] [--all]
/mission run list
/mission run status RUN_ID
/mission run create --template FILE [--id ID] [--meta JSON]
/mission retry MISSION_ID --reason TEXT --category infrastructure|operator|outcome [--run ID]

queue 渲染五桶队列表(id / 标题 / 桶 / 模板状态 / 计划阻塞 / 时长),附「持有 resource 未 releasable」警示;默认只显示本会话的 run(originSession 过滤),--all 看全部,--run 指定一个。run status 打印与 CLI 相同的投影表。run create 把调用会话记为 run 的 originSession;写操作在 history 里记为 slash:。用法错误返回 usage 文本。export 尚不是 slash 命令——它随泄题闸在 M2 剩余部分落地。

导出与泄题闸

dsh-mission export RUN_ID --out DIR 写自包含 bundle -bundle/:manifest.json(冻结的状态机、数据集快照引用、逐层内容哈希、收录层清单如实标明 guarded 层、ns 完整性报告)、run.json、missions//attempt-N/{meta.json, annotations.json, artifacts/}、每个收录层的 dataset//、以及留待人工撰写的 methodology.md。已有 bundle 目录绝不覆盖。

泄题闸:收录 guarded(modelFacing: false)层必须 TTY 交互确认——逐个列出、逐项确认。非 TTY 一律拒绝(fail-closed):agent 经 Bash 调 CLI 没有 TTY,自然被闸住;任何 flag(含 --include-guarded 式)都不放行。slash 面没有确认通道,/mission export 对 guarded 层直接拒绝并指向 TTY CLI;挂载 datasets 插件时,slash 面从其元数据读层可见性(否则靠显式 --guarded 声明)。

expectedNs:run meta 声明 expectedNs 时,run status 与 export 输出每格 ns 清单——缺失如实标缺失(绝不用其他 ns 顶替),全部注解都在 expectedNs 之外的格子在报告与 manifest 里标「只有未列 ns」。每个 ns 的 writtenBy 是其当前 attempt 全部注解写入者前缀的去重集合(例如 tool:、cli、service、slash:);若某个 expected ns 只有 tool: 写入,报告只如实展示,不作质量判断。

会话 tab

会话 tab 环上的 missions 入口(web profile):五桶筛选 chip(多选)、run 范围选择器(默认本会话、全部 run、单个 run)、任务表——# / 标题 / 状态(桶)/ 模板状态 / 计划阻塞 / 时长——每个 run 段带未释放资源警示。选中行打开详情面板(attempt / 检查点 / 注解计数)与人操作:填写原因与类别后重跑(新开 attempt)、释放检查、导出 bundle。导出对话框先检查(plan),收录 guarded(modelFacing: false)层时逐层列出,逐项勾选后导出才可用——与 CLI 的 TTY 确认同一个闸,宿主侧复核。数据走 mission Typert Remote 命名空间(宿主侧 MissionRemoteService,ctx.mission 的薄适配器)。

Compatibility

  • npm release line(@deepseek-ai/dsh@0.1.2-rc.1):✅——store、状态机与 guard、lint、五桶投影、服务面、CLI、slash 命令在发布版宿主上全部可用(模型工具经伴生行 @khorsheed/dsh-mission-tool 提供,见下)。minHost 前移至 0.1.2-rc.1,旧宿主请停留在旧发布线。
  • source line(deepseek-harness master,fork 或 upstream):✅——同上(verifiedHost: 0.1.2-rc.1)。

降级 / 缺席项(与 package.json 的 dsh.compat 同步):slash 命令需要交互式 UI adapter(web/TUI)——headless profile 没有 command adapter,/mission 在那里不可用,工具、服务面、CLI 不受影响。模型工具的分组(tools: 'read' / 'none')现在是伴生行 @khorsheed/dsh-mission-tool 上的选择,不是本行的配置——按上表裁掉模型工具不是宿主能力缺失,而是挂载方的选择;服务面、CLI、slash、tab 照常。任务 tab 自隐:只有当当前会话的 preset 组合引用了 @khorsheed/dsh-mission-tool 行时它才注册,判据取自官方 pluginInventory Remote,任何读不出的路径一律 fail-open(保持可见)。「当前会话的 preset」沿父链取第一个(I5·T60 · web-eval T39 · G13):成员子会话自己没有 preset,单看它就失败开放,于是评测会话里 mission 明明该隐身,任务 tab 却出现在选手的子会话里。发布顺序有约束:引用伴生行的 pack 必须先有伴生包被发布 / 安装——行解析失败只让该 preset 组合报 broken,实例 boot 不受影响。会话 tab 是 web 面;TUI 没有 tab 机制,headless profile 只提供 Remote 数据面而没有浏览器消费者。tab 已在 0.1.0-rc.8 web profile 做 live smoke;更早发布线共享同一 gateway 约定,但未做 smoke。

Known Limitations and Deferred Work

  • 模板是 JSON 不是 YAML——提案示例用 YAML,但 v1 在依赖决策落定前不引入 YAML 依赖;两种形式描述同一文档模型。
  • retry 天然不幂等——每次带原因的调用都真实新开一个 attempt;相同 reason/category 重复调用也会再开一次。其余所有写操作幂等(相同参数重复提交 = no-op)。
  • 锁对 pid 复用是尽力而为——stale 锁在 pid 已死或锁龄超 60 秒时回收;窗口内 pid 被复用最多等到 10 秒锁超时。在预期写密度下足够;存储层可换 sqlite 而不动数据模型。
  • 每个模板恰一个初始态——mission 的起点必须无歧义;终态数量任意。
  • tab 没有提交产出按钮——提交产出需要 Remote 面不具备的产物上传管线;mission_submit(工具)与 dsh-mission submit(CLI)覆盖。导出对话框的确认是泄题闸的 web 形态:guarded 层逐项勾选才放行,宿主侧用全新 plan 复核确认清单。