Zum Hauptinhalt springen
L

dsh-global-todo

lugabeeee/dsh-global-todo

Global cross-session todo list in the sidebar that the person and the agent both write to: ten global_todo_* tools add, batch-capture, update, attach documents and links to, append progress notes to and complete items from any session, and each item can be handed to an agent session in one of four modes with the outcome recorded on the item. The list is a single JSON file.

Installation

dsh plugin --profile web add github:lugabeeee/dsh-global-todo

README

中文 · English

dsh-global-todo · 全局待办

全局待办图标:一张打了勾、并有箭头离开的清单卡片

一份跨会话的待办清单:一个属于你的文件,你和 Agent 读写同一份,一键交给某个对话继续干。

三件会话级计划做不到的事——第一件是这个插件最要紧的一条:

  1. Agent 说「做完了」不算数,两个入口都只是声明。它自己在工具里报完成是一种,交给的会话干净收尾、 宿主替它收尾是另一种;两条路都只把记录送进待确认。人点「确认完成」才算完成,点「退回清单」 就回到清单里、完成记录一并清掉。会话级计划没人问这一句:Agent 把表一改,事情就算结束了。
  2. 一个 JSON 文件就是全部。能 cat、能 diff、能提交进仓库、能放 NAS、换机带走——不藏在任何会话日志里。
  3. 交接是一个按钮,不是一段话。四种模式、会先检查工作目录,待办自己的步骤会作为编号计划一起交出去。

面板实况:列表、详情、步骤、待确认、收件箱、提醒、批量选择

dsh plugin add dsh-global-todo

在线主页(22 秒排版演示)· English · 5 分钟上手 · 常见问题 · 每个条件状态都有基线图 · 设计说明


它补的是哪个缺口

DSH 自带的 todo_write 属于某一个 agent 会话:整表替换、三个状态、会话一关计划就翻篇。 社区里解决这件事的插件分成两派:一派擅长把活派给 Agent(自动续跑、绑定会话), 一派能收真正的文档附件。这个插件把两件事放在同一条记录上:

自带 todo_writedsh-global-todo
归属一个 agent 会话你——跨会话、跨工作区
写入方式整表替换逐字段,人和 Agent 都能写
加入时间不记录createdAt 只写一次,处处可见
上下文标题 + 状态描述 + 文档与地址
交给 Agent无一键:复用已绑定会话,或新开一个
标成完成Agent 改了就算进「待确认」,人点头才算完成
存储会话事件日志一个 JSON 文件,能 cat、能手改、能 diff、能备份

完成确认:没有一条记录能自己变成「完成」

这是它最容易被记住的一条,也是它和所有「让 Agent 自己报进度」的清单最不一样的地方: Agent 说「做完了」,只是它说了这句话。 记录写完是 done,同时带上 review: { pending: true };行上有「待确认」徽标,顶部有带计数的筛选,直到人回答为止。

两个入口都受同一条规则管,因为它们同样只是声明:

「做完了」从哪来记录变成谁来回答
Agent 调 global_todo_done,或把状态改成 donedone + 待确认人
交给的会话干净收尾,宿主自动收尾(turn/end 是 completed)done + 待确认人
你自己在面板里点完成done已经是人
  • 回答只有两个:「确认完成」清掉标记,动态里记下是谁确认的;「退回清单」回到 open, 清空 doneAt 与完成记录,动态里加一条 reopened。标题、描述、步骤、附件、进展一条不删。
  • 自动收尾也走这个门。宿主替 Agent 写的那份完成记录本来就更薄——只有最后一段回复和这一轮 改过的文件——它更没理由自己算数。
  • 只有干净收尾才是候选:error、aborted、interrupted、max-tokens 一律留着未完成。 一轮失败的任务挂着「完成」标签,是让整份清单不可信的唯一起点。

规则在代码里只有一个实现:completeTodo、applyPatch 和状态改写共用 lib/model.js 里那个 by === 'agent' 判断,面板和十个工具都走它。

0.9 / 0.10 补了什么(以及再往前两版)

五件这两份 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 是正常情况——这个进程没见那个对话说过话。

Markdown 进与出

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 指向的那份。

每张图显示什么状态、什么时候重新生成,见 docs/screenshots/README.md。

环境要求

  • DeepSeek Harness 0.2.x(按 0.2.1-rc.2 开发与验证;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 标签安装

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 收起详情。行内操作在悬停或键盘聚焦时出现。
  • 访问地址:面板可以正常从宿主的本机、受信任的局域网地址(--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 只发给已绑定的对话,不在就明确报出原因。 找不到任何可用的对话时明确失败,不会偷偷开一个新的。
  • 分组维度:工具栏上有 对话 / 时间 / 级别 三个维度,默认按对话——同一个 agent 对话提交的待办会合并成一条(显示「N 条」,点开才展开),这样几次「抓一下待办」不会把清单糊成一片。单条的对话直接显示那一行;没有来源对话的(在面板里手加的)归到「手动添加」。时间 是原来的逾期 / 今天 / 接下来 / 以后 / 没有截止时间,级别 按高 / 一般 / 低分组。
  • 级别 P0–P3:每条待办可设 P0 紧急 / P1 高 / P2 常规 / P3 低;接口两种写法都收(priority: "P0" 或 "urgent"),旧记录里的 high/normal/low 原样有效,默认 P2。行上有徽标,对话分组头显示组内最高级别,绿色小圆点表示该对话里还有在跑的会话。
  • 从本对话抓取:面板上一个按钮,把「请把这段对话里需要跟进的事用 global_todo_capture 抓一下」注入你正在说话的那个对话——聊着聊着把该跟进的事一次变成待办,不用自己一条条敲。提示词里同样写清了「不要记什么」(会话计划、闲聊、已完成的事),否则抓取只会制造噪音。
  • 权重下限:每条候选会被打一个 0–1 的权重(具体标题、截止时间、步骤、附件、原话、承诺措辞加分;「可能 / 也许 / 看看 / 再说」这类犹豫语、纯疑问句、没有任何补充信息的条目减分),低于设置里的 captureMinWeight(默认 0.35)就不登记,只在 skipped 里报出权重与原因。设置面板里可以直接改这个值。
  • 自动完成:派发出去的会话干净结束时,宿主自己把这条待办标成完成,并附上它最后那段回复作为完成说明、这一轮改动的文件作为产出——不用等模型记得调工具。面板上完成的行显示「完成于 …」加产出芯片,展开详情有「完成记录」段(说明 + 产出路径,可点开或复制)。 两条护栏值得知道:只对刚派发过的会话生效(你在那个会话里随便聊两句不会把待办完成掉),失败 / 中断的回合不标记完成(宁可留着未完成,也不能让一份干砸的活显示成已完成)。 你也可以自己收尾:global_todo_done 支持 summary 与 artifacts,那是主路径,自动收尾只是兜底。
  • 工作目录:工具栏上显示这次派发会让新会话在哪个目录开工。默认跟随你当前所在的对话(从界面侧的会话状态里读 cwd), 也可以点「选择」自己指定(会记住,下次继续用),或点「跟随当前会话」清掉指定值。 待办本身也记着它是在哪个目录里被写下的(Agent 通过工具写的时候从调用上下文取),派发时会继承; 三者都没有时拒绝派发并说明原因——因为一个没有工作目录的会话里,agent 什么也做不了。

Agent 工具

工具用途
global_todo_add登记一条:描述、附件、步骤、标签、优先级、截止时间;同时记下「在哪个工作目录、哪个会话里写的」
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永久删除(做完的请标记,不要删)

实际联通检查

测试用的是替身;npm run check:live 检查的是真正接上线的宿主:面板实际会调的路由、它实际读的清单, 以及一条「删掉再撤销」之后仍然完整的记录。它会临时建一条记录,往返后逐字段比对(id、加入时间、附件、 标签、级别、截止、描述),确认绑定记录带 sessionAlive / sessionDir,最后即使失败也会删掉这条临时记录。 它需要一个在跑的宿主,所以不在 verify 里。

主动抓取

插件会往 system prompt 里注入一段引导(用 guidance: false 关掉)。它把两侧都写清楚:只写「记得记事」的 提示词只会制造噪音,而吵闹的清单比空清单更糟。

  • 主动记:用户提到需要跨会话跟进的事(等对方回复、下周要交、待确认的决定、答应别人的事)、 一轮结束时还有一步不该在本会话继续、或用户说「记一下 / 别忘了 / 回头办」;
  • 不要记:你这一步就能做完的、本会话内的计划、纯闲聊;
  • 记完用一句话说明记了什么,别复述整条描述;
  • 用户说**「抓一下待办」**,就扫最近对话,用 global_todo_capture 一次登记,再回一句汇总。

边界是:它只负责记,从不擅自开工。清单属于人,开不开工是人决定的(见 ROADMAP 里的非目标)。

抓取不能变成噪音。写进去之前,同一个规范化标题(全半角、大小写、空格、标点都折叠掉)、 最近 72 小时内意思很接近的转述(按字符二元组的 Dice 相似度,0.8),以及 7 天内刚做完的同一件事, 都会被拒绝,并在记录上写出原因;已归档和已取消的条目不挡新条目。规则在 lib/capture.js,单独测试。

HTTP 接口

面板通过这些路由读写宿主。有 connection 服务时走宿主的连接围栏,没有时退化为严格的 本机回环校验。

方法路径参数
GET/api/global-todo/liststatus、query、tag、sort、limit——被截断时响应带 matched 与 truncated
GET/api/global-todo/dirspath?——列子目录,供面板内置的目录浏览器使用(宿主自己的选择器在时会优先)
GET/api/global-todo/getid
GET/api/global-todo/export——(存储用的那份 JSON 文档;?format=md 给 Markdown 任务列表)
POST/api/global-todo/import{ doc, mode?: replace|merge, dryRun? }——replace 是无损往返,merge 同 id 覆盖、新 id 追加(面板用的是这个);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? }——面板自己上报的错误

/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(10 个工具)| core(6 个:只操作清单)| none

文件本身就是产物:纯 JSON、一条记录一个待办,写入走「临时文件 + rename」, 并且有一把写锁把人和 Agent 的编辑串起来——两边同时改也不会互相覆盖。 文件坏了会明确报 store-corrupt 而不是被静默清空;单条记录坏了只跳过那一条。 备份就是复制这个文件。

收录情况

npm 是唯一安装源,下面每个地方都只是「能不能被找到」。核对日期 2026-10-21;复核用 node tools/listings.mjs,不要只信这张表。

渠道形式状态
npm包本体0.11.2
1024 Store目录条目(PR #654 已合并)已收录;安装行仍解析成 GitHub 源
dsh.pub目录页 + dshpub CLI已上线,pin 到某个 commit
DSH-Plugins-Marketplaceregistry.json,按 dsh-plugin topic 采集v0.11.2,风险分级 safe
awesome-dsh-plugin(19.7k★)每个插件一个 YAML已合并(PR #7498)
walkinglabs带来源核验的 README已合并(PR #114)

另有五个在审(Zhiyuan-Fan、kejixiaoliang、beancookie、dshworks、losebird),一个维护者已承诺 下次提交收录(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          # 257 单元/契约/bundle 测试,零依赖
npm run verify    # 两者都跑
npm run check:matrix  # 当前 dsh 与 engines.dsh 的实际匹配,再跑 manifest/bundle/
                      # 工具 schema 三类契约(不写 preview)

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 tour          # 用这些 PNG 重新拼 docs/screenshots/tour.gif(需要 ffmpeg)

docs/screenshots/ 下那几张 PNG 会随包发布、并嵌在两份 README 里,所以面板 UI 一改就要重跑 npm run screenshots。

lib/
  index.js       宿主入口:store + service + dispatcher + 可选挂载面
  store.js       一个 JSON 文件:原子写、串行更新、容错读取
  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」的相关权利归其所有者。

Ähnliche Plugins