中文 · English
一份跨会话的待办清单:一个属于你的文件,你和 Agent 读写同一份,一键交给某个对话继续干。
三件会话级计划做不到的事——第一件是这个插件最要紧的一条:
- Agent 说「做完了」不算数,两个入口都只是声明。它自己在工具里报完成是一种,交给的会话干净收尾、 宿主替它收尾是另一种;两条路都只把记录送进待确认。人点「确认完成」才算完成,点「退回清单」 就回到清单里、完成记录一并清掉。会话级计划没人问这一句:Agent 把表一改,事情就算结束了。
- 一个 JSON 文件就是全部。能
cat、能 diff、能提交进仓库、能放 NAS、换机带走——不藏在任何会话日志里。 - 交接是一个按钮,不是一段话。四种模式、会先检查工作目录,待办自己的步骤会作为编号计划一起交出去。
dsh plugin add dsh-global-todo在线主页(22 秒排版演示)· English · 5 分钟上手 · 常见问题 · 每个条件状态都有基线图 · 设计说明
DSH 自带的 todo_write 属于某一个 agent 会话:整表替换、三个状态、会话一关计划就翻篇。
社区里解决这件事的插件分成两派:一派擅长把活派给 Agent(自动续跑、绑定会话),
一派能收真正的文档附件。这个插件把两件事放在同一条记录上:
自带 todo_write |
dsh-global-todo | |
|---|---|---|
| 归属 | 一个 agent 会话 | 你——跨会话、跨工作区 |
| 写入方式 | 整表替换 | 逐字段,人和 Agent 都能写 |
| 加入时间 | 不记录 | createdAt 只写一次,处处可见 |
| 上下文 | 标题 + 状态 | 描述 + 文档与地址 |
| 交给 Agent | 无 | 一键:复用已绑定会话,或新开一个 |
| 标成完成 | Agent 改了就算 | 进「待确认」,人点头才算完成 |
| 存储 | 会话事件日志 | 一个 JSON 文件,能 cat、能手改、能 diff、能备份 |
这是它最容易被记住的一条,也是它和所有「让 Agent 自己报进度」的清单最不一样的地方:
Agent 说「做完了」,只是它说了这句话。 记录写完是 done,同时带上
review: { pending: true };行上有「待确认」徽标,顶部有带计数的筛选,直到人回答为止。
两个入口都受同一条规则管,因为它们同样只是声明:
| 「做完了」从哪来 | 记录变成 | 谁来回答 |
|---|---|---|
Agent 调 global_todo_done,或把状态改成 done |
done + 待确认 |
人 |
交给的会话干净收尾,宿主自动收尾(turn/end 是 completed) |
done + 待确认 |
人 |
| 你自己在面板里点完成 | done |
已经是人 |
- 回答只有两个:「确认完成」清掉标记,动态里记下是谁确认的;「退回清单」回到
open, 清空doneAt与完成记录,动态里加一条reopened。标题、描述、步骤、附件、进展一条不删。 - 自动收尾也走这个门。宿主替 Agent 写的那份完成记录本来就更薄——只有最后一段回复和这一轮 改过的文件——它更没理由自己算数。
- 只有干净收尾才是候选:
error、aborted、interrupted、max-tokens一律留着未完成。 一轮失败的任务挂着「完成」标签,是让整份清单不可信的唯一起点。
规则在代码里只有一个实现:completeTodo、applyPatch 和状态改写共用 lib/model.js 里那个
by === 'agent' 判断,面板和十一个工具都走它。
五件这两份 README 一直没讲的能力:会话回读与原话是 0.9 的,Markdown 与本周报告是 0.10 的, 步骤是 0.8 的。每一个都改变了记录能装什么,或者改变了事后能拿它做什么。 (还有一件是 0.7 的「待确认」,它现在就是开头那条规则,不在这里当新功能讲。)
一条待办最多 20 个步骤、每步 200 字(lib/model.js 的 LIMITS)。步骤是 { text, done }——
一条工作里的一行,不是有自己 id 和状态的子任务。
- 加:新增表单的「更多字段」→ 步骤,一行一步。
- 勾:详情里每步一个勾选框;行上显示「步骤 2/5」。
- 交出去:派发提示词会把步骤编号,已完成的加
[已完成]前缀。 - 导出:Markdown 里写成缩进的
- [ ],导入时读回来。
evidence 是抓取时那条待办来自的原话,逐字引用,最多 300 字。global_todo_capture 会向模型要它,
global_todo_add 也收,详情里以引用块显示在「原话」下面;你自己敲的条目没有这一项。
它只为一件事存在:没有出处的一次抓取,过几天看起来就像一句观点。Markdown 导出里也有这一行(原话:)。
派发是在另一个对话里开工,所以详情要能回答「它说了什么」,不用切过去。
GET /api/global-todo/session?id= 返回 alive、dir 和最后一条 assistant 消息的 tail;
详情里就是「会话回读」:在跑 / 已休眠、最后一句,以及一个打开该对话的按钮。
tail 有上限、只在内存里:200 个会话、每个 400 字,来自每条 assistant/message。不落盘,宿主一重启就忘。
查不到 tail 是正常情况——这个进程没见那个对话说过话。
JSON 导出是往返格式;Markdown 是拿来读、diff、手改的那份:
- [ ] 标题
优先级: 高 · 截止: 2026-10-21T18:00 · 标签: 发布, 文档
原话: …
描述: 第一行
- [x] 一步
| 方向 | 入口 | 行为 |
|---|---|---|
| 出 | 更多操作 → 导出 Markdown,或 GET /export?format=md |
按「未完成 / 已完成 / 已归档」分组,元数据缩进在每条下面 |
| 入 | 更多操作 → 导入(选 .md),或 POST /import { markdown } |
走正常 create 新建;Markdown 没有 id,所以永远不会覆盖已有记录 |
解析是容错的:不认识的行直接忽略,日期解析失败就丢掉那一项,整份文件里没有一行 - [ ] 会明确报错。
字段标注固定是中文(优先级 / 截止 / 标签 / 原话 / 描述 / 附件 / #),导入按同一套读。
更多操作 → 本周报告(GET /report?days=7)回答「这一周动了什么、有没有落到代码上」。
全部从完成记录推出来,不存任何东西。
| 字段 | 含义 |
|---|---|
finished |
窗口内完成的条目,最新在前,带完成者和说明 |
sessions |
这些完成来自多少个不同的对话 |
created |
窗口内新增的条数 |
pendingReview |
已完成但还没人回答的条数 |
files |
触及的文件,按次数排序——列 12 个,fileCount 是总数 |
days 超过 90 会被压到 90;缺省或非正数按 7 算。
每个「只在特定情况下出现」的面板状态都有一张固定基线图,由同一套预览渲染、同一支工具拍摄,改样式时可以直接对比:
| 状态 | 图片 |
|---|---|
| 清单为空 | panel-empty.png |
| 筛选无结果 | panel-filtered.png |
| 收件箱(待确认) | panel-inbox.png |
| 待确认(Agent 完成) | panel-review.png |
| 到点提醒 | panel-reminders.png |
| 批量选择 | panel-select.png |
图都由 npm run screenshots(内部串起 npm run preview)生成:先把真实的客户端半区渲染成静态
HTML(不依赖宿主、零依赖),再以 1280×720 拍进 docs/screenshots/。Playwright 不是依赖——工具
借的是 DSH profile 里已经装好的那份,或 DSH_PLAYWRIGHT 指向的那份。
手机档同一支工具拍:npm run screenshots:mobile 先按 390×844 渲染同一棵组件(夹具会把面板的
实测宽度答成 390,所以窄屏分支是真的被执行,不是把桌面布局缩小),再拍进 docs/screenshots/mobile/。
每张图显示什么状态、什么时候重新生成,见 docs/screenshots/README.md。
- DeepSeek Harness
0.2.x(按0.2.1-rc.2开发与验证,0.2.1-rc.3的变更已逐项核对——它新增的 侧栏「更多」菜单不影响本插件的入口注册;engines.dsh是>=0.2.0-rc.1 <0.3.0,区间外的宿主会 整层跳过这个 bundle,不是警告) - Node.js
>= 22.19.0(Web profile 本身的要求) - Web 客户端:面板由宿主的 web server 提供。headless / ACP / SDK 只有 Agent 工具、没有面板; Electron 式桌面端(file:// + IPC)暂不支持
- 宿主升级后要刷新页面——客户端模块图属于页面启动时那个 release
- 零运行期依赖,也不 import 任何
@deepseek-ai/*包
先确认 profile 名字。 插件是装进某个 profile 的,不是装进整台机器;profile 名写错, 命令会成功,但重启后侧栏里什么都没有——这是最常见的一次踩坑。
ls ~/.dsh/profiles # Windows: dir %USERPROFILE%\.dsh\profiles把下面命令里的 <profile> 换成上一步列出来的那个(本机常见是 web;用桌面端就填桌面端的
profile 名,用 headless 就填 headless):
# 从 npm 装
dsh plugin --profile <profile> add dsh-global-todo
# 或从本地检出装(也是开发路径)
dsh plugin --profile <profile> add /绝对路径/dsh-global-todo插件是宿主组合里的一行 Loader,不是热加载脚本:装完要重启宿主(dsh web,
或你用的那个启动命令),再刷新页面。侧栏会出现「待办」入口。
侧栏还是空的?按顺序查三件事:① 上面的
ls里到底有哪几个 profile,你装的是不是正在跑的那个; ② 宿主有没有真的重启(Loader 只在启动时读一次);③ 页面有没有刷新(客户端模块图属于 启动时那个 release)。
DSH 的 Discover 标签读的是你自己添加的一个 JSON 索引(官方不带索引),这也是唯一一条 不用手抄命令的安装路径。本仓库发布了一份:
https://lugabeeee.github.io/dsh-global-todo/market/index.json
它由 npm run market 从 npm 生成(docs/market/index.json),写入前会用 DSH 自己的
@deepseek-ai/dsh-plugin-market-index 校验一遍。索引里的最新版本与 package.json 不一致时
npm test 会失败 —— 发版后忘了重新生成,会在发出去之前就被拦下。
如果你是手动拷贝目录(而不是从 npm 装),先自检一遍,别靠猜:
npx dsh-global-todo-doctor "$HOME/.dsh/plugins-local/全局待办插件"
# Windows: npx dsh-global-todo-doctor "$env:USERPROFILE\.dsh\plugins-local\全局待办插件"它逐项检查 manifest、patch 文件、两个入口、locale/、图标和 lib/;缺必需项时退出码为 1。
最容易漏的是 locale/——少了它插件照样加载,但桌面端会报
Plugin metadata … ENOENT … \locale。
npm 上的包才是事实来源。社区目录可能落后于 npm、或指向某个旧 release;需要确定版本时
直接钉版本(dsh plugin --profile <profile> add dsh-global-todo@0.13.1)。
回滚:
dsh plugin --profile <profile> remove dsh-global-todo装卸都不会碰你的清单——它在插件之外:$DSH_HOME/global-todo/todos.json。
- 图片附件:附件也可以写成
{ "kind": "image", "value": "/绝对路径/shot.png" },行上显示缩略图、详情里显示预览;字节按需读取(png/jpg/jpeg/webp/gif/avif,≤8MB),不复制进清单。 - 添加:写一句标题回车即存。「更多字段」展开描述、附件框(一行一个文档绝对路径或网址)、 标签、优先级、截止时间。
- 筛选:全部 / 未完成 / 进行中 / 已完成 / 逾期,外加跨标题、描述、标签、附件的文本搜索; 顶部的统计胶囊本身就是筛选按钮。
- 按紧迫度分组:逾期 / 今天 / 接下来 / 以后 / 没有截止时间 / 已完成,每组可折叠—— 「哪件已经晚了」是清单要回答的第一个问题。
- 每一行显示「加入于 10-13 22:47(3 小时前)」,悬停看精确时间戳;另有优先级、截止、 标签、附件数,以及这条是人加的还是 Agent 加的。
- 详情:就地改标题/描述/优先级/截止;挂上或摘掉文档与地址(每条都带「打开 / 复制路径」); 查看动态(谁在什么时候做了什么)。
- 删除可撤销:删掉的记录保留 8 秒,点「撤销」原样恢复——同一个 id、同一个加入时间、同样的附件。
- 键盘:
/聚焦搜索;新增输入框展开后,n聚焦它;Esc收起详情。行内操作在悬停或键盘聚焦时出现。 - 手机 / 窄屏:面板按自己的实测宽度切换,≤600px 进触控布局(折叠手势、66px 一行、44px 触摸目标、安全区)。细节、阈值与实测尺寸见下面的「手机端」一节。
- 访问地址:面板可以正常从宿主的本机、受信任的局域网地址(
--trusted-host里那些,如192.168.1.10:3080)以及隧道访问——路由会交给宿主的连接围栏判断;只有宿主的围栏不存在时才退回「只接受本机请求」的保守检查。/health里的fence会告诉你当前实际是哪一种。 - 自动获取(可关,默认关):面板上的「自动获取」开关。打开后——你在某个对话里正常收完一轮、那个对话安静满 30 分钟、且你人就在那个对话里(不是派发目标/子 Agent/后台任务),插件才会自动问它一次「有没有要跟进的事」。中断/失败的轮次不问。它问过之后,只有你在那个对话里再说一句话才会重新问——因为那条提问本身就是对话里的一条消息,会重置安静计时、并让它自己的回答收尾成一轮,曾经正是这一点让同一个对话整夜追着自己反复问;冷却只是次要的阻尼。开关是宿主状态(面板关着也生效,
/health可见),关掉就完全靠你和 Agent 手动记。另外每个工作区分组旁边有一个**「豁免」按钮:点一下就把那个工作区整个排除在自动获取之外(再点一下取消)。这是给由机器驱动的对话准备的——比如微信机器人所在的聊天区,它们的会话重启后会换新 id,所以豁免记在目录上而不是某一条对话上**,才不会悄悄失效。 - 主动记:插件会往 system prompt 里注入一段引导,列出该主动记的具体情形(跨会话的跟进、「先做 X 再 Y」、一轮收尾时发现还有一步),并明确写上「问用户要不要记是错的,直接记」。
- 「抓取」在每个对话的分组上:让那个对话里的 Agent 把该跟进的事登记成待办,结果就归在它自己那组下面(分组名旁边那个「抓取」)。工具栏上的「从本对话抓取」只在当前对话还没有分组时出现——否则没地方点它。抓取对象没在运行会先唤醒再问(与派发同一条规则)。
- 工作目录:
选择会用宿主的目录选择器;如果这个部署没挂载它(远程命名空间缺失),面板会自动改为手动填路径(手动填→ 粘绝对路径 →使用),并说明原因——不会只丢一条看不懂的错误。 - 一键分配(多选):工具栏点「选择」→ 勾几条 → 「一键分配」。每条按原对话 → 同工作区对话 → 你选的目标的顺序尝试(是尝试顺序,不是一次定死:原对话加载不了就自动往下走)。都找不到的会报回来并给你选择器,绝不擅自送到别处;其中一条失败也不会中断整批。
- 搜索语法:
tag:发布(或#发布)、is:overdue、is:active、has:link、has:note、level:p0(也可写level:urgent)、before:2026-11-01/after:2026-10-01(按截止时间卡边界;日期没打完时只是不生效,不会报错),自由词之间是 AND,并且会一并搜进展记录。不带冒号的普通搜索行为不变。 - 进展记录:每条待办可追加「谁在什么时候说了什么」的流水(行上显示条数,详情里看全文并可直接补一条)。Agent 报「做不完、卡在哪」应该写在这里,而不是改写你的描述。
- 失效附件会标出来:文件被移走/删除的附件在行上标红、详情里注明;这个标记是读取时算的,不写进记录——文件回来了就不该再标红。
- 新对话开在哪:默认继承写下这条待办的对话所在的工作区(新会话的工作目录也就是那个工作区),所以「接着这个活干」不会跑到不相干的工作区去;想指定就选
+ 新对话 · 工作区名(列在工作区一栏里),指定的优先。只有当两者都不知道时,才退回按工作目录判断。 - 选择器按「最后使用」排序、不含已归档的对话:顺序就是侧栏的 recency 键(
updatedAt),最近用过的在最上;归档过的对话完全不列(归档的对话不是「这活该放哪」的答案)。同名对话会带一个短 id 后缀以免选错;如果当前目标已不在列表里(选完之后被归档),会显示为「(已归档)」而不是悄悄显示成另一个对话。 - 交给谁由你选:工具栏上是
交给:[ 对话名 ▾ ]——列出你的对话,你点哪个就送哪个(显式选择以authoritative: true发下去,Host 原样照办,不再猜)。列表里会标注 Host 自己要选的那个(「最近说过话」);想放回自动判定就选自动,想要干净上下文就选+ 开一个新对话。选过一次会记住,换目标只需在这个下拉里改——因为你看见的就是会发生的事。 - 点完有回执:派发成功后面板会弹出「已交给「对话名」——去那个对话看它的进展」,并带一个**「打开那个对话」按钮直接跳过去。派发是在另一个对话**里开工的,之前面板一声不吭,看起来就像「点了没反应」。
- 交给 Agent:面板上先显示这次要交给谁,默认是
current当前对话——也就是你本人最近在里面写过消息的那个对话(Host 读会话日志,只认source.kind=user的消息;插件自己注入的派发消息不算,否则每次派发都会把「当前对话」推向它自己刚派发的目标;面板打开时它自己那个「主视图保留的会话」可能是几小时前的陈旧值,所以活动流优先,只有「确实知道自己在哪个对话里」的调用方才能用authoritative: true覆盖)。想让它去别处就换:auto优先复用这条待办已绑定的会话、其次是当前对话、最后才新开;new总是新开对话;bound只发给已绑定的对话,不在就明确报出原因。 找不到任何可用的对话时明确失败,不会偷偷开一个新的。 - 分组维度:工具栏上有
工作区 / 时间 / 待办等级三个维度,默认按工作区——同一个目录里写下的待办会合并成一条(显示「N 条」,点开才展开),组头就是那个目录的名字(悬停看完整路径),这样几次「抓一下待办」不会把清单糊成一片;既没有来源目录也没有绑定会话的(在面板里手加的)归到「手动添加」。时间是逾期 / 今天 / 接下来 / 以后 / 没有截止时间,待办等级按高 / 一般 / 低分组。 - 级别 P0–P3 由截止时间决定:没有日期 = P3 低,7 天内 = P2 常规,48 小时内 = P1 高,已逾期 = P0 紧急。Agent 传的
priority("P0"或"urgent"都收)作为suggestedPriority保留在记录里作参考,不再决定排序;你在面板上改一次就锁死(prioritySource: human),之后规则不再覆盖它。行上有徽标,工作区分组头显示组内最高级别,绿色小圆点表示该工作区里还有在跑的会话。 - 从本对话抓取:面板上一个按钮,把「请把这段对话里需要跟进的事用
global_todo_capture抓一下」注入你正在说话的那个对话——聊着聊着把该跟进的事一次变成待办,不用自己一条条敲。提示词里同样写清了「不要记什么」(会话计划、闲聊、已完成的事),否则抓取只会制造噪音。 - 权重下限:每条候选会被打一个 0–1 的权重(具体标题、截止时间、步骤、附件、原话、承诺措辞加分;「可能 / 也许 / 看看 / 再说」这类犹豫语、纯疑问句、没有任何补充信息的条目减分),低于设置里的
captureMinWeight(默认 0.35)就不登记,只在skipped里报出权重与原因。设置面板里可以直接改这个值。 - 自动完成:派发出去的会话干净结束时,宿主自己把这条待办标成完成,并附上它最后那段回复作为完成说明、这一轮改动的文件作为产出——不用等模型记得调工具。面板上完成的行显示「完成于 …」加产出芯片,展开详情有「完成记录」段(说明 + 产出路径,可点开或复制)。
两条护栏值得知道:只对刚派发过的会话生效(你在那个会话里随便聊两句不会把待办完成掉),失败 / 中断的回合不标记完成(宁可留着未完成,也不能让一份干砸的活显示成已完成)。
你也可以自己收尾:
global_todo_done支持summary与artifacts,那是主路径,自动收尾只是兜底。 - 工作目录:工具栏上显示这次派发会让新会话在哪个目录开工。默认跟随你当前所在的对话(从界面侧的会话状态里读
cwd), 也可以点「选择」自己指定(会记住,下次继续用),或点「跟随当前会话」清掉指定值。 待办本身也记着它是在哪个目录里被写下的(Agent 通过工具写的时候从调用上下文取),派发时会继承; 三者都没有时拒绝派发并说明原因——因为一个没有工作目录的会话里,agent 什么也做不了。
手机页不需要任何配置:DSH Mobile 把面板放进 main 槽,面板量自己的宽度,≤600px 进触控布局。
把桌面窗口拖窄到 600px 以下也是同一套——信号是面板的实测宽度,不是窗口宽度,也不靠 UA 猜。
601px 以上桌面一字不变,check:render 对 601 / 900 / 1280 / 1440 四个宽度有像素断言。
390×844 下实测:头部一行 44px、工具栏三行(筛选 / 搜索 / 分组,各 44px)、清单从 287px 开始、 每条待办 66px——第一屏能看到八条左右。
- 折叠是手势,不是开关。 向下滑过 80px 收起搜索那一行,并且要越过它让出的 110px——否则刚收起来的 内容会从指下消失,看起来像页面在跳。向上滑 48px 或回到顶部展开。翻面后 300ms 内不再响应, 拇指停在边界上不会来回抖。
- 收起后每条只剩「标题 + 一行信号」:截止 / 优先级 / 待确认 / 完成时间,打开着的那条保持完整; 附件芯片淡出而不是硬消失(行高 66px 不变)。
- 三种分组一直可见:工作区 / 时间 / 待办等级是横排标签,不随折叠收起——「工作区」按写下这条待办的 目录分组,组头是目录名。
- 手机上不摆没用的开关:「交给 / 上下文」与「自动获取」从手机工具栏移到设置面板(桌面照旧), 「选择」挪到搜索框后面;工具栏只剩筛选、搜索、分组三行。
- 触摸与安全区:按钮、输入框、筛选胶囊都是 44px;筛选横向滑动而不是被裁掉;行内「交给 Agent」收成图标
(菜单与详情里仍是文字);键盘提示隐藏;底部让出安全区;正文跟随手机页的「文字大小」设置,
输入框不小于 16px(否则 iOS 会替你把页面放大)。
这些数字是量出来的,不是写出来的:
npm run check:mobile从每个控件中心向外探到「再往外就不属于它」为止——2026-10-31 就是这样发现 搜索框(44px 的框里只有 20px 的输入框)、附件胶囊(22px)、详情里的分段控件(29px)和 「轮换 token」这类文字按钮(20px)都够不着。 - 不改别人:
dsh-mobile、DSH 和手机插件都不用改配置;桌面端观感不变。
390×844 的三张基线图:面板 · 展开一条 · 设置。
| 工具 | 用途 |
|---|---|
global_todo_add |
登记一条:描述、附件、步骤、标签、优先级、截止时间;同时记下「在哪个工作目录、哪个会话里写的」。没有 dueAt 的 Agent 记录会先进收件箱(见「自动记录的门槛」) |
global_todo_note |
追加一条进展记录(做到哪了、卡在哪、需要谁做什么);不改写你的描述 |
global_todo_capture |
一次登记多条(批量抓取):用户说「抓一下待办」,或你一轮里发现好几件要跟进的;逐条校验,坏的那条只跳过它自己。同样:没有日期的条目进收件箱 |
global_todo_list |
按状态/标签/关键词查询,每条都带上加入时间 |
global_todo_get |
读一条的全文,含附件与最近一次派发 |
global_todo_update |
改任意字段,包括 status(archived 归档而不删除);"" 清空,[] 清空列表 |
global_todo_link |
挂上或摘掉文档与地址 |
global_todo_done |
标完成 / 标回未完成 / 标进行中 |
global_todo_reopen |
把被误标完成的一条标回未完成(只做 done / cancelled → open);本来就未完成的会被拒绝,动作按 agent 记进活动轨迹 |
global_todo_remove |
永久删除(做完的请标记,不要删);删除会留墓碑,另一台机器不会把它带回来 |
global_todo_sync |
和共享目录里的副本合并一次(多端合一;面板里填了「共享目录」,或部署配置了 sync.dir);dryRun 只算不写 |
测试用的是替身;npm run check:live 检查的是真正接上线的宿主:面板实际会调的路由、它实际读的清单,
以及一条「删掉再撤销」之后仍然完整的记录。它会临时建一条记录,往返后逐字段比对(id、加入时间、附件、
标签、级别、截止、描述),确认绑定记录带 sessionAlive / sessionDir,最后即使失败也会删掉这条临时记录。
它需要一个在跑的宿主,所以不在 verify 里。
插件会往 system prompt 里注入一段引导(用 guidance: false 关掉)。它把两侧都写清楚:只写「记得记事」的
提示词只会制造噪音,而吵闹的清单比空清单更糟。
- 主动记:用户提到需要跨会话跟进的事(等对方回复、下周要交、待确认的决定、答应别人的事)、 一轮结束时还有一步不该在本会话继续、或用户说「记一下 / 别忘了 / 回头办」;
- 不要记:你这一步就能做完的、本会话内的计划、纯闲聊;
- 记完用一句话说明记了什么,别复述整条描述;
- 用户说**「抓一下待办」**,就扫最近对话,用
global_todo_capture一次登记,再回一句汇总。
边界是:它只负责记,从不擅自开工。清单属于人,开不开工是人决定的(见 ROADMAP 里的非目标)。
抓取不能变成噪音。写进去之前,同一个规范化标题(全半角、大小写、空格、标点都折叠掉)、
最近 72 小时内意思很接近的转述(按字符二元组的 Dice 相似度,0.8),以及 7 天内刚做完的同一件事,
都会被拒绝,并在记录上写出原因;已归档和已取消的条目不挡新条目。规则在 lib/capture.js,单独测试。
一条规则:Agent 记的事,没有截止时间就先进「收件箱」,不直接进清单。
| 入口 | 怎么走 |
|---|---|
| 面板手输 | 永远直接进清单——是你自己敲的,级别也听你的 |
| Agent 工具 / 批量抓取 | 有 dueAt → 进清单;没有 → 收件箱,等你「收下」或「丢弃」 |
| 每天额度 | Agent 写的记录每天最多 8 条进清单,超出的也进收件箱(不丢,只是不在清单里) |
| 外部 ingest | 仍按它自己的置信度阈值,不看日期 |
为什么:本机 2026-10-16 → 10-30 的 357 条记录里 99% 是 Agent 写的、日均 16 条;未完成的 86 条里 40 条标「高」,其中 34 条没有截止时间——级别和清单都不再携带信息。
配套的三条规则:
- 级别由截止时间算,不再由模型挑:没有日期 = P3,7 天内 = P2,48 小时内 = P1,已逾期 = P0。模型想给的级别留在
suggestedPriority里只作参考;人改一次就锁死(prioritySource: human),之后改截止时间也不会被覆盖。 - 收件箱不在清单的计数里:收件箱中的记录不出现在「全部 / 未完成」,也不计入那些数字——所以「未完成」现在是清单本身的条数,不再是「文件里 open 的条数」。
- 这一轮自己记过,就不再问:Agent 在同一轮里写过待办,本轮结束后的自动获取提问直接跳过;
/health的autoCollect.stats.skippedCaptured能数出来。
自动获取的提问另写了一句更严的(一次最多 2 条、没有就直说没有、只记「等别人 / 有截止 / 答应过的」),和「抓一下待办」那句分开——后者是你主动要的,宁可多记。
升级到 0.22 后第一次启动会做一次一次性清理:把还没完成、没有截止时间、而且你从没动过的 Agent 记录移进收件箱, 再按截止时间重算剩下的级别。这台机器上回放的结果是 68 条移入收件箱、6 条改了级别,清单从 87 条降到 19 条。 你亲手改过的记录一条都不动;收件箱里可以多选「收下」把它们放回清单。之后不再自动动它们。
这台机器上装了 dsh-mnemon 时,插件会额外注入一段分工,并且只在真的检测到记忆插件时才出现。 两个都会说「记一下」的插件放在一起,最常见的坏结果是同一件事写两遍:一遍当动作、一遍当事实。
| 记到哪 | 装什么 |
|---|---|
待办(global_todo_*) |
要跟进的动作:等回复、下周要交、答应别人的事 |
记忆(mnemon_remember / mnemon_recall) |
已经知道的事实:决定、偏好、结论、踩过的坑 |
- 待办完成时,工具结果会多一句提醒:「结论值得以后想起来就
mnemon_remember一条,正文带上待办 id」。 - 从记忆里读到「还有件事要办」→ 记成待办;只留在记忆里的事不会被「画掉」。
- 不装记忆插件时,这段引导和那句提醒都不出现——没有的东西不能被模型当成能调用的工具。
实现是两处:memoryGuidanceFor()(lib/index.js,独立 section,ctx.inject(['systemPrompt','mnemonMemory'])
命中才注册)和 createTools({ hasMemory })(lib/tools.js,工具结果里那句话)。
同一份清单要在主机和笔记本上都出现时,插件不共享文件,而是各存一份、在中间碰头:
把 storePath 指到 NAS 上会让面板的每次读都变成一次网络往返,而且断网时连自己的清单都打不开。
在每台机器的面板里打开 设置 → 多端同步,填上同一个绝对路径:
| 这一项 | 值(示例) |
|---|---|
| 共享目录(Linux / macOS) | /mnt/nas/agent-rw/global-todo |
| 共享目录(Windows) | Z:\Agent产出\global-todo(映射的盘符或 UNC 路径都行) |
| 自动同步间隔 | 默认 5 分钟;0 = 只在手动/让 Agent 调工具时同步 |
值就落在 settings.json 的 syncDir / syncMinutes 里。无面板的 headless 部署可以在插件配置的 config.sync.dir 里给默认值,
面板填了之后以面板为准:
# profile 的 cordis.patch.yml(config 是整体替换,重述你要保留的键)
- id: global-todo
config:
sync:
dir: /mnt/nas/agent-rw/global-todo
intervalMinutes: 5两边指向同一个目录后,那里会出现 todos.hub.json(以及它旁边的 .lock)。一次同步 =
读本地 + 读 hub → 合并 → 写回 hub(带锁、写前重读再合并)→ 把合并结果写回本地。合并规则只有三条:
| 情形 | 结果 |
|---|---|
| 两台改了不同的待办 | 两边都留下 |
| 两台改了同一条 | updatedAt 晚的那次赢;同一毫秒才比内容,而两边算出来一样 |
| 一边删除、另一边只是旧副本 | 删除赢。删除会留下墓碑(文档里的 trash),随文档一起走;撤销删除会把墓碑一起撤掉 |
| 两台离线时都新建 | seq 编号由记录集合决定:先创建的保留号码,另一条按创建顺序重排,两台得到相同编号 |
- 触发:启动后约 10 秒第一次,之后每分钟检查一次间隔(间隔就是面板里那个值;改完不用重启);
也可以点面板的「立即同步」、
POST /api/global-todo/sync,或让 Agent 调global_todo_sync({ "dryRun": true }只算不写)。 - 读列表永远读本地文件:共享盘慢、掉线、甚至根本没挂上,面板和工具都照常工作,
只是
/health.sync会给出「上次同步失败 + 原因」。 - 不会替你造出共享目录:上层目录不存在就报
sync-unavailable(503)。否则盘没挂上时, 它会安静地建一个本地空目录,两边各自写着、看着都正常——那才是最难查的故障。 - hub 写入带锁、写前二次合并:两台在同一秒同步,后写的那台会先重读 hub,不覆盖对方刚送来的记录。
- 心跳很便宜:没有变化就两边都不写(不刷 mtime、不惊动同步盘)。
所有机器都要 ≥ 0.21.0。 旧版本读到带 trash 的文档会按「schema 版本更新」拒绝打开——
这是有意的:悄悄丢掉墓碑,删除会在下一次同步里复活,而那看起来像「删除功能坏了」。
它做不到的(写在这里,而不是等你撞上):
- 同一条待办里不同字段的并发修改(A 加标签、B 改优先级)只留晚的那次;
- 墓碑保留一年(
TOMBSTONE_TTL_MS),离线超过一年的机器手里的旧副本会被当成真实数据; updatedAt是各机器自己的墙上时间,时钟跑快的那台更容易「赢」;- 不传附件本身:附件是路径,两台机器上要各自可达(同一份 NAS 路径就行)。
面板通过这些路由读写宿主。有 connection 服务时走宿主的连接围栏,没有时退化为严格的 本机回环校验。
| 方法 | 路径 | 参数 |
|---|---|---|
| GET | /api/global-todo/list |
status、query、tag、sort、limit——被截断时响应带 matched 与 truncated |
| GET | /api/global-todo/dirs |
path?——列子目录,供面板内置的目录浏览器使用(宿主自己的选择器在时会优先) |
| GET | /api/global-todo/get |
id |
| GET | /api/global-todo/export |
——(存储用的那份 JSON 文档;?format=md 给 Markdown 任务列表) |
| POST | /api/global-todo/import |
{ doc, mode?: replace|merge, dryRun? }——replace 是无损往返,merge 按记录合并(updatedAt 晚的赢,删除随墓碑传播;面板用的是这个);dryRun 只返回 wouldCreate / wouldUpdate,不落盘 |
| POST | /api/global-todo/reminders/ack |
{ ids }——这些已到期的提醒不再列出来 |
| POST | /api/global-todo/note |
{ id, text }(追加进展记录) |
| POST | /api/global-todo/capture |
{ sessionId? }(请当前对话把要跟进的事抓进清单) |
| POST | /api/global-todo/bulk |
{ ids, status?, addTags?, removeTags? }——一次改一整批;失效的 id 进 unmatched,不会让整批失败 |
| POST | /api/global-todo/ingest |
{ items: [{ title, source: { kind }, confidence }], dryRun? },带 Authorization: Bearer <settings.ingestToken>——外部条目;置信度低于 ingestConfirmBelow 的先进收件箱 |
| GET | /api/global-todo/health |
—(数据位置、统计、派发能力,以及工具注册状态与自动收尾观察者是否挂上——这两种失败是静默的,所以必须能查;还有 metrics:派发成功率、工具调用次数、清单增长;autoCollect:每个候选对话为什么被问/没被问、计次,以及设置文件是怎么加载进来的) |
| POST | /api/global-todo/create |
{ title, description?, links?, steps?, tags?, priority?, dueAt? } |
| POST | /api/global-todo/update |
{ id, ...字段, by?: human|agent } |
| POST | /api/global-todo/status |
{ id, status, by?: human|agent } |
| POST | /api/global-todo/remove |
{ id } |
| POST | /api/global-todo/restore |
{ todo } —— 原样还原被删除的记录(撤销) |
| POST | /api/global-todo/review |
{ id, decision: confirm|reject } —— 回答 Agent 的完成声明 |
| GET | /api/global-todo/report?days=7 |
这段时间里 Agent 完成了什么、触及了哪些文件 |
| GET | /api/global-todo/session?id= |
绑定的会话最后说了什么(tail / alive / dir) |
| POST | /api/global-todo/relink |
{ id, index, value }——把失效附件重新指向(新值仍须是该类型的合法绝对路径/网址) |
| GET | /api/global-todo/asset?id=&index= |
读图片附件的字节(路径只从待办记录里取,不接受请求传路径;≤8MB,png/jpg/jpeg/webp/gif/avif) |
| POST | /api/global-todo/link |
{ id, add?: [...], remove?: [值 或 下标] } |
| POST | /api/global-todo/dispatch |
{ id, mode?: auto|bound|new|current, sessionId?, dir? } |
| POST | /api/global-todo/assign |
{ ids, items?, target?, dir? }——一次把多条交给 Agent;配不上的 id 原样报回,不擅自送到别处 |
| POST | /api/global-todo/settings |
{ autoCollect?, idleMinutes?, cooldownMinutes?, exemptWorkspaces?, captureMinWeight? }——自动获取的开关与阈值;captureMinWeight(0–1,默认 0.35)是批量抓取的权重下限,低于它的候选只在 skipped 里报出、不登记 |
| POST | /api/global-todo/diagnostics |
{ scope, status?, reason?, message?, url? }——面板自己上报的错误 |
| GET | /api/global-todo/sync |
——(configured、dir、last、error:多端同步现在是什么状态) |
| POST | /api/global-todo/sync |
{ dryRun? }——立刻合并一次并写回两边;没配置共享目录时 sync-not-configured |
/update 与 /status 的 by 说明这次写是谁做的:不传(面板就是这样)就是人在键盘前,
脚本替 agent 干活时写 by: 'agent',而不是被记成人的操作。传别的值是 400——
一个会把笔误悄悄收下的来源字段,比没有这个字段更糟。
错误是带类型的:非法值 400、id 不存在 404、请求体过大 413、过不了围栏 403、
够不到 agents 服务 502。响应体始终是 { ok, ... },并带机器可读的 reason。
$DSH_HOME/global-todo/todos.json,也可以指定任意绝对路径:
# profile 的 cordis.patch.yml
- id: global-todo
config:
storePath: /home/me/notes/global-todos.json # 放进同步盘或版本库
dispatchDir: /home/me/projects/thing # 新会话的默认工作目录
requireExistingFiles: true # 文件还不存在就先不让挂
toolGroups: all # all(11 个工具)| core(6 个:只操作清单)| none
sync:
dir: /mnt/nas/agent-rw/global-todo # 多端合一的碰头目录;空 = 单机模式
intervalMinutes: 5 # 多久同步一次文件本身就是产物:纯 JSON、一条记录一个待办,写入走「临时文件 + rename」。
同一个进程里,人和 Agent 的编辑走同一条串行链;跨进程还有一把文件锁:写之前先独占建出
todos.json.lock,另一个宿主进程(Web 面板 + 桌面端 / 命令行会话,或两台机器共用一份同步盘)
只能在旁边排队,等不到 5 秒就明确报 store-busy(503)而不是默默把你的改动覆盖掉。
文件坏了会明确报 store-corrupt 而不是被静默清空;单条记录坏了只跳过那一条。
备份就是复制这个文件(外加确认没有 .lock 残留——正常情况下它写完就没了)。
文档里还有一份 trash:每次删除留下一条墓碑 { id, at, by },保留一年、最多 5000 条
(lib/replica.js 的 TOMBSTONE_TTL_MS / TOMBSTONE_LIMIT)。没有它,删除就传不到另一台机器:
对方手里的旧副本会把记录原样带回来。要清掉也可以,但那等于放弃「删除会传播」。
同一个目录里还有 settings.json(宿主侧开关)和 client-errors.log(面板错误日志,有上限);
备份只需要 todos.json。如果 .lock 一直在、而且写入一直报 store-busy,说明有进程正持有锁——
先确认没有第二个宿主在用同一个 storePath,再按 FAQ 第 9 问 处理。
npm 是唯一安装源,下面每个地方都只是「能不能被找到」。核对日期 2026-10-26;复核用
node tools/listings.mjs,不要只信这张表。
页头那三枚徽章里有两枚是目录自己发的,不是我们画的:dsh.works 的静态徽章来自他们 README
给出的写法,dsh-xray 的是动态端点——它读对方每天的扫描结果,随扫描自动变,点进去是按行号列证据的
能力卡片。第三枚只说明 npm 上的版本号。
| 渠道 | 形式 | 状态 |
|---|---|---|
| npm | 包本体 | 0.22.0 |
| dsh.works | 每日重建的机读索引(21,928 条) | 已收录,跟到 0.19.0 |
| dsh-xray | 能力卡片 + 可嵌入徽章 | 评级 C2。此前被记成 C3:files 里一条 ! 让他们的白名单判断整包失效(#7)。他们改用 npm-packlist 修正后重新扫描(#9),我们也不再在自己的 files 里用取反;exec_tooling / eval_tooling 仍作为不计分证据留在卡片上 |
| 1024 Store | 目录条目(PR #654 已合并) | 版本已跟到 0.19.0;安装行仍是 not_checked 的 GitHub 源——他们的仓库巡检自 2026-09-07 停摆(#19) |
| awesome-dsh-plugin(19.7k★) | 每个插件一个 YAML | 已合并(PR #7498) |
| bruc3van/awesome-dsh-plugin(469★) | MARKET.md 表格 |
已收录,第 656 行 |
| dsh-plugin.shop(22★) | 人工策展条目(PR #49 已合并) | 安装行是 npm spec dsh-global-todo,不再是 GitHub 源——它替掉的正是那个钉死 v0.6.6 的自动条目 |
| dsh.pub | 目录页 + dshpub CLI |
页在,安装命令 pin 到某个 commit |
| DSH-Plugins-Marketplace | registry.json,按 dsh-plugin topic 采集 |
v0.19.0,风险分级 safe |
| walkinglabs | 带来源核验的 README | 已合并(PR #114) |
| Blue-Whale | 站点索引 + repos.txt |
已收录(tools 分类) |
| deepseekserver.com | 中英各一个资源页 | 页在,快照偏旧(还写着 ⭐3) |
另有五个在审(Zhiyuan-Fan、kejixiaoliang、beancookie、losebird,以及一份对 1024 目录的更新 PR #662),一个维护者已承诺 下次提交收录(dshopen.org)。完整表格、以及我们尚未逐条核实的 topic 自动采集渠道,见 docs/DISTRIBUTION.md。
附件是引用,不是上传。 文档就是一个原地不动的绝对路径,地址就是一个 URL。 插件不复制任何字节:文档依然可编辑、可 diff,清单保持小巧,被派发的 Agent 用 自己的工具打开的就是同一个文件。带用户名密码的 URL 一律拒绝——否则清单、动态记录和 每一次派发提示词里都会留下那份凭据。
加入时间是一等公民。 createdAt 写入一次、永不改写;面板显示它,工具同时给出
epoch 与 ISO,派发提示词也带上它。因为「这事什么时候落到我头上的」本来就是全局待办
一半的意义。
只有一条写入路径。 面板和工具调用的是同一批 service 函数,所以一条校验规则、 一条动态记录、那把写锁,不可能只在一侧存在。
失败的派发要比成功的更显眼。 成功和失败都记在待办上,面板把失败原因显示出来。 一次悄无声息的「什么也没发生」,比压根没派发过更糟。
可选服务缺失就降级,不崩。 webServer、tools、agents、agentPresets、
workspaceRegistry、agentDefaultModel 全部通过 ctx.inject 或带保护的 ctx.get 获取。
精简组合里少的只是那一块能力。
数据模型与被否掉的方案见 docs/DESIGN.md; 下一步可以做什么见 docs/ROADMAP.md。
npm run check # 解析全部文件、校验发布形态;本机有 DSH profile 时,还会用宿主
# 自己的校验器逐条验证工具 schema
npm test # 单元/契约/bundle 测试,零依赖
npm run verify # 两者都跑
npm run check:matrix # 当前 dsh 与 engines.dsh 的实际匹配,再跑 manifest/bundle/
# 工具 schema 三类契约(不写 preview)发版有单独的一份流程:docs/RELEASING.md —— 从 release:check 到
移植到公开仓、从 npm 取 tarball 做 Release 资产、再到市场索引,每一步都写着它防的是哪个已经发生过的事故。
engines.dsh 是唯一真正决定“装不装得上”的版本事实:区间外的宿主会整层跳过这个
bundle,而不是警告;所以放宽区间之前先跑 check:matrix。
没有构建步骤。 宿主半区是 lib/ 下的纯 ESM;客户端半区是一个自注册到
window.__ModuleLoader__ 的普通脚本——不需要打包器、不需要 TypeScript 工具链、
CI 里也没有构建产物。改 lib/client.js 刷新页面即生效;改宿主半区要重启宿主。
npm run preview # 把面板渲染成 preview/panel.html(另附详情态与选择态)
npm run screenshots # 把这些预览重新拍成 docs/screenshots/,1280×720
npm run check:mobile # 在真浏览器里逐个量手机档的命中区(44px),先跑 preview:mobile
npm run tour # 用这些 PNG 重新拼 docs/screenshots/tour.gif(需要 ffmpeg)docs/screenshots/ 下那几张 PNG 会随包发布、并嵌在两份 README 里,所以面板 UI 一改就要重跑
npm run screenshots。
lib/
index.js 宿主入口:store + service + dispatcher + 可选挂载面
store.js 一个 JSON 文件:原子写、进程内串行 + 跨进程文件锁、容错读取
lockfile.js 那把锁:wx 独占建文件、pid/host 判活、过期接管
model.js 待办实体:校验、补丁、状态、附件、步骤、待确认、进展
links.js 附件规则(纯函数,不碰文件系统)
service.js 唯一写入路径、筛选、排序与派发提示词
routes.js 面板用的 HTTP 接口
tools.js 十一个 Agent 工具
dispatch.js 把待办交给 agent 会话(auto / bound / new / current)
completion.js 干净一轮之后的自动收尾观察者
capture.js 抓取去重闸门(同名、近义转述、刚完成)
ingest.js 外部写入入口与收件箱规则
markdown.js Markdown 导出与容错导入
report.js 从完成记录推出来的周报
reminders.js 到点提醒的判定规则
settings.js 宿主侧开关与自动获取策略
metrics.js /health 里的进程内计数
diagnostics.js 有上限的面板错误日志
errors.js 带机器码的类型化失败
client.js 浏览器半区(面板 + 侧栏入口)
test/ 单元、service、工具、路由、派发、bundle、manifest 测试
tools/ preview.mjs —— 不依赖宿主,把面板渲染成静态 HTML
screenshot.mjs —— 把预览拍成 PNG(借 profile 里的 Playwright)
tour.mjs —— 用这些 PNG 拼 README 里的 tour.gif
docs/ DESIGN.md(数据模型与取舍)、ROADMAP.md
欢迎贡献,先读 CONTRIBUTING.md。
问题、bug 和想法都发到 Discussions。 报 bug 时带上 DSH 版本、面板页脚里的插件版本、系统,以及能复现的步骤;提示语截图比一段描述更有用。 安全问题请走 SECURITY.md,不要发在讨论区。
HTTP 接口只服务本机,清单是本地文件,任何凭据都不会被存储。 漏洞请按 SECURITY.md 的方式报告。
MIT © 2026 LUgABeEEE
社区插件,不是 DeepSeek 官方项目:包名里的 dsh 只表示它接入的生态,不代表任何背书;「DeepSeek」与「DeepSeek Harness」的相关权利归其所有者。




