用命名任务包住工作过程;任务结束后,在下一个步骤边界把整段消息折叠成一段摘要,原文可随时还原。
安装
# npm 包(预构建)
dsh plugin --profile web add dsh-taskfold
# Release 预构建包
dsh plugin --profile web add "https://github.com/yindf/taskfold/releases/download/v0.31.1/dsh-taskfold-0.31.1.tgz"
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:yindf/taskfold
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
让长时间的 AI 编程会话保持快速、便宜、可读:完成的工作被折叠成一条短摘要,完整原始内容随时一条命令取回。
面向 DeepSeek Harness(DSH)。
当前支持的最新 dsh 版本:
0.2.1-rc.3。 dsh 的 rc 版本在本分支(master)支持;dsh 的 alpha 版本在alpha分支支持。
快速开始
在 dsh Desktop 里安装(推荐)
- 打开 dsh Desktop,在侧边栏进入 插件 页。
- 点右上角的 添加插件。
- 在 包名或地址 一栏填
dsh-taskfold——安装框自己调 pnpm,不用你手工装依赖:- 想在新版本一发布就装上?钉住它——
dsh-taskfold@<版本>(版本号从 npm 版本列表 取)。dsh Desktop 自带的 pnpm 11 会让发布不满 24 小时的版本对裸包名不可见,显式写版本即可绕过,pnpm 还会把它记进 profile 的minimumReleaseAgeExclude。0.37.7 与 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-taskfold.frozen-*.bak快照,升级时整个替换)。
- 想在新版本一发布就装上?钉住它——
- 安装源 保持「默认安装源」;中国大陆网络若拉取失败,改选「中国大陆镜像源」再试。
- 点 安装,等它显示「已安装,下次启动后加载。」,再点 立即启用。
- 重启 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 上的最新发布
dsh plugin --profile desktop add dsh-taskfold
# 钉住某个版本——发布不满 24 小时时必须钉;0.37.7 / 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 面板一样)——默认隐藏,在插件页用 showTaskBar 打开:外层任务在前、最内层高亮,附带 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(默认false)——同一张卡片上的开关,控制是否在对话输入框旁显示任务栏。默认隐藏:新 profile 不打开开关就没有这条 banner;显示或隐藏都不影响折叠行为。唯一的兜底可见是有意保留的——命名空间未被服务或设置服务缺失时仍然显示,因为 dock 的注册本身不依赖设置服务。
本地化
所有用户可见界面均提供英文与简体中文。设置卡片文案经宿主 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.1-rc.3(2026-10-26 实测——这是一次补丁级前进:monorepo 整体 0.2.1-rc.2 → 0.2.1-rc.3,@deepseek-ai/*恰好新增一个包,306 → 307(新增dsh-client-ui-entry-customization,无移除),因此耦合面是重测而非沿用旧结论。对插件触及的 21 个包逐文件 diff:服务端侧——dsh-app-boot、dsh-compaction-basic、dsh-llm、dsh-tools、dsh-system-prompt、dsh-session-projection、dsh-token-meter、dsh-agent-loop、dsh-session、dsh-session-persistence——只改了package.json的版本字段;真实代码改动集中在客户端侧(dsh-client-ui-primitives、dsh-client-ui-conversation、dsh-client-modules、dsh-client-ui-plugin-manager),外加重建的dsh-web-frontendshell(index-CN7NIWNw.js→index-BXV-oxvX.js)与dsh-web-app/cordis.patch.yml。兼容门以真实evaluatePluginCompatibility(@deepseek-ai/dsh-app-boot@0.2.1-rc.3,getDshRuntimeVersion()→0.2.1-rc.3)复核:现有^0.2.0-rc.1peer 下限通过——门自身的satisfies()以includePrerelease: true调 semver,0.2.x 预发布版只要落在 caret 的<0.3.0-0上界内就被接受,并不要求同 minor 下限——无需改动插件,v0.39.0 原样加载;三组反向对照(三处 peer 全部、单独一处改成^0.1.7-rc.1,以及用 0.1.7-rc.1 运行时判真实清单)均被拒并返回 unmet-requirements 对象,证明这个"通过"不是空转。宿主 API 形状探针(对真实 0.2.1-rc.3 包)——52/52:BasicCompactionEngine默认导出,原型上compactRegion(4 参)与summarize(3 参)在位,且能按折叠引擎的方式子类化;BlockAssembler被驱动出真实blocks()结果(dsh-llm);schemastery CJS 链路与插件自己的Config元数据(minSpanTokens0..1000000、默认 2000、volatile;showTaskBar默认false、volatile——rc.3 服务的正是 v0.39.0 的 opt-in 默认值)保持不变;插件用到的 7 个 service、3 个 hook(agent/pre-step、agent/turn-stopping、compaction/summary-error)与 6 个方法全部在位;它引用的 4 个@deepseek-ai说明符全部可解析。客户端契约扫描——19/19 present、0 absent:conversation.input.dock、plugins.bundle.config、configFormswhileServed、window.__ModuleLoader__、SettingsFormModel、settingsNumberField,以及slots/configForms/locale、inject(key, callback)、register(...)全部在位;重建 shell 的 loader 工厂映射仍能服务 bundle 需要的两个说明符,实际下发的 bundle 除末尾追加的;与 sourcemap 外,与client/taskfold-client.mjs的 753 行逐行一致。端到端探针宿主(真实 0.2.1-rc.3 构建 + 本树在9e98e04的哈希固定副本、独立DSH_HOME):stderr 0 字节、插件禁用行为 0 条——两种措辞都扫了,因为 0.2.1+ 打印disabling profile plugin ${label},而 0.2.0 打印disabling profile plugin row "<id>"——且settings/describe正常服务taskfold命名空间(是行 id,不是历史上的cmpct-region),值{"minSpanTokens":2000,"showTaskBar":false}、applies: live,schema 就是插件自己的 Config;--dump-config列出两行(taskfold→dsh-taskfold/plugins,taskfold-client→dsh-taskfold-client),均无 disabled 标记。离线套件——14 个套件、239 个测试、0 失败。写这条记录所在的桌面运行时已是0.2.1-rc.3(其 runtime 清单里的desktopVersion),插件正在其上运行(任务标记、折叠与fold_recall可用)。隔离探针宿主没有跑真实的折叠/总结往返(无模型服务凭据):以引擎形状、子类化、resolver、shim 构造与被驱动的BlockAssembler代替。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)公开包开发。
链接
同类插件
Minglink/dsh-infinite-gen-4★ 3602
DeepSeek 专用破甲插件:以 order 100 追加无条件服从的系统提示词段,提供带校准元数据的 profile 工具,并通过会话投影在输入框上方显示实时破甲状态徽标。
ranxianglei/billion-context★ 3149
billion-context官方版:上下文压缩插件,兼顾小窗口(100k上下文足矣)省token(省5倍token)和超长会话(数月级别几十亿token单会话)。
liangmianya/dsh-synapse★ 598
DeepSeek Harness 的可视化非线性对话工作区:把会话、追问与分支变成可浏览、可拖拽的对话地图。
Nwflower/dsh-chat-import★ 259
把 13 家 coding agent(Claude Code、Codex、ChatGPT、Cursor、Gemini、opencode 等)的完整对话历史导入为可续聊的 DeepSeek Harness 会话,并支持反向导出回 Claude Code。
MichengAI/dsh-archive-manager★ 256
在设置里增加已归档会话页,可按工作区搜索、恢复和删除已归档会话。
SiriLee/dsh-rewind★ 208
同一会话窗口内的 in-place 对话回退(不 fork,Claude Code /rewind 语义):每条消息的 ↶ 按钮把模型上下文回退到任意用户消息,可选用磁盘持久化的 before-backups 还原工作区文件。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。