# 常见问题 19 个真问题,答案都能在代码里核对。文中路径相对仓库根;`$DSH_HOME` 默认是 `~/.dsh`。 > **这个插件的中心规则:完成要人点头。** Agent 在工具里报完成,和交给的会话干净收尾后宿主自动 > 收尾,都只把记录送进「待确认」;你点「确认完成」才算完成,点「退回清单」就回到清单里。为什么 > 两条路都要人回答,见[第 3 问](#3-agent-把一条标成完成之后会发生什么)。 ## 1. 我的数据到底存在哪?卸载会删掉吗? 默认 `$DSH_HOME/global-todo/todos.json`,可以用插件配置的 `storePath` 指到任意绝对路径 (`lib/store.js` 的 `resolveStorePath`)。同一个目录里还有 `settings.json`(宿主侧开关)和 `client-errors.log`(面板错误日志,有上限)。 安装和卸载都**不碰**这些文件。卸载后手动 `rm -r` 那个目录才会彻底清掉。 ## 2. 两台机器之间会同步吗? 0.21.0 起会了,但要你自己指定一个**碰头目录**(插件仍然没有账号、没有服务器): 在**每台机器**的面板里打开 设置 → 多端同步,把「共享目录」填成同一个绝对路径(Windows 用盘符或 UNC, 例如 `Z:\Agent产出\global-todo`;「自动同步间隔」填 0 就只在手动同步)。无面板的 headless 部署可以在 `cordis.patch.yml` 的 `config.sync.dir` 里给默认值。 每台机器**各存一份**本地文件,在那个目录里交换一份 `todos.hub.json`。同步 = 两边都折一次: 按记录合并(同一条看 `updatedAt`,删除靠墓碑传播,`seq` 重号会按创建顺序重排),所以顺序、重试、 两台同时同步都不会丢记录。规则与限制见 README 的「两台机器,一份清单」。 - 读列表**永远读本地文件**:共享盘挂了、NAS 掉线,清单照常能用,只是 `/health.sync` 报失败原因。 - 手动同步:面板/脚本 `POST /api/global-todo/sync`,或让 Agent 调 `global_todo_sync`。 - **不要**再把 `storePath` 指到共享盘上:那是另一种做法(共享同一个文件),每次读都走网络、 断网就打不开,而且它和 `sync` 混用会互相覆盖。 老办法仍然可用(把 `storePath` 放在 Syncthing/Dropbox 里),但它**没有合并逻辑**:两边同时改, 最后落盘的那份赢,另一边丢失;删除也会在对方的下一次写里复活。真正跨机请用 `sync`。 ## 2b. 配了 `sync.dir`,另一台机器还是看不到我的改动? 按顺序查(`GET /api/global-todo/sync` 或 `/health.sync` 会给答案): | 现象(`last.error` / 状态) | 原因 | |---|---| | `sync-not-configured` | 那台机器的面板里没填共享目录,或 headless 部署的 `config.sync.dir` 没写 | | `sync-unavailable` | 上层目录不存在(共享盘没挂上)。插件**故意不替你建**这个目录,否则会安静地写进本地空目录 | | `sync-busy` | 上一次还没跑完(网络盘卡住时常见);等下一轮 | | `sync-timeout` | 单次文件操作超过 20 秒:挂载点僵死,检查 `mount` / 重挂 | | `sync-corrupt` | `todos.hub.json` 不是合法 JSON,或被别的程序改坏。它不会被覆盖:手工检查/移走后下一轮重建 | | 一切正常但另一台没变 | 两台看的是不是同一个目录(Windows 盘符映射 vs Linux 挂载点)?那台机器的插件是不是 ≥ 0.21.0? | ## 3. Agent 把一条标成完成之后会发生什么? 记录变成 `done`,同时带上 `review: { pending: true }`(`lib/model.js` 的 `completeTodo`)。 清单里两处可见:行上的「待确认」徽标,顶部的「待确认」筛选与计数。 | 你的回答 | 结果 | |---|---| | 确认完成 | 保持已完成,标记清除,动态里记下是谁确认的 | | 退回清单 | 回到 `open`;`doneAt` 和完成记录清空;动态里加一条 `reopened` | 派发出去的会话干净结束时,自动收尾也走同一条规则——自动完成同样是「agent 的声明」,一样要人回答。 ## 4. 已经有 steps 了,为什么不做子任务? 因为子任务就是另一条待办:要有自己的 id、状态、加入时间、附件、派发目标和完成记录。真做出来, 「一份清单」就变成了「一堆互相嵌套的清单」。 `steps` 明确是**一条工作里的计划**:扁平、最多 20 步、每步只有 `text` 和 `done`,派发时作为编号 计划原样交出去。需要各自派发的,就拆成多条待办——那才是清单该有的粒度。 ## 5. evidence(原话)是什么?为什么有的条目没有? 抓取时逐字引用的那一句用户原话,最多 300 字(`lib/model.js` 的 `LIMITS.evidence`)。 `global_todo_capture` 会要求模型带上,`global_todo_add` 也收。 面板详情里显示在「原话」下;Markdown 导出里是 `原话:` 一行。你在面板里手敲的条目没有这一项—— 它只服务于「抓来的东西有没有听错」这件事。 ## 6. 怎么从别的工具导进来? 三个门,按需要选: | 入口 | 形状 | 行为 | |---|---|---| | JSON 文档 | `{ "version": 2, "todos": [...], "trash": [...] }` | 面板「更多操作 → 导入」,或 `POST /api/global-todo/import`;面板用 merge(按记录合并:`updatedAt` 晚的赢,删除随 `trash` 传播,旧副本里的已删记录不会复活),`dryRun: true` 只返回 `wouldCreate` / `wouldUpdate` | | Markdown | `- [ ] 标题` 任务列表,缩进标注 `优先级 / 截止 / 标签 / 原话 / 描述 / 附件 / #` | 同上,选 `.md` 文件;按 `create` **新建**,没有 id 所以不会覆盖任何记录 | | 外部写入 | `POST /api/global-todo/ingest`,带 `Authorization: Bearer ` | 来源只接受 `mail / notification / announcement`;一次最多 100 条;置信度低于 `ingestConfirmBelow`(默认 0.8)的进收件箱 | CSV 没有直接入口:先转成 JSON 或 Markdown。Markdown 解析是容错的——不认识的行忽略,日期解析失败 就丢掉那一项,整份文件里一行任务都没有才会报错。 ## 7. 不用 Agent 也能用吗? 能。面板只依赖宿主的 web server 和那个 JSON 文件:增删改查、标签、优先级、截止、步骤、附件、 筛选、搜索、导出导入、提醒都不需要 Agent。`global_todo_*` 工具只是可选挂载面。 没有 Agent 参与时用不上的只有四件事:抓取、派发、Agent 的完成声明、会话回读。 ## 8. 哪些功能目前还没有? | 目前没有 | 现状 | |---|---| | 看板 / 日历视图 | 现在只有列表,加三种分组(对话 / 时间 / 级别);没有拖拽看板,没有日历 | | 重复任务 | `dueAt` 是单个时间点,还没有 recurrence 字段;同一截止只提醒一次,改了时间才会重新提醒 | | 子任务树 | 现在只有扁平的 `steps`,最多 20 条 | | 上传附件 | 现在只存引用(绝对路径或 URL),不复制字节 | | 多清单 / 项目 | 现在只有一份全局清单 | | 自动开工 | 现在没有;它是一份个人清单,不是任务队列 | | 桌面端面板 | 现在只有 Web 客户端 | | 账号 / 多用户 | 现在没有;只有一个本机(或受信网络)可访问的 HTTP 面 | ## 9. 两个进程同时写这个文件会怎样? **有锁,会排队。** 每次写之前,先在文件旁边独占建出 `todos.json.lock`(`open(…, 'wx')`),写完删掉: - 同一个进程里,人和 Agent 的写本来就走同一条串行链。 - 另一个宿主进程(Web 面板 + 桌面端 / 命令行会话,或两台机器共用一份同步盘)只能排队。轮到了就继续; 等不到 5 秒就报 `store-busy`(HTTP 503,工具返回 `{ ok: false, reason: "store-busy" }`), **不会**默默把你的改动覆盖掉。 - 持锁进程崩了也不会把清单锁死:锁文件里记着 `pid`、`host` 和一个随机 token。同一台机器上, 只有持有者进程确实不在了才算过期;跨机器没有 pid 可问,按 30 秒超龄接管。接管会写一条 warn 日志; 被接管过的锁,前一个持有者回来也不会误删新的那把。 - 读者永远不会看到半截文件(rename 是原子的)。清单在别处变过,面板仍然会提示。 有三件事它管不到,说清楚:**旧版本插件**(不拿锁)、**你手改文件**、**同步盘客户端在任意时刻写入**—— 这几种仍然是「后写覆盖先写」。所以把清单放进同步盘时,别让多个客户端同时同步它。 要并行又不想排队?那就各自一份文件,再用导出 / 导入合并。 ## 10. 提醒是怎么工作的? 宿主每 30 秒扫一次(`lib/index.js` 的 reminder tick):状态是 `open` / `doing`、`dueAt` 已过、 且这个 `id → dueAt` 还没报过的条目,进 `/health` 的 `reminders.due`,面板顶部显示成一条提醒条。 | 设置 | 默认 | 作用 | |---|---|---| | `remindersEnabled` | 开 | 关掉就不再扫、不再显示 | | `reminderLeadMinutes` | 0 | 提前几分钟报;0 = 到点才报 | 点「知道了」调 `POST /api/global-todo/reminders/ack`,只从**当前进程**的提醒列表里移除。 「报过了」的记忆也在内存里,所以重启宿主后,仍然到点且未完成的条目会再报一次。 浏览器通知是可选的:面板里点「开启系统通知」授权后,到点的条目会发一条系统通知。 ## 11. 怎么关掉自动获取(auto-collect)? 先分清三件不同的事: | 开关 | 默认 | 管什么 | 怎么关 | |---|---|---|---| | `autoCollect` | **关** | 插件自动去问一个安静了 30 分钟的对话「有没有要跟进的事」 | 面板设置里的「自动获取」开关,或 `POST /api/global-todo/settings { autoCollect: false }` | | `guidance` | 开 | 往 system prompt 里注入「主动记」的引导 | 插件配置 `guidance: false` | | 对话分组上的「豁免」 | 无 | 把某个**工作目录**整个排除在自动获取之外 | 再点一次取消 | 「抓一下待办」/「从本对话抓取」不在此列:那是你手动触发的,永远听你的。 ## 12. 怎么干净卸载? ```sh dsh plugin --profile remove dsh-global-todo ``` 然后重启宿主、刷新页面,侧栏入口消失。清单和设置都还在原处——这是刻意的。 要彻底清掉(先确认里面的 `todos.json` 你不要了): ```sh rm -r "$DSH_HOME/global-todo" ``` 更稳的顺序:先导出 JSON 或 Markdown,再删目录。重新装回来后,把导出的 JSON 用「更多操作 → 导入」 就能接着用。 ## 13. 收件箱和「待确认」有什么区别? | | 收件箱(Inbox) | 待确认(Review) | |---|---|---| | 谁产生的 | 外部写入(`/ingest`),置信度低于阈值 | Agent 的完成声明(工具或自动收尾) | | 问题是什么 | 「这条要不要收下」 | 「这活是不是真做完了」 | | 两个动作 | 确认(去掉 `inbox` 标签进清单)/ 丢弃(归档并去掉标签) | 确认完成 / 退回清单 | | 不处理会怎样 | 超过 7 天自动归档(宿主每 6 小时扫一次) | 一直挂着,行上一直有「待确认」 | ## 14. 派发失败最常见的原因? | 原因 | 表现与处理 | |---|---| | 没有工作目录 | 明确报错,因为没有目录的会话里 agent 做不了事;改用「开新对话」,或先选目录 | | 目标对话已归档 | 选择器里标「(已归档)」;换一个,或新开 | | 宿主没有 agents 服务 | `agents-unavailable`:精简组合里没有能接活的 agent | | `bound` 模式但没绑定 | 直接告诉你没有绑定会话,并建议改用 `auto` | 失败一定带原因,也记在详情的「最近派发」里;不会静默什么都不做。 ## 15. 桌面端报 `Plugin metadata … ENOENT … \locale` 是什么? **插件本身已经加载了,这是「显示文字」取不到**,不影响面板和工具。宿主读插件页标题/描述的顺序是: 先解析 `<包>/locale/en.json`(走 `exports` 里的 `./locale/*.json`),解析成功后**扫这个目录**, 把 `zh.json`/`en.json` 逐个读进来。目录不存在,扫描就抛 ENOENT,于是元信息报错。 也就是说:**你那份本地插件目录缺 `locale/` 文件夹**。常见于把仓库文件夹手动复制到 `%USERPROFILE%\.dsh\plugins-local\` 时只拷了 `lib`、`package.json`、`cordis.patch.yml`。 先确认到底缺什么,别靠猜: ```sh npx dsh-global-todo-doctor "$HOME/.dsh/plugins-local/全局待办插件" # Windows: npx dsh-global-todo-doctor "$env:USERPROFILE\.dsh\plugins-local\全局待办插件" ``` 它逐项检查 `package.json`、patch 文件、两个入口、`locale/`、图标与 `lib/`,缺哪样直接点出来, 缺必需项时退出码为 1(可以放进安装脚本里当门禁)。 修法二选一: 1. **推荐**:别用本地目录,直接装 npm 包(包里有 `locale/en.json`、`locale/zh.json`): ```powershell dsh plugin add dsh-global-todo ``` 2. 保留本地目录,把缺的补齐(`$src` 换成仓库或 npm 包里那份): ```powershell $dst = "$env:USERPROFILE\.dsh\plugins-local\全局待办插件" $src = "C:\path\to\dsh-global-todo" New-Item -ItemType Directory -Force "$dst\locale" | Out-Null Copy-Item "$src\locale\*.json" "$dst\locale\" -Force Copy-Item "$src\docs\icon.svg" "$dst\docs\" -Force # 图标同理会报错 ``` 然后重启宿主(插件元信息在启动时读一次)。 为什么会这么脆:`exports` 的 `./locale/*.json` 是**通配映射**,Node 解析它时不检查文件是否存在, 所以宿主以为文件在、去扫目录,才发现没有。完整安装不会触发;只有手工拷半份才会。 ## 16. 一直报 `store-busy` 怎么办? 说明有进程正持有 `.lock`。锁文件里记着 `pid`、`host`、`at`,按顺序看: 1. `host` 是本机、`pid` 还活着:真的在写,正常几毫秒就过去。一直卡住说明那个进程卡死了—— 重启它(或等它退出),下一次写入会按 pid 判死、立刻接管。 2. `host` 是本机、`pid` 已经不在了:下一次写入立刻接管并写一条 warn 日志,不用手动删。 3. `host` 是另一台机器:没有 pid 可问,按 30 秒超龄接管,最多等半分钟。两台机器时钟差得多时 先校准时钟,否则判断会偏。 4. 确认没有第二个宿主在用同一个 `storePath`(Web 面板 + 桌面端 / 命令行会话,或同步盘两端)—— 这是唯一会把「排队」变成「一直排队」的情况。 5. 兜底:确认没有任何进程在写之后,直接删掉那个 `.lock` 文件。它是缓存,不是数据,删了不会丢条目。 ## 17. 装了记忆库插件(dsh-mnemon),两边会不会记重复? 会,而且这是最常见的浪费。插件在检测到 `mnemonMemory` 服务时(也就是记忆插件真的装着)会多注入一段分工: - **要跟进的动作用待办**(`global_todo_*`);**已经知道的事实用记忆**(`mnemon_remember`)。同一件事只写一边。 - 待办标完成时,工具结果里会多一句「结论值得以后想起来就 `mnemon_remember` 一条,正文带上待办 id」, 所以「完成了」和「记住了」不是两套记录,而是一条线索。 - 从记忆里看到「还有件事要办」→ 用 `global_todo_add` 记成待办;记忆里的条目不会被画掉。 不装记忆插件时这段和那句都不出现:模型不会被引导去调一个不存在的工具。 **想更深一层**(让记忆检索直接召回待办)需要另写一个 `dsh-mnemon-source-global-todo`(用它的 Source SDK), 那是第二个包、带 peer 依赖,本仓库故意不做——这个插件必须能单独工作。 ## 18. Agent 记的条目为什么跑进了「收件箱」,而且一天只有 8 条进清单? 0.22.0 起,Agent 写进清单要过一道门(阈值由数据定,见 [README](../README.md) 的「自动记录的门槛」): | 情形 | 结果 | |---|---| | **没有截止时间**(`dueAt` 为空) | 进「收件箱」,等你点「收下」或「丢弃」 | | 有截止时间,但**今天已经进了 8 条** Agent 记录 | 进收件箱,原因写 `quota`(不丢,只是不在清单里) | | 有截止时间、额度还有 | 直接进清单 | | 面板里**你自己敲的** | 永远直接进清单 | | 升级到 0.22 那一刻 | 做一次清理:没有截止时间、且**你从没动过**的 Agent 记录移进收件箱(本机回放:68 条),之后再按新规则走 | 顺带两件事也变了: - **级别按截止时间算**:没有日期 = P3、7 天内 = P2、48 小时内 = P1、已逾期 = P0。你在面板上改过一次级别,这条就锁成你的(`prioritySource: human`),规则不再覆盖;Agent 当时想给的级别留在 `suggestedPriority` 里。 - **同一轮里 Agent 已经自己记过,就不再触发自动获取的提问**——它已经把要说的写下来了,再问一次只会多出没人要的条目。 收件箱里的记录**不算进「全部 / 未完成」的计数**,所以那两个数字现在就是清单本身。想把它放回清单,就「收下」;想彻底不要,就「丢弃」(归档,不删)。