Skip to content

Releases

v0.18.9 - 修论文会话上下文串台

Choose a tag to compare

@toustifer toustifer released this 28 Sep 04:15

修复论文会话上下文串台

切换论文后,顶部当前论文已经是新论文,但具体会话里还带着上一篇论文的上下文。根因是「按工作区取最近会话」时只按 cwd 过滤,没有限制会话必须属于当前论文;于是同一个 Active SLAM 工作区里的其他论文会话可能被复用。

现在只复用 zotero-paper-<当前条目 key>(以及它的 -2、-3 实例),不会再复用工作区里别的论文或泛用会话。没有当前论文专属会话才新建。

复用会话时如果 chat store 没登记或没有上下文时间戳,会重新注入当前论文的 meta 上下文;并同步更新本地 chat store。

另外,Zotero 侧现在会把 PDF 附件 key 归一化为父条目 key,host 侧也会再次兜底。这样标题、Zotero key、PDF 和当前会话始终属于同一篇论文。

实测当前论文 99L7JHSL 的模型回复为:
Active view planning for visual SLAM in outdoor environments based on continuous information modeling / 99L7JHSL。

这次还重新构建了 host 与 client 两端;之前只跑 host build 会导致前端轮询代码没有进入 lib/client.js。

v0.18.8 - 修「会话没挂进工作区」

Choose a tag to compare

@toustifer toustifer released this 28 Sep 02:27

修了什么

「打开会话」按下去以后,DSH 停在「选择工作区开始」——会话切过去了,但界面认为它不属于任何工作区。

根因:sessionController.create() 只在拿到 workspaceId 时才会调 workspace.attachSession();只传 cwd 的话,会话 header 里的 cwd 是对的,但它不在任何工作区的 sessionIds 里,于是侧栏看不到、切过去也停在「选择工作区」。两者还不能同时传(gateway/bad-request)。之前的实现传的正是 cwd。

现在:

  • 会话走 workspaceId 创建,挂载由 controller 自己完成;
  • 已经存在的会话也补挂一次(早先的会话只带 cwd、没进过 sessionIds),attachSession 对已挂的是幂等的。补挂也走 sessionController.create —— 它在会话已存在时是 adopt 语义,而 workspaceController 根本不暴露 get,拿不到实体就调不了 attachSession。

实测 workspace.json 里从 sessions=[] 变成 sessions=[zotero-paper-99L7JHSL],切过去之后输入框上方的选择器显示的是那篇论文的工作区,不再是「选择工作区」。

顺带两处提速

browsingContext 就绪等待从常量 500ms 改成每 20ms 探一次(上限 1s)。 以前是一刀切 sleep,而实测通常几十毫秒就好了——切换论文时这段是纯白等。

URL 解析加 30 秒缓存。 切换论文会重建整个 pane,每次都要读一遍日志尾部取 token,而日志可能几 MB、token 在一次实例生命周期里却是不变的。30 秒恰好覆盖「连续切几篇」,又不会把重启后的新 token 拖太久。

清掉的历史残留

<DshHome>/sessions/--C-Users-15775-.dsh-zotero--/ 下躺着 4 个 cwd 指向 DSH_HOME 的旧会话(zotero-paper-7R47AUHQ / CUILYLGY / GIHHY4ZF / GIHHY4ZF-2)。它们的 cwd 永远对不上任何论文工作区,所以永远挂不上、也永远不会被「按论文找会话」认领。整个目录挪到了 sessions-orphaned/,没有删除。

v0.18.7 - 选段带的是附件 key

Choose a tag to compare

@toustifer toustifer released this 28 Sep 02:15

那次会话为什么慢

一轮「这句话什么意思」,实际打了 11 次工具调用,其中 5 次是白打的:

调用 结果 原因
zotero_get_item CUILYLGY 条目不存在 key 是 PDF 附件,不是论文条目
zotero_retrieve CUILYLGY 条目不存在 同上
zotero_health fetch failed 当时 Zotero 没开(现在正常)
glob **/* 空 论文工作区目录本来就是空的
search_fulltext ×3 error MCP 那侧的抖动,重试也没好

主因:选段送的是附件 key

Zotero 阅读器的 selection 事件里的 reader.itemID 指的是正在读的那个 PDF 附件,不是论文条目。插件直接把它的 key 当论文 key 送了出去,于是选段文件里写着 Zotero key:CUILYLGY——那是附件,条目其实是 99L7JHSL。模型拿着附件 key 去查条目,两次都只能得到「条目不存在」,然后才改走 MCP 那条路。

两头都修了:

  • Zotero 侧顺着 parentItem 换成条目再送,附件 key 另存一栏(要精确定位到哪个 PDF 时还有用)。
  • client.getItem 拿到附件也会跟一步 parentItem。这一层是兜底——历史选段、模型自己从别处拼出来的附件 key,都能用。

实测:GET /api/item?key=CUILYLGY 现在返回 found=true, key=99L7JHSL, title=Active view planning for visual SLAM...。

另外两处

科研预设里补了两条:选段的 key 现在是条目 key、工具会自动跟到父条目不用自己查;以及论文工作区目录一开始是空的,那是放自己产出的地方,别用 glob 去里面翻上下文——论文正文只在 Zotero 里。

还没动的

search_fulltext 的连续失败来自 zotero-mcp-plugin 那一侧,不是这套插件;zotero_health 那次失败只是 Zotero 没启动,现在 23119 正常应答。

v0.18.6 - 修面板卡在「加载中」

Choose a tag to compare

@toustifer toustifer released this 28 Sep 02:08

修了什么

面板有时会卡在「加载中...」,DSH 区域一片空白。

根因是两个 load() 在并发跑。Zotero 会在条目切换、面板重排时反复调 renderPane,每次都起一次嵌入流程;而 unmountEmbedded 是个全局清扫——它按 browser[data-zotero-dsh-embed] 把文档里所有嵌入浏览器都删掉,包括另一个正在跑的实例刚建的那个。于是两边都拿不到标题,各自跑满 5 轮 probe,最后那次再把状态栏写回「加载中」。

日志里的样子是一堆交织的 probe 编号(probe#0 #1 #2 #3 混着出现),最后跟一条 embed final title=""。

现在每次 load() 发一个代号:

  • 旧的在每个 await 点检查自己是否已被取代,是就把自己建的 browser 摘掉、直接返回;
  • 被取代的那次不许再写状态栏 —— 它会把后来者刚写好的「已连接」盖回「加载中」。

部署后 Mac 的日志立刻干净了:embed gen=1 superseded before load 紧跟着 embed final title="DeepSeek Harness"。

升级

两端 Zotero 都要更新 zotero-dsh.xpi。

v0.18.5 - 面板加「打开会话」

Choose a tag to compare

@toustifer toustifer released this 28 Sep 01:56

新增:面板上的「打开会话」

Zotero 面板顶部多了一个按钮,和「为这篇论文建工作区」并排。点它直接跳到这篇论文工作区里最近用过的那个会话;没有就现建一个再跳过去。

之前从 Zotero 想看这篇的会话,得先在侧栏里找到这篇论文的工作区、展开、再点会话行——三步。现在一步。

怎么实现的

Zotero 是另一个进程,够不到浏览器里的界面状态。所以分两段:

  1. POST /papers/open-session 在服务端算这篇论文的工作区目录(和 papers/workspace 共用同一个 resolvePaperDir,两边不会分叉),ensure 出工作区,在 sessionPersistence.list() 里挑 cwd 等于它的会话取最新的那个,没有就按 zotero-paper-<itemKey> 建一个。然后把 id 存起来。
  2. DSH 前端每 2 秒轮询 GET /pending-open,拿到 id 就调 sessions.open(id)——和点侧栏会话行走的是同一条路。取走即清,否则下一次轮询会把用户从当前会话里再拽出去一次。

轮询只在 document.visibilityState === 'visible' 时发请求:这个页面常年挂在 Zotero 侧栏里,切到后台还在打自己的 HTTP 纯属浪费。

用 list() 而不是去猜 sessions/ 下的目录名——那个名字是 cwd 的自定义转义(~5B66 这类),反推规则等于把一个内部实现抄进插件。

顺带

paperWorkspace 里算目录的那段抽成了 resolvePaperDir,两个端点共用。各算一遍的话,迟早会在某个集合结构下分叉,然后就出现「工作区在这儿、会话在那儿」的错位。

v0.18.4 - 当前论文上下文 + 科研预设

Choose a tag to compare

@toustifer toustifer released this 27 Sep 17:02

这一版解决什么

之前的 dsh-zotero 只会「你点一下,它读一下」:模型不知道你此刻在 Zotero 里选中哪篇,也不知道那篇的 PDF 落在磁盘哪里。你得先把论文送进去,它才开始工作 —— 否则它只能反问,或者更糟:拿记忆里的论文硬答。

这一版把这件事变成每一轮都成立的上下文:

  • Zotero 半边在选中变化时(节流 1.5 秒)把当前条目的标题、作者、年份、所在集合、PDF 磁盘路径、当前页码推给 DSH 的 POST /focus。
  • DSH 半边用 systemPrompt.context 注册一个 zotero:focus 段,文本在每次组装时重新求值。换一篇选中的论文,模型下一步就看到了 —— 不需要点按钮,也不用重开会话。
  • 没选中任何东西时返回空串,组装层会把空段丢掉,所以不会往提示词里塞垃圾。超过两小时没动过也当作你已经离开那篇。

实测(模型回答原文):

以上全部来自本轮运行时上下文里的「当前论文 · 用户在 Zotero 里正选中这一条」那一段,我没有调用任何工具,也没有去文件系统核实这个路径。

那条 PDF 路径本身是给你核对用的,按规矩我不能拿它当读取入口 —— 要读正文我得走 zotero_read_pdf / zotero_read_fulltext / zotero_retrieve。

科研预设

包里多了一份 presets/research/:一个 DSH agent preset(「科研模式」),装配照抄内置的 standard,换掉人格段落。

人格里写死了三件事,都是文献工作里代价最高的失误点:

  1. Zotero 资料一律走 zotero_ 工具*,不许用文件系统去翻 Zotero 的存储目录 —— 那些路径是内部实现,翻到 PDF 也不知道它对应哪条。
  2. 引用纪律:转述必须能指回章节名 / offset / 批注 key;原文、批注、一般知识三者不要混;没读到就说没读到,不许按标题"补全"正文。
  3. 调研走 harvest 四步(scout → extract → verify → audit),不许用一次网页搜索当结论。

安装脚本会把它拷进 <DshHome>/.agent-presets/research,已存在就不覆盖 —— 那是给人改的东西。

安装

macOS / Linux:

bash install-dsh.sh

Windows:

pwsh -File install-dsh.ps1

两端的 Zotero 插件(zotero-dsh.xpi)也要更新 —— 选中项的推送在那一半。

附带修掉的

  • zotero-dsh 的 version 常量不再硬编码。之前升级后日志里还打着旧版本号,看日志的人会误判"新代码没生效"(我自己就被骗过一次)。
  • 界面文案改写器的占位符替换漏了根元素本身 —— React 新挂进来的 composer 元素自带 data-placeholder,而 querySelectorAll 只查后代,于是新会话永远显示旧占位符。

v0.18.1 - 修好 Zotero 10 上的面板嵌入

Choose a tag to compare

@toustifer toustifer released this 27 Sep 16:18

修了什么

Zotero 10 上面板一直显示"加载失败",且没有任何可用报错。

根因:Zotero 10 的 PageData actor 删掉了 loadURI 消息(只剩 prepareLoad)。于是 sendQuery("loadURI") 不再抛错,而是安静地 resolve 成 undefined —— 浏览器停在 about:blank,代码却以为导航已提交。

改成 Zotero 10 自己的做法(见 chrome/content/zotero/HiddenBrowser.mjs):

  1. PageData.sendQuery("prepareLoad")
  2. 父进程 browser.loadURI(Services.io.newURI(url), { triggeringPrincipal: getSystemPrincipal() })

旧的 sendQuery("loadURI") 保留为回退,Zotero 9 行为不变。

顺带修掉的两个坑

register-zotero-addon.py 现在会检测 Zotero 是否在运行并拒绝写入。Zotero 退出时会用内存里的插件表覆写 extensions.json,运行期间写进去的记录会被静默抹掉 —— 现象是"装的时候一切正常,重启后插件没加载",很难查。(macOS 上别用 pgrep -x Zotero 判断:Zotero 的进程名是小写 zotero,带 -x 的精确匹配是假阴性。)

注册的版本号改为从 xpi 的 manifest.json 读取,不再和脚本里的常量两处手改 —— 那两处迟早会漂。

安装

macOS / Linux:

bash install-dsh.sh

Windows:

pwsh -File install-dsh.ps1

zotero-dsh v0.18.0 — PDF 选区送进 DSH

Choose a tag to compare

@toustifer toustifer released this 27 Sep 10:12

装什么

文件 装到哪
zotero-dsh.xpi Zotero 7 / 9
dsh-zotero-0.1.1.tgz + install-dsh.ps1 DSH

Zotero 侧:工具 → 插件 → 齿轮 → Install Plugin From File… → 选中 xpi → 重启。

DSH 侧:两个文件放同一目录,pwsh -File install-dsh.ps1,然后重启 DSH。脚本只解包、建链、补一行锚点。

前提

  • Zotero 7 或 9
  • 一个跑得起来的 DSH Web 实例(npm i -g @deepseek-ai/dsh,然后 dsh web)
  • Zotero 的本地接口要打开:设置 → 高级 → 勾选 Enable Local API。
    不开的话 zotero_* 系列工具全部报 "Local API is not enabled"

这一版

Zotero 侧 0.18.0(新)

  • PDF 选区送进 DSH:在 Zotero 阅读器里选中文字,气泡里出现「送入 DSH」和「问 DSH」。
    会话按 itemKey 懒开,不必先打开面板;首次送入选段会自带论文元数据。
    走官方的 renderTextSelectionPopup 事件,不劫持原生行为
  • 地址解析:base 取自偏好项、token 每次从实例日志现取(DSH 重启不再把面板打成 401)
  • 挂载后回读标题落地确认;修正指向死仓库的 update_url

DSH 侧 0.1.1

  • 新增 POST /quote:选段 → 该论文会话,ask:true 时追加一轮解读
  • 论文 ↔ 工作区映射(区/组两级);12 个 zotero_* 工具 + 右侧栏面板

已知问题

  • pnpm install 在干净机器上会 404(依赖 DSH 内部未发布的 @deepseek-ai/dsh-type-meta),
    所以直接发预构建产物
  • 选区按钮的呈现由 Zotero 的气泡渲染决定,主题差异下样式可能不完美

zotero-dsh v0.17.0 — 预构建安装包

Choose a tag to compare

@toustifer toustifer released this 27 Sep 09:57

装什么

文件 装到哪
zotero-dsh.xpi Zotero 7
dsh-zotero-0.1.1.tgz + install-dsh.ps1 DSH

Zotero 侧:工具 → 插件 → 齿轮 → Install Plugin From File… → 选中 xpi → 重启。

DSH 侧:把 tgz 和 ps1 下到同一目录,跑 pwsh -File install-dsh.ps1,然后重启 DSH。
脚本只解包、建链、补一行加载锚点,不编译。

前提

  • Zotero 7
  • 一个跑得起来的 DSH Web 实例(npm i -g @deepseek-ai/dsh,然后 dsh web)

这一版

Zotero 侧 0.17.0

  • 地址解析改成 base 取自偏好项、token 每次都从实例日志现取。
    旧实现信任偏好项里的 token,DSH 一重启面板就停在 401 页
  • 候选日志按优先级扫:~/.dsh-zotero/web-3081.log → ~/.dsh/restart-web.stdout.log → ~/.dsh/web.stdout.log
  • 挂载后回读页面标题,401 也能导航成功,所以要落地确认
  • 修掉指向不存在仓库的 update_url,转为从本仓库自更新

DSH 侧 0.1.1

  • 论文 ↔ 工作区映射(区/组两级,组映射到真实目录)
  • 12 个 zotero_* agent 工具 + 右侧栏面板
  • MinerU 全文解析、标注回写、证据检索

已知问题

  • pnpm install 在干净机器上会 404(依赖 DSH 内部的 @deepseek-ai/dsh-type-meta,
    该包未发布)。所以这一版直接发预构建产物,源码构建需要先有 DSH 环境
  • Zotero 侧更新检查依赖 packages/zotero-addon/updates.json,首次发版后生效