dsh-taskfold
Đã xác minhdsh-taskfold · v0.38.2 · MIT
Keeps long coding-agent sessions lean: wrap a stretch of work in a named task, and when it ends, fold that whole span into one short titled summary. The conversation stays readable, context costs stay low, and any fold's original content can be read back
Cài đặt
dsh plugin add dsh-taskfold Xác nhận layer đã áp bằng dsh --profile default --dump-config — xem hướng dẫn cài plugin.
Mã nguồn
Thẻ
Tác giả
Readme
给你的编程智能体,近乎无限的上下文
让长时间的 AI 编程会话保持快速、便宜、可读:完成的工作被折叠成一条短摘要,完整原始内容随时一条命令取回。
面向 DeepSeek Harness(DSH)。
当前支持的最新 dsh 版本:
0.2.0-rc.2。 dsh 的 rc 版本在本分支(master)支持;dsh 的 alpha 版本在alpha分支支持。
快速开始
在 dsh Desktop 里安装(推荐)
- 打开 dsh Desktop,在侧边栏进入 插件 页。
- 点右上角的 添加插件。
- 在 包名或地址 一栏把版本号写全:
[email protected]——若已有更新版本,以 npm 版本列表 为准。安装框自己调 pnpm,不用你手工装依赖:- 要钉版本,别只写裸包名。 dsh Desktop 自带的 pnpm 11 会执行
minimumReleaseAge供应链策略,pnpm add dsh-taskfold只会解析到"已过策略期"的最新版本——2026-10-08 那次解析到的是根本装不上的 0.38.0,而对话框上的版本号却显示 0.38.1。显式写版本会被接受,pnpm 还会把它记进 profile 的minimumReleaseAgeExclude。(0.38.0 千万别钉:报ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND,npm 上它已被标记为 deprecated。) - 完全绕过 npm:直接粘最新 Release 里预构建的
.tgz直链——tarball 不受发布时长策略限制; - 跟分支走:
github:yindf/taskfold#master(rc 通道)/github:yindf/taskfold#alpha(alpha 通道),要求本机能直连 GitHub; - 本机检出的绝对路径同样可用。
- 要钉版本,别只写裸包名。 dsh Desktop 自带的 pnpm 11 会执行
- 安装源 保持「默认安装源」;中国大陆网络若拉取失败,改选「中国大陆镜像源」再试。
- 点 安装,等它显示「已安装,下次启动后加载。」,再点 立即启用。
- 重启 dsh Desktop——插件组合只在启动时求值。重启后回到 插件 页,应看到 Taskfold 卡片写着
包含的组件 · 共 2 个 · 2 运行中(taskfold与taskfold-client两行);折叠下限与任务栏开关就在这张卡片里(见设置)。 - 升级请先 卸载 再装新版:界面明确说明暂不支持自动更新;卸载后它提供的功能即消失。
这个安装框做的和下面的命令行是同一件事:把依赖与 bundle 条目写进当前 profile(dsh Desktop 用 desktop),此外不需要任何手工配置。
用命令行安装
dsh plugin --profile <profile 名> add 就是上面对话框的命令行等价物。desktop 是 dsh Desktop 的 profile,web 是 DSH Web GUI 的——按你实际跑的那个改:
# npm 上的最新发布——要等它过了 pnpm 的发布时长策略窗口才会解析到
dsh plugin --profile desktop add dsh-taskfold
# 钉住某个版本(@ 后面填你要的版本;0.38.1 起才装得上,0.38.0 装不上)
dsh plugin --profile desktop add dsh-taskfold@<版本>
# 跟某个通道的分支(引号不能省:# 在 sh 里是注释)
dsh plugin --profile web add "github:yindf/taskfold#master"
dsh plugin --profile web add "github:yindf/taskfold#alpha"
无论走界面还是命令行,装完都要重启 dsh。
通道、npm 包与支持范围
npm 上的 dsh-taskfold 是预构建包——免去 dsh 的 allowBuilds 构建授权,0.37.6 起由发布流程随通道分支同步发布。裸包名取的是 npm 的 latest,也就是最后发布的那条通道;要确定性就用 @版本 钉住,或直接填分支的 Git 地址。
master 分支承载 rc 通道、alpha 分支承载 alpha 通道:本分支记录 rc 通道的支持范围(见支持的 dsh 版本),alpha 通道的记录在 alpha 分支的 README。
重启 dsh——该 profile 下的每个会话都拥有这些工具。之后智能体用命名任务包住自己的工作:
task_begin("修复登录 bug") … 干活 … task_end("修复登录 bug")
整段来回就此折叠成一条带标题的摘要,fold_recall 随时能读回原始内容。
它解决什么问题
长会话会被自己的历史淹没:每个请求都在重发几小时前就完成的工作——旧的工具输出、调试日志、失败的尝试。成本越滚越高,模型注意力被稀释,上下文窗口迟早被塞满。
taskfold 用“好笔记本”的方式解决:干活前,智能体先用 task_begin("修复登录 bug") 开一个任务;做完后 task_end 关闭任务,同时把整段来回替换成一条带标题的短摘要:
之前: [800 条原始调试消息……]
之后: 「修复登录 bug」— 摘要:试了什么、为什么失败、改了什么、
用户拍板了什么。(约一屏)
会话保持可读,每个请求都更便宜,模型带走的是经验而不是流水账。
什么都不丢。 每次折叠都会把原始消息原样存成文件,fold_recall({ fold: N }) 随时能重新生成。先折叠、后查阅——像合上一本随时能翻开的书记。
与 dsh 内置压缩的关系
同一个目标,不同的时机——两者可以叠加。
- dsh 内置压缩是自动的、由压力驱动的。 它在窗口快满时触发,按 token 压力选出一段区间替换成摘要;原始事件仍留在会话日志里,只是被 shadow 掉,而不是删除。
- taskfold 是显式的、按任务划分的。 每完成一个任务就顺手折叠一次,摘要是趁那一段还在上下文里时写下的——天然准确——而且带标题,会话始终可导航。
- 因为你折叠得早,窗口很少被塞满。 下面实测会话的峰值是 20.7% 而不是 59.5%,于是压力压缩要么更晚触发、要么根本不触发;真触发时,需要总结的东西也更少。
- 每一次折叠都可寻址。
fold_recall({ fold: N })取回的是原始消息,不是二手摘要。
原理(通俗版)
- 命名任务。 智能体开工前开任务、完工后关任务。开启状态跨重启不丢;关闭按嵌套顺序(内层先关);关闭失败不会破坏任何状态——重试即可。
- 折叠 = 关闭 + 总结,一次调用完成。 摘要在原始内容还在上下文里时一次性写好,所以准确——不是“摘要的摘要”。
- 摘要保留要紧的东西。 总结指令明确要求保留用户的关键决策与反馈(措辞重要处原文照录)、踩过的坑和为什么失败、改了什么、最终结果。
- 温和护栏。 智能体忘记纪律时,上下文里会出现一条简短提示。提示是事件而非状态:只在条件出现或措辞变化时发布一条;条件解除后什么都不发(模型已经照做了,不需要再被告知);没有包装标签、没有取代声明、也没有过期通知——流程健康时零噪音。
- 对缓存友好。 折叠只改写历史中段;稳定前缀(系统提示词、工具、更早的上下文)保持缓存命中。
省了多少(实测)
一次真实会话——411 个模型步、26 次折叠——数字直接读自 harness 自己的用量记录:
| 不折叠 | 用 taskfold | |
|---|---|---|
| 提示 token 总量 | 142,654,308 | 52,127,098(−63.5%) |
| 单次请求最大体积 | 594,909 | 206,896(−65%) |
| 峰值上下文窗口占用 | 59.5% | 20.7% |
机制:折叠把 441,100 token 的已完成工作移出表层。这些历史本来会在之后每个请求里被重发一遍,累计下来就是 90,527,210 token 从未发出。生成那 26 条摘要本身花了 3,550,270 token(相当于节省量的 3.9%,且其中大部分是缓存读取);单次折叠最多一次性移走 40,422 token。
这些被省下的 token 大多是缓存读取而非全新输入——单价更低,但依然计费、依然占窗口。会话再长一些,这就成了「还在窗口内」和「已经塞满」的区别。
一次会话、一种任务形态——你的数字会不同;关键是机制:完成的工作离开表层,稳定前缀持续命中,模型带走经验而不是流水账。
它添加了什么
四个智能体工具(加上上述提醒机制):
| 工具 | 一句话 |
|---|---|
task_begin({ name }) |
开一个命名任务。 |
task_end({ name }) |
关闭它,并把整段折叠成一条带标题的摘要。 |
list_folds |
列出全部折叠(编号、大小、标题)。 |
fold_recall({ fold }) |
按需取回任意折叠的原始内容。 |
在 Web GUI 中,当前打开的 task 栈还会以常驻 dock 显示在输入框上方(和 todo 面板一样):外层任务在前、最内层高亮,附带 folding/pending 计数——直接读会话的 taskMarks 投影,不追加任何事件。


设置
折叠有一个用户可配置的下限,在宿主插件页的 “Taskfold” 卡片中配置(宿主服务该命名空间期间由浏览器端注册)——不用环境变量,也无需重启:
minSpanTokens(默认2000)——任务关闭后,其折叠区间携带的估算 token 数需要达到的最小值,才值得发起一次摘要调用。token 按区间消息文本估算(区分中日韩字符的启发式,速率常数已按实测shadowedTokenCount校准,估算值整体偏低约 7%,方向保守),在任何模型调用之前即可算出。低于下限的区间不折叠直接关闭,且判定在关闭当时做出、记录在Task ended结果文本里(0.37.6):该区间根本不进入归档队列,跳过由构造保证永久——不存在可被触发的重开路径,因为回溯折叠已落定的区间要改写表面、使其后方的 provider 前缀缓存整体失效,这笔代价任何小折叠都省不回来。关闭消息若还携带其他工具调用(其结果在task_end执行时尚未落地),则回退为 drain 侧的内存落定,重启后按当时下限重测。0折叠一切;无法计量的区间始终折叠。非法值(非整数、负数)回退到默认值,绝不抛错。修改由表单 schema 校验、持久化到当前 profile 配置,并即时生效——下一次关闭(或兜底落定)即按新下限判定。默认值及其推导见 docs/fold-floor.md——对最近两周会话日志的实测表明,摘要调用的固定开销使 ~1000 token 以下的区间稳亏,默认 2000 保留了 2 倍安全边际。showTaskBar(默认true)——同一张卡片上的开关,控制是否在对话输入框旁显示任务栏。隐藏它不影响折叠行为;命名空间未被服务或设置服务缺失时始终显示。
本地化
所有用户可见界面均提供英文与简体中文。设置卡片文案经宿主 locale 服务解析(字典 settings.taskfold)。任务栏 dock 的注册刻意不声明 locale 依赖——locale 服务可用时由 bundle 接线绑定读取器(字典 ui.taskfold),缺失时打印内置英文,因此在任何部署上都能渲染。包同时导出 locale/en.json 与 locale/zh.json(插件页页头与 npm 简介),并为每个挂载组件提供一份行级 locale——宿主行用 plugins/locale/*.json(经它挂载的子路径 dsh-taskfold/plugins 解析)、客户端行用 client/locale/*.json(经它自己的裸包名 dsh-taskfold-client 解析)——插件页因此按当前语言显示每一行的标题与一行简介,而不再回退到 package.json 的英文文案。两行的标题都刻意不同于行 id 与模块名:client.js 只在标题与行 id 不同时才打印 id、与模块名不同时才打印模块名,于是每一行都渲染出同样的四行——标题、简介、行 id、模块名——与官方 bundle 的行形态一致。客户端包的 package.json 依旧不写简介,因此行内简介只会来自它那份 locale。包自身的简介继续服务于插件页页头与 npm 页面。
其他安装方式
每个 Release 都附带预构建的 dsh-taskfold-<版本>.tgz。插件市场会优先提供该资产(或 npm 包)而不是源码构建命令,同时也免去 dsh 的 allowBuilds 构建授权——见最新 Release。
支持的 dsh 版本
- rc 通道 —— 支持到
0.2.0-rc.2(2026-09-30 实测——同 minor 锁步升级:dsh monorepo 整体从 0.2.0-rc.1 移到 0.2.0-rc.2,包集合零变化(无增无删),因此本轮是复验而非迁移。兼容门以真实 0.2.0-rc.2 的evaluatePluginCompatibility复核:现有^0.2.0-rc.1peer 下限本就覆盖 rc.1 直至 0.2.0 正式版——无需改动插件,v0.37.1 原样加载。宿主 API 形状探针(对真实 0.2.0-rc.2 包):BasicCompactionEngine默认导出且原型上有compactRegion(dsh-compaction-basic)、BlockAssembler导出(dsh-llm)、schemastery CJS 入口带 Config schema 链式的完整 z-object 面。客户端契约扫描:conversation.input.dock、plugins.bundle.config、configFormswhileServed、window.__ModuleLoader__、SettingsFormModel、settingsNumberField全部在位。端到端探针宿主(真实 0.2.0-rc.2 构建 + 本树链接):bundle 过门加载——日志里唯一的禁用行是 profile 里无关的旧 auto-review——且settings/describe正常服务cmpct-region,值为{"minSpanTokens":2000,"showTaskBar":true}、applies: live。离线套件——14 个套件、208 个测试、0 失败。被取代的 0.2.0-rc.1 记录(旧范围被拒、形状探针、探针宿主 describe、peer 提升本身)维持 v0.37.1 发布时的记录。dist-tag 备注:0.2.0-rc.2现在同时挂在latest与next上,裸npx @deepseek-ai/dsh web即可拿到。dsh、dsh-compaction-basic、dsh-llm三者版本锁步发布,一个数字覆盖全部耦合面。 - alpha 通道 —— 请用
#alpha安装(dsh plugin --profile web add "github:yindf/taskfold#alpha",即 alpha 分支);alpha 构建的支持版本记录在 alpha 分支的 README。 - 边界:下界已强制,上界未测试。 插件把宿主耦合面写进
peerDependencies(@deepseek-ai/dsh、@deepseek-ai/dsh-compaction-basic、@deepseek-ai/dsh-llm,均为^0.2.0-rc.1):dsh 0.2.0 宿主在安装与启动/重组合时检查这些范围,不兼容的宿主将得到incompatible-version判定、bundle 被跳过,而不是带着不兼容加载——0.1.7-rc.* 宿主请安装 v0.37.0(其^0.1.7-rc.1下限与之匹配)。早于该检查机制的宿主没有任何协商——在那些宿主上,折叠会降级(任务照常关闭、不折叠),不会损坏数据。每次 dsh 升级后,请复核本节并按实测结果更新。 - 可选钩子:
agent/turn-stopping—— 0.26.0 起归档排干还会在回合结束时运行,让回合末交付的折叠赶在 provider 前缀缓存还热时执行。没有该钩子的宿主保持原来的纯 pre-step 语义(折叠照常发生,只是晚一个回合);注册语句整体包裹,钩子缺失不会破坏apply()。
维护者须知
- 目录:
plugins/(一个挂载宿主行taskfold.mjs,以包子路径dsh-taskfold/plugins挂载——bundle patch 只声明一个宿主组件,所以插件页显示两个:该行与以自身组件包名dsh-taskfold-client挂载的客户端行;compact-stats.mjs是它 import 的普通模块,不是行;以及它们共享的纯模块events.mjs、task-marks.mjs、fold-instruction.mjs、fold-engine.mjs、fold-drain.mjs、fold-settings.mjs、lifecycle-nudges.mjs、lifecycle-injection.mjs、span-preview.mjs,以及浏览器端:task-stack-ui.mjs——dock 的唯一事实源——与fold-settings-ui.mjs——插件页设置卡,均由scripts/build-client.mjs生成到client/taskfold-client.mjs)、client/(嵌套的dsh-taskfold-client子包——浏览器 bundle 的唯一属主行;一个客户端包只能有一个属主 Loader 行,而宿主端已占用根包的行;它作为独立 npm 包发布,根package.json以注册表版本范围把它声明进dependencies,于是 dsh-app-boot 的依赖闭包会发布该名字,客户端行以裸包名挂载而非file:///路径,且消费方的 pnpm 能真正装上它——file:/link:这类路径说明符是相对安装者的 workspace 根解析、而非相对本包解析,这正是 0.38.0 完全装不上的原因;行标题由client/locale/*.json提供)、scripts/release.mjs、scripts/verify-cache.mjs与scripts/build-client.mjs、test/(npm test)、assets/(README banner、仓库设置里上传的社交预览图,以及screenshots.json列出的商店截图)、docs/(docs/README.md索引,以及docs/design/设计笔记与docs/adr/决策记录——有意不随 npm 包发布)、CHANGELOG.md。 - 发版:
node scripts/release.mjs draft→ 审阅 CHANGELOG 条目 →node scripts/release.mjs release(CHANGELOG 是版本唯一事实源)。release(及其 PENDING 续跑)在NPM_TOKEN环境变量存在时还会把版本发布到 npm——须为 Automation 类型令牌(开了 2FA 的账号会在发布时拒绝 granular 令牌的 OTP);该步骤幂等(注册表上已有的版本直接跳过),失败也绝不回滚 git 侧的发布;令牌取自进程环境,Windows 上还会回落到用户级变量。若该步骤被跳过或失败,用node scripts/release.mjs npm [--version X.Y.Z]单独补发——四道守卫(package.json版本、本地v<版本>tag、干净工作区、工作区与 tag 内容一致)全部满足才肯发。发布的是两个包,且客户端包必须先发:根包 manifest 里写着dsh-taskfold-client的版本号,若根包先于客户端包发布,在客户端包落地前所有消费者都会 404(两个包各自探测、各自发布,重跑只会补上缺的那一半)。发布前assertClientDependencyResolvable会拒绝任何非注册表版本范围形式的客户端依赖——file:/link:/workspace:这类说明符相对消费者的 workspace 根解析,会让每一次安装都失败,这正是 0.38.0 发布出去的样子。通道分支(0.34.6 起):master只承载 rc 通道发版——它停留在最新的已验证 rc 版本;alpha 通道发版在alpha分支上进行,其提交与 tag 承载 alpha 验证过的工作(在alpha分支上跑 draft/release;脚本推送当前分支与 tag)。若本次发版改变了支持的 dsh 版本范围,发版前先更新两份 README(README.md + README.zh.md)的“支持的 dsh 版本”一节——release 脚本会提醒。每个分支只记录本分支构建的实测:本通道一条最新验证版本(被取代的条目删掉),另一通道只放一条指向对方分支 README 的链接——绝不抄版本号。 - 折叠缓存校验是流程的一部分。 每次 dsh 升级后——以及任何触及折叠信封的发版前——对一份 live 会话日志跑
node scripts/verify-cache.mjs --since-restart,并把数字记进 CHANGELOG 条目。若某次折叠的摘要调用重新付费了它的 span——判据是uncached − span > --tail-budget(tail 为正)——脚本以非零码退出,这正是前缀信封不再匹配宿主摘要输入的 signature。离线测试只能钉住结构前提(只有一个 system 消息、严格前缀);真实缓存命中只能由 live 日志给出。 - 设计决策与历史见
CHANGELOG.md,以及仓库内的docs/(索引见docs/README.md;docs/design/设计笔记与docs/adr/决策记录随仓库走,不随 npm 包发布)。
如果它帮你省下了 token
点一个 star 能让更多 dsh 用户找到它——在这个生态里,插件就是靠这个被发现的。你自己会话里的实测数字,欢迎贴到 Discussions。
许可
MIT。基于 DeepSeek Harness(@deepseek-ai/*,MIT)公开包开发。