Skip to content

dsh-tree-task-flow

Verified

dsh-tree-task-flow · v0.3.4 · MIT · Web UI

树形任务流:把多步任务组织成目标→任务→子任务三级树,节点完成时由 AI 提交结果,插件随即把该节点的执行过程从上下文里折叠掉,只留下结果。

Install

dsh plugin add dsh-tree-task-flow

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Creators

Readme

dsh-tree-task-flow · 树形任务流

给 DeepSeek Harness 的树形任务流:把一件需要多步做完的事组织成 目标 → 任务 → 子任务三级树,每个节点完成时由模型提交结果,插件随即把这个节点的 执行过程从上下文里折叠掉,只留下那条结果。

安装即启用:dsh plugin add 之后就生效,不需要再改任何配置。 不想用的时候,在 profile 里写一句 enabled: false 就能关掉,关掉后一个字节都不碰会话。

它解决什么

长任务跑到后半程,上下文里绝大多数内容是过程:读过哪些文件、试过哪些错、 执行过哪些命令。这些内容对后面没有用,但会一直占着位置——每多一轮, 喂给模型的上下文就更长一分,而其中真正需要留住的只有寥寥几句。

真正需要留给后面的只有一句话:这一步做完了,它产出了什么。

所以这里的分工是:

内容 去向
过程 读文件、试错、命令输出 节点完成时整段折叠掉
结果 模型在 tree_task_done 里提交的 result 留下来,供后续节点引用

上下文怎么长

[节点 0]  系统提示词(身份 / 工具 schema / 工具说明)   ← 不随计划变化
[用户消息]
[目标级汇总]                          ← 换目标时逐条累积
  [任务级汇总]                        ← 换任务时逐条累积
    [子任务汇总] [子任务汇总] …       ← 同级逐条累积,互不卷走
    [当前子任务的执行过程]            ← 下一次折叠的对象

每一层有一个游标,记着"这一层总结到哪儿了"。节点完成时,插件把 游标之后到末尾这一整段替换成一条汇总消息:

子任务完成 → 替换 [上个游标之后, 末尾] → 本子任务的汇总
任务完成   → 替换 [进任务时的边界之后, 末尾] → 本任务的汇总
目标完成   → 替换 [进目标时的边界之后, 末尾] → 目标的汇总

游标自己留在替换范围之外,所以同级上一条汇总消息不会被这一次折叠卷走。 这就是"同级完成不互相屏蔽"的来由。

游标失效时(被压缩遮掉、或它之后本来就什么都没有)折叠降级为纯追加: 过程收不起来,但提交的结果照样进上下文。收不起来可以,丢掉结果不行。

折叠发生在 agent/pre-step——上一轮已结束、下一个请求还没构造的那一刻。 工具执行中途不能折叠,那时 surface 还在增长,替换会把自己卷进去。

检查点

子任务全部结束,不会让父节点自动完成。 那一刻树进入检查点:

检查点:任务「重构解析器」(t-3) 的子节点已全部结束。
此刻轮到你的判断:用 tree_task_plan 继续给 t-3 拆子节点,
或者用 tree_task_done 提交这个任务的结果并完成它。

检查点只是提示,不是闸门。 它出现在 tree_task_done 的回执、树上和汇总消息里, 但插件不会拦住任何工具——"这一层到底做完了没有"由模型判断,插件既不替它定死, 也不靠拒绝别的工具把它逼到墙角。

六个工具

工具 用途
tree_task_status 查看整棵树、各节点状态与已提交的结果,以及现在该推进哪个节点
tree_task_create 建立计划:一个目标 + 若干任务
tree_task_plan 给一个节点追加子节点:给目标追加任务,给任务追加子任务
tree_task_done 完成一个节点并提交它的结果,result 必填
tree_task_update 修改一个节点的标题或说明
tree_task_drop 丢弃一个节点;丢弃任务会连同它的子任务一起丢弃

层级固定三级,子任务是最低一级,不能再往下拆。

安装

需要 Node.js 22.19 以上,以及能运行 dsh。

从 npm 安装:

dsh plugin --profile web add dsh-tree-task-flow

从源码目录安装(装成 link:,改完源码重启即生效)。把仓库克隆到任意目录, 然后指向那个目录:

dsh plugin --profile web add <插件目录>

dsh plugin add 会把包加进 profile 的 dsh.profile.bundles,而本包自带的 cordis.patch.yml(由 package.json 的 dsh.bundle.patch 指定)会顺势把 task-tree 这一行插进插件列表——装上即启用,不需要再改任何配置。装完可以核对:

dsh --profile web --dump-config          # 输出里应出现 task-tree 这一行,且不带 config

必须重启 dsh web 才生效——运行中的会话不追溯,新工具要在新会话里才会出现。

关闭

不想要了,除了 dsh plugin remove 卸掉,也可以只把它关掉。在 $DSH_HOME/profiles/<名字>/cordis.patch.yml 里写:

- id: task-tree
  config:
    enabled: false

然后重启 dsh web。仓库里的 config.example.yml 有可以直接抄的完整版本。

⚠️ profile 这一层按行覆盖时 config 是整体替换,不是逐字段深合并—— 覆盖时要把想保留的字段一起写全,没写的会走插件默认值(见下面的配置表)。

关掉之后插件一个字节都不碰会话:不注册工具、不加提示词段落、不折叠、不续行、 不挂暂停闸门。只留一条 /task-tree 命令,让你确认它是被关掉的以及怎么打开。

界面

输入框上方的「树形任务流」条目(和内置的待办、目标条目并排,宽度与消息正文对齐)。 外观照内置条目做——用的是同一套 UI 基座(@deepseek-ai/dsh-client-ui-primitives 的图标、Tooltip 与 CSS 变量),所以它看起来就是内置的一部分:

  • 折叠时是目标条那样的 36px 单行条:左边清单图标,中间「阶段标签 + 目标标题 + 进度」, 右边一排圆形图标按钮——暂停/继续、清空计划、展开。 阶段标签四种:进行中 / 已暂停 / 检查点 / 已全部完成。
  • 展开后是待办清单那样的卡片:三级树之间有竖线与拐角连线,层级一眼看得出来; 同一层的节点之间、节点内部的各个内容块之间,各有一条分界线。 默认只摊开正在走的那条路径——目标一路展开到当前子任务,其余节点收起, 一打开就能看见正在跑的那一个,不用在一堆已经收尾的节点里翻;检查点所在的节点 也一样默认摊开。想细看哪个就点它行尾的箭头,逐层摊开或收起。 当前节点的虚线圈会匀速转,已完成打勾、丢弃的划掉。
  • 节点标题不缩略:有多长写多长,多了自动换行。
  • 提交的结果默认只占一行,点一下摊开看全文。
  • 节点上可以直接完成、丢弃,不用敲命令。
  • 数据每 4 秒自动拉一次,模型在后台改计划时界面会自己跟上。

没有计划的会话一个像素都不占——读不到计划就返回 null,不会平白挤掉内置的 待办与目标条目。

开关不在界面上,在 profile 的 cordis.patch.yml 里(见上面的「关闭」一节)。

命令

/task-tree 不经过模型,直接执行:

命令 作用
/task-tree 查看本会话的任务树与检查点状态
/task-tree done <id> <结果> 提交某个节点的结果并完成它
/task-tree drop <id> 丢弃某个节点
/task-tree pause 暂停:正在跑的工具跑完就停住,不再往下推进
/task-tree resume 继续:从停住的地方接着跑,不往会话里插消息
/task-tree reset 清掉本会话的计划树

配置

默认值全在 lib/index.js 的 DEFAULT_CONFIG 里:

字段 默认 说明
enabled true 总开关;装上即启用,写 false 关掉
autoContinue false 模型停下时是否自动续行
maxAutoRounds 5 连续自动续行的轮次上限
promptOrder 1800 工具说明段落的排序
rootDir null 状态目录,默认 $DSH_HOME/dsh-task-tree

暂停与继续

界面上的暂停按钮(或 /task-tree pause)是真暂停,不是"关掉自动续行":

  • 正在执行的那个工具会正常跑完,不会被中途掐断;
  • 它跑完之后,会话就静静停在原地——不再发起模型请求,也不再往下走一步;
  • 全程不往会话里插任何消息,上下文干干净净。

继续(界面按钮或 /task-tree resume)就是从这个岔口放行:模型从它原本要请求的地方 接着请求,同样零新增消息。任务树的进度停在哪儿,就从哪儿接着跑。

闸门开在 agent 的 agent/pre-step 上——一个 step 走完、工具结果都已落盘、模型还没 发起下一次请求的那一刻。这是唯一同时满足"不打断工具"和"不留痕迹"的位置:再晚一点 的 agent/turn-stopping 一旦放行,就得靠 steer() 推一条消息才能续上,那就留下痕迹了。

闸门只拦自动推进,不拦真人发言。 暂停期间你自己发一条消息,说明你要它跑, 插件会直接放行并解除暂停,不会把你堵在门外。

暂停期间人按界面上的停止(中断本轮)也不会卡住:闸门盯着本轮的取消信号, 信号一 abort 就立刻放行。

自动续行的三道闸门

autoContinue 默认关闭,开着也只是兜底:tree_task_done 的返回文本里已经带了 "请继续执行下一个子任务",模型通常会在同一轮里接着做。这里兜的是模型仍然停下来的情况。

开启后仍有三道闸门,缺一不可:

  1. 默认关——autoContinue: false 时不注册续行逻辑。
  2. 中断即停——用户按下停止(agent/turn-stopping 且 signal.aborted), 这个会话就被标记为不再自动续行,直到真人再发一条消息。
  3. 轮次上限——连续续行达到 maxAutoRounds 就停;只有真人发的消息才重置配额。

队列里还有排队输入时也不抢,让用户先说话。

另外,暂停会一并压住它:/task-tree pause 之后,即使模型自己停了下来, 兜底也不会把它重新拉起来;/task-tree resume 再放开。

状态文件

全部在 $DSH_HOME/dsh-task-tree/ 下,以 sessionId 为键:

文件 内容
plans/<sessionId>.json 计划树(每个会话一棵)
surfaces/<sessionId>.json 三层的折叠游标 { key, cursor }

目录名沿用插件改名前的标识,为的是不让既有的计划文件失联;插件名与它无关。

计划属于建立它的那个会话。放到会话无关的位置,任何一个会话的计划都会被 所有会话读到——那等于把不相干的节点指派塞进别人的上下文。

写入一律走「临时文件 + rename」,进程崩在写入中途也不会留下半截 JSON。 插件绝不直接写会话日志,会话里的一切都走 session.append()。

你会观察到的行为

  1. 完成必须写 result。 节点的执行过程会被折叠掉,result 是它唯一的出口; 空 result 会被拒绝。所以要写清"产出了什么",写成能独立看懂的样子—— 过程会丢,别把过程写进去。

  2. 折叠是尽力而为的。 游标被压缩遮掉时收不起来,但结果一定进上下文。 面板上看到某段过程还在,通常是这个原因。

  3. 模型看到的工具说明不随计划变化,也不复述工具清单。 那一段只讲什么时候用 这组工具、完成时守什么规矩;每个工具的用法由它自己的 description 承担。 要了解计划就让模型调 tree_task_status。

  4. 每次折叠,界面上的"系统提示词"卡片会重新渲染一次,但提示词一个字都没变。 DSH 把"上下文被替换过"记成一次请求序列的边界,界面在 reason 不是 change 的请求上都会渲染这张卡片,而复用的是同一个锚点,所以卡片不会变多,位置也不动。 这是 DSH 的固有语义,插件只能把替换次数压到最低——一次折叠只替换真正变化的 那一层,典型的完成一个节点只产生一次替换。

  5. 改需求用「丢弃 + 追加」,不要重建整棵树。 丢掉的节点留在树里可查, 它和完成的节点会合进同一条汇总。

  6. 暂停不留痕迹。 暂停后模型看到的是"没有下一条请求",看不到任何"你被暂停了" 之类的提示;继续之后也不会有"继续"消息进上下文。想让模型知道中间发生过什么, 得自己发消息说。

不做的事

不做 原因
资料 / 文件快照 / 记忆 上下文里值得留下的只有结果,其余靠模型自己重读
文件回退 / 撤销 改需求用「丢弃 + 追加」
多目标并行(多个根) 单活动计划,数据结构已预留扩展
把计划内容注入提示词 计划状态由 tree_task_status 现取,提示词里只放不随计划变化的说明

自检

六个自检脚本都不 import 任何 @deepseek-ai/* 运行时包——插件以 link: 方式安装时, 从插件目录往上找不到宿主作用域的包,一旦 import 就根本跑不起来。 verify-client.mjs 因此自带一套最小的 window / document / React 替身, 真去物化 client.js、真调一次 apply、真渲染一遍条目。

node verify-entry.mjs     # 入口装配:默认(安装即启用)/ 显式关闭两条路径各注册了什么
node verify-tools.mjs     # 工具层:schema、result 必填、三级约束
node verify-region.mjs    # 折叠行为:游标推进、降级追加、检查点提示
node verify-plan.mjs      # 计划树纯逻辑:状态流转、自动完成、丢弃级联
node verify-pause.mjs     # 暂停闸门:挂起、放行、取消信号、真人发言、会话隔离
node verify-client.mjs    # 客户端条目:挂载点、样式注入、文案键集、四种渲染态

verify-region.mjs 用假 session 忠实复刻了 DSH surface 的三条硬规则 (替换保持位置、sourceEventSeqs 必须覆盖全部遮蔽节点、节点 0 重写约束), 否则测出来的东西不算数。

卸载

dsh plugin --profile web remove dsh-tree-task-flow

状态目录不会自动清理,需要的话手动删 $DSH_HOME/dsh-task-tree。

设计文档

  • docs/02-DSH插件开发方案.md —— 完整设计:上下文组成、游标、折叠、检查点
  • docs/01-AIDesktop联动设计整理.md —— 本插件最初的设计来源

许可

MIT