Khorsheed/dsh-plugins--packages-mission ↗★ 0
@khorsheed/dsh-mission
Generic task management: runs of missions with template-declared state machines, deterministic guards, plan data (dependencies / schedules), attempts and checkpoints, append-only annotations, a releasable-state gate for irreversible resources, gated run-bundle export, and a web session tab over a Typert Remote data face 适合需要批量创建、跟踪和控制复杂任务生命周期的用户。
Install
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:Khorsheed/dsh-plugins#548eeabe331c121621b2d08deab4278ad81d6312&path:packages/missionREADME
Read the full README ↗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 个,行为与本配置项加入前完全一致 |
read | mission_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 复核确认清单。