dsh-zotero 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.en.md +43 -22
  2. package/README.md +65 -47
  3. package/lib/ask.js +2 -2
  4. package/lib/ask.js.map +1 -1
  5. package/lib/client.js +1754 -44
  6. package/lib/client.js.map +4 -4
  7. package/lib/config.d.ts +3 -0
  8. package/lib/config.d.ts.map +1 -1
  9. package/lib/config.js +1 -0
  10. package/lib/config.js.map +1 -1
  11. package/lib/constants.d.ts +9 -0
  12. package/lib/constants.d.ts.map +1 -1
  13. package/lib/constants.js +7 -0
  14. package/lib/constants.js.map +1 -1
  15. package/lib/contract.d.ts +18 -0
  16. package/lib/contract.d.ts.map +1 -1
  17. package/lib/contract.js +28 -3
  18. package/lib/contract.js.map +1 -1
  19. package/lib/normalize.d.ts +0 -1
  20. package/lib/normalize.d.ts.map +1 -1
  21. package/lib/normalize.js +10 -6
  22. package/lib/normalize.js.map +1 -1
  23. package/lib/presentation-meta.d.ts +256 -0
  24. package/lib/presentation-meta.d.ts.map +1 -0
  25. package/lib/presentation-meta.js +219 -0
  26. package/lib/presentation-meta.js.map +1 -0
  27. package/lib/prompt.d.ts +20 -3
  28. package/lib/prompt.d.ts.map +1 -1
  29. package/lib/prompt.js +33 -15
  30. package/lib/prompt.js.map +1 -1
  31. package/lib/provider-local.d.ts.map +1 -1
  32. package/lib/provider-local.js +2 -2
  33. package/lib/provider-local.js.map +1 -1
  34. package/lib/remote.d.ts +8 -1
  35. package/lib/remote.d.ts.map +1 -1
  36. package/lib/remote.js +24 -0
  37. package/lib/remote.js.map +1 -1
  38. package/lib/service.js +1 -1
  39. package/lib/service.js.map +1 -1
  40. package/lib/settings-namespace.d.ts +5 -5
  41. package/lib/settings-namespace.js +5 -5
  42. package/lib/tools/attachment.d.ts.map +1 -1
  43. package/lib/tools/attachment.js +2 -0
  44. package/lib/tools/attachment.js.map +1 -1
  45. package/lib/tools/export.d.ts.map +1 -1
  46. package/lib/tools/export.js +5 -1
  47. package/lib/tools/export.js.map +1 -1
  48. package/lib/tools/get.d.ts.map +1 -1
  49. package/lib/tools/get.js +2 -0
  50. package/lib/tools/get.js.map +1 -1
  51. package/lib/tools/retrieve.d.ts.map +1 -1
  52. package/lib/tools/retrieve.js +2 -0
  53. package/lib/tools/retrieve.js.map +1 -1
  54. package/lib/tools/search.d.ts.map +1 -1
  55. package/lib/tools/search.js +16 -21
  56. package/lib/tools/search.js.map +1 -1
  57. package/lib/typert.d.ts.map +1 -1
  58. package/lib/typert.js +5 -0
  59. package/lib/typert.js.map +1 -1
  60. package/package.json +10 -4
package/README.en.md CHANGED
@@ -1,9 +1,16 @@
1
- # dsh-zotero
1
+ <h1 align="center">dsh-zotero</h1>
2
2
 
3
3
  <p align="center">
4
4
  <b>English</b> · <a href="README.md"><b>中文</b></a>
5
5
  </p>
6
6
 
7
+ <p align="center">
8
+ <a href="https://awesome-dsh-plugin.com"><img src="https://awesome-dsh-plugin.com/badge.svg" alt="Awesome DSH Plugin"></a>
9
+ <img src="https://img.shields.io/npm/v/dsh-zotero" alt="npm version">
10
+ <img src="https://img.shields.io/npm/dm/dsh-zotero" alt="npm downloads">
11
+ <img src="https://img.shields.io/npm/l/dsh-zotero" alt="license">
12
+ </p>
13
+
7
14
  Let agents search, read, and cite your local [Zotero](https://www.zotero.org) library: find papers, browse notes and annotations, pull evidence by question, open the source document, generate citations.
8
15
 
9
16
  Describe what you need in a session and the Agent calls the tools below as needed. The only manual command is `/zotero status`.
@@ -44,20 +51,6 @@ The Agent moves down the ladder as a request deepens. A typical conversation:
44
51
 
45
52
  `/zotero status` reports connectivity, API/schema versions, and the database identity (Server ID, Zotero 10+). This is the only health check. Ordinary calls fail with typed domain errors.
46
53
 
47
- ## On-demand work and connectivity-failure interaction
48
-
49
- - The plugin is resident but strictly request-driven: loading, idling, and unloading never issue a request (no probes, no polling, no background work). Only two entry points touch Zotero: the five tools, invoked when the user explicitly asks about their library, and the explicitly invoked `/zotero status` command.
50
- - When a tool call fails with a connectivity error (`ZOTERO_NOT_RUNNING` not running / `ZOTERO_API_DISABLED` local API disabled / `ZOTERO_API_VERSION` unsupported version / `ZOTERO_TIMEOUT` timed out), the plugin asks the user how to proceed through an interactive question card: the first option is the recommended action marked `(Recommended)` (e.g. "I started Zotero, retry (Recommended)"); choosing it re-runs the same request once, and a second failure or the "Abort this query" choice surfaces the original typed error — never a second question.
51
- - Without an interactive provider (headless compositions), the ask is skipped and the typed error is returned as-is; a failing question mechanism never masks the original connectivity error.
52
-
53
- ## Limits
54
-
55
- - Read-only library: no path modifies items, notes, tags, or collections.
56
- - Full-text evidence depends on Zotero's index: `everything` search and `retrieve` full-text passages both require indexing.
57
- - Note-content search is a client-side scan: library/collection scopes and the first result page only, bounded by `maxNoteScanRecords`; notes beyond the cap never match.
58
- - Attachment depth depends on the harness composition: `zotero_attachment` returns the file location; reading that PDF further needs a matching file/PDF capability.
59
- - Evidence ranking is term-based relevance, not embedding or semantic search.
60
-
61
54
  ## Requirements
62
55
 
63
56
  - Zotero desktop with the local API enabled: **Settings → Advanced → "Allow other applications on this computer to communicate with Zotero"**.
@@ -126,16 +119,33 @@ All values are `Config` fields changeable from the bundle's `config` block (e.g.
126
119
  | `maxExportRefs` | `1000` | Upper bound for refs in one `zotero_export` call; keeps the request line under the server's HTTP header limit. |
127
120
  | `defaultStyle` | `apa` | CSL style for citation/bibliography formats. |
128
121
  | `defaultLocale` | `en-US` | CSL locale for citation/bibliography formats. |
122
+ | `webEnabled` | `true` | Enables the dedicated Zotero conversation tab; the gate is read once per page load. |
129
123
 
130
124
  ### Web configuration
131
125
 
132
- The plugin registers a "Zotero" card in dsh web's **Settings → Plugins → Plugin configuration** page listing all 19 fields above. The card binds the `zotero` settings namespace: writes land in the `zotero:` section of `$DSH_HOME/settings.yaml` (layered over the patch entry's `config`, user layer wins), and **saves apply live** — the transport and the provider rebuild on the new values, so the next tool call or `/zotero status` uses them without a dsh restart.
126
+ The plugin registers a "Zotero" card in dsh web's **Settings → Plugins → Plugin configuration** page listing all 20 fields above. The card binds the `zotero` settings namespace: writes land in the `zotero:` section of `$DSH_HOME/settings.yaml` (layered over the patch entry's `config`, user layer wins), and **saves apply live** — the transport and the provider rebuild on the new values, so the next tool call or `/zotero status` uses them without a dsh restart.
133
127
 
134
128
  - Invalid values (a non-loopback `baseUrl`, a non-positive limit) are refused before the write; the card reports the failed save and keeps the draft, and the plugin keeps running on the last valid value.
135
129
  - Every field shows its effective value; fields overridden by the settings document carry an "Overridden" badge and offer a one-click reset (clears the user layer, back to the patch entry value).
136
130
  - External edits to the settings document (e.g. editing `settings.yaml` directly) hot-apply too.
137
131
  - Compositions without a settings service (pure headless) never register the namespace, and the plugin behaves exactly as if unconfigured.
138
132
 
133
+ ### Web view
134
+
135
+ The dsh web session view is a tab ring (Chat, Trajectory, …). The plugin registers a dedicated **Zotero** tab (`conversation.view`, id `zotero`, after Trajectory and dsh-context) and leaves dsh's built-in chat and trajectory display untouched:
136
+
137
+ - A **connection strip** leads the tab: one status probe on mount, another per explicit Refresh (request-driven, no polling timers); it shows the connection state, API/schema versions, Server ID (Zotero 10+), and the last-checked time, with the diagnosis when Zotero is unavailable.
138
+ - Below it, the session's **Zotero tool activity**: every search, read, retrieve, attachment, and export call renders as a rich card (expandable, copyable refs, evidence passages labeled by source), fully replay-driven from the conversation snapshot — the same transcript renders the same cards, and missing meta degrades to the raw content.
139
+ - The **Web → Session tool cards** toggle in the settings page (`webEnabled`, default on) controls the tab's registration; the gate is read once per page load, so a toggle change applies after the page reloads. When off, Zotero calls show as dsh's built-in generic cards in the trajectory.
140
+
141
+ ## Limits
142
+
143
+ - Read-only library: no path modifies items, notes, tags, or collections.
144
+ - Full-text evidence depends on Zotero's index: `everything` search and `retrieve` full-text passages both require indexing.
145
+ - Note-content search is a client-side scan: library/collection scopes and the first result page only, bounded by `maxNoteScanRecords`; notes beyond the cap never match.
146
+ - Attachment depth depends on the harness composition: `zotero_attachment` returns the file location; reading that PDF further needs a matching file/PDF capability.
147
+ - Evidence ranking is term-based relevance, not embedding or semantic search.
148
+
139
149
  ## Development
140
150
 
141
151
  ### Commands
@@ -175,7 +185,14 @@ pnpm dsh web --patch ./dsh-zotero/dev.cordis.yml
175
185
 
176
186
  #### With the npm-installed dsh
177
187
 
178
- **Resident instance**: pack a tarball and install it into a profile. The plugin runs from the tarball copy; code updates require re-packing and re-installing. Verify with the production-stack smoke:
188
+ This plugin builds in two halves: the **Node side** (`lib/`, emitted by `tsc`, holds the service, tools, provider, and other logic) and the **browser side** (`lib/client.js`, emitted by `esbuild`, holds the dsh web configuration card and the Zotero tab view). The three flows below cover the common cases.
189
+
190
+ - `npm run build` emits both halves; `npm run build:client` rebuilds only the browser side.
191
+ - The rest of this section assumes `npm run build` has been run at least once so `lib/` exists.
192
+
193
+ **① Resident instance verification (tarball install)**
194
+
195
+ Pack a tarball and install it into a profile. The plugin runs from the tarball's built artifacts; code updates require re-packing and re-installing. Verify with the production-stack smoke after install:
179
196
 
180
197
  ```sh
181
198
  npm pack
@@ -184,19 +201,23 @@ cd ~/.dsh/profiles/<name>
184
201
  node --input-type=module < /path/to/dsh-zotero/scripts/smoke.mjs
185
202
  ```
186
203
 
187
- Run the smoke inside the profile directory, so bare imports resolve from the profile's flat `node_modules`. It verifies `status`, `search`, `get`, `retrieve`, `export`, the policy prompt section, and tool registration; `SMOKE PASS` indicates the packed plugin passes the installed-path checks.
204
+ The smoke must be run inside the profile directory, so bare imports resolve from the profile's flat `node_modules`. It verifies `status`, `search`, `get`, `retrieve`, `export`, the policy prompt section, and the registration of all five tools; `SMOKE PASS` indicates the packed plugin passes the installed-path checks.
205
+
206
+ **② Node-side hot-swap development**
188
207
 
189
- **Dev instance (hot swap)**: the `dev-lib.cordis.yml` overlay disables the profile's tarball copy (id `zotero`), inserts `zotero-dev` at this checkout's `lib/index.js`, and re-enables HMR. The production web profile disables loader HMR, and the HMR watch base sits in the profile directory, so the overlay sets `base` explicitly. When the build output changes, HMR disposes the old instance and re-constructs the plugin in the same process; dsh keeps running:
208
+ The `dev-lib.cordis.yml` overlay disables the profile's tarball row (id `zotero`), inserts a `zotero-dev` row pointing at this checkout's `lib/index.js`, and re-enables HMR. The production web profile disables loader HMR by default, and HMR's watch root lives in the profile directory, so the overlay sets `base` explicitly. When the build output changes, HMR disposes the old instance and reconstructs the plugin in the same process — no dsh restart needed:
190
209
 
191
210
  ```sh
192
- cd /Volumes/Work/deepseek-harness/dsh-zotero
211
+ cd ./dsh-zotero # from the deepseek-harness checkout
193
212
  npm run dev & # tsc --watch: rebuild lib on src changes
194
213
  dsh web --patch ./dev-lib.cordis.yml --port 3307
195
214
  ```
196
215
 
197
- Hot swap affects only the instance started with `--patch`; the resident instance keeps running the tarball version.
216
+ Hot swap only affects the instance started with `--patch`; the resident instance keeps running the tarball version, independently.
217
+
218
+ **③ Browser-side development**
198
219
 
199
- **Developing the browser half**: the web frontend only scans loader rows whose `name` is a bare package name (resolvable to `package.json`) the absolute-path row in `dev-lib.cordis.yml` has no browser half, so the card does not appear in the dev instance. To develop the card, install this checkout into the profile (`npm install <this repo path>` as a file: dependency, or pack and install the tarball), then pair `npm run dev:client` (esbuild watch) with the hot-swap overlay: browser-bundle changes make HMR re-fetch `/plugins/dsh-zotero/client.js`.
220
+ The web frontend only scans loader rows whose `name` is a bare package name (npm-resolvable to `package.json`) to load the browser-side bundle. `dev-lib.cordis.yml` uses an absolute-path row, which does not trigger browser-side loading, so the card does not appear in the dev instance. To develop the card, first install this checkout into the profile (`npm install <this repo path>` as a `file:` dependency, or pack and install the tarball), then pair `npm run dev:client` (esbuild watch) with the hot-swap overlay: browser-bundle changes make HMR re-fetch `/plugins/dsh-zotero/client.js`.
200
221
 
201
222
  ## License
202
223
 
package/README.md CHANGED
@@ -1,9 +1,16 @@
1
- # dsh-zotero
1
+ <h1 align="center">dsh-zotero</h1>
2
2
 
3
3
  <p align="center">
4
4
  <a href="README.en.md"><b>English</b></a> · <b>中文</b>
5
5
  </p>
6
6
 
7
+ <p align="center">
8
+ <a href="https://awesome-dsh-plugin.com"><img src="https://awesome-dsh-plugin.com/badge.svg" alt="Awesome DSH Plugin"></a>
9
+ <img src="https://img.shields.io/npm/v/dsh-zotero" alt="npm version">
10
+ <img src="https://img.shields.io/npm/dm/dsh-zotero" alt="npm downloads">
11
+ <img src="https://img.shields.io/npm/l/dsh-zotero" alt="license">
12
+ </p>
13
+
7
14
  让 Agents 搜索、阅读并引用你的本地 [Zotero](https://www.zotero.org) 文献库:找文献、查看笔记与批注、按问题取证、打开原文、生成引用。
8
15
 
9
16
  在会话里用自然语言描述需求,Agent 自动按需调用下面的工具;唯一的手动命令是 `/zotero status`。
@@ -44,20 +51,6 @@ Agent 按需求逐层深入,一段典型对话:
44
51
 
45
52
  `/zotero status` 报告连通性、API/schema 版本和数据库身份标识(Server ID,Zotero 10+)。这是唯一的健康检查。普通调用失败时返回带类型的领域错误。
46
53
 
47
- ## 按需工作与连接失败交互
48
-
49
- - 插件常驻但完全请求驱动:加载、闲置、卸载都不会发起任何请求(无探测、无轮询、无后台任务)。只有两种入口会触达 Zotero:Agent 在用户明确要求时调用五个工具,或用户手动执行 `/zotero status`。
50
- - 工具调用遇到连接类失败(`ZOTERO_NOT_RUNNING` 未运行 / `ZOTERO_API_DISABLED` 本地 API 被禁用 / `ZOTERO_API_VERSION` 版本过旧 / `ZOTERO_TIMEOUT` 超时)时,会通过交互式问题卡片询问用户怎么处理:第一个选项是带 `(Recommended)` 的推荐操作(英文文案,与错误消息一致,如 "I started Zotero, retry (Recommended)"),选择后插件按原参数重试一次;再失败或选择 "Abort this query" 时返回原类型化错误,绝不反复询问。
51
- - 无交互能力的环境(headless 组合、无 UI provider)自动降级:不询问,直接返回类型化错误。询问机制自身故障也绝不掩盖原始连接错误。
52
-
53
- ## 限制
54
-
55
- - 对文献库只读:没有任何修改条目、笔记、标签、分类等文献库数据的路径。
56
- - 全文证据依赖 Zotero 的全文索引:`everything` 搜索和 `retrieve` 的全文片段都以索引为前提。
57
- - 笔记正文搜索是插件侧补扫:仅库/分类范围、仅结果首页、受 `maxNoteScanRecords` 上限约束,超出上限的笔记不参与匹配。
58
- - 附件深度分析取决于当前 Harness 配置:`zotero_attachment` 返回文件位置,能否继续读取该 PDF 由 composition 里是否有相应文件/PDF 能力决定。
59
- - 证据排序是词项相关度检索,不是 embedding 或语义搜索。
60
-
61
54
  ## 环境要求
62
55
 
63
56
  - 已安装 Zotero 桌面版,并启用本地 API:**设置 → 高级 → “Allow other applications on this computer to communicate with Zotero”**。
@@ -105,36 +98,53 @@ allowBuilds:
105
98
 
106
99
  所有值都是 `Config` 字段,可在 bundle 的 `config` 块中修改(例如通过 `dsh plugin config`)。以下为默认值。
107
100
 
108
- | 字段 | 默认值 | 含义 |
109
- | ---------------------- | ---------------------------- | ------------------------------------------------------------------------- |
110
- | `baseUrl` | `http://127.0.0.1:23119/api` | 本地 API 基础 URL。仅支持纯回环 HTTP。 |
111
- | `provider` | `local` | 要选择的 provider id。 |
112
- | `timeoutMs` | `5000` | 每个请求的 provider 超时时间。 |
113
- | `maxSearchResults` | `20` | `zotero_search` `limit` 的上限。 |
114
- | `maxNoteScanRecords` | `200` | `zotero_search` 补扫笔记正文的笔记数量上限。 |
115
- | `maxEvidenceChars` | `6000` | 检索证据的总字符预算。 |
116
- | `maxEvidencePassages` | `4` | 证据片段数量的上限。 |
117
- | `maxDetailChars` | `3000` | `zotero_get` 摘要预览的字符预算。 |
118
- | `maxNoteBodyChars` | `30000` | `zotero_get` 返回 note 条目自身正文的字符预算。 |
119
- | `maxNoteChars` | `2000` | `zotero_get` 单条笔记预览的字符预算。 |
120
- | `maxNoteRecords` | `50` | `zotero_get` 返回笔记数量的上限。 |
121
- | `maxAnnotationRecords` | `100` | `zotero_get` 返回批注数量的上限。 |
122
- | `fulltextChunkWords` | `200` | 进入证据排序的全文片段词数。 |
123
- | `maxFulltextChars` | `250000` | 进入证据排序的全文大小上限。 |
124
- | `maxResponseBytes` | `16777216` | 每个 API 响应的流式字节上限。 |
125
- | `maxExportChars` | `1000000` | 导出输出的硬上限。不会中途截断。 |
126
- | `maxExportRefs` | `1000` | 单次 `zotero_export` 的 refs 数量上限,保护请求行不超服务器 HTTP 头限制。 |
127
- | `defaultStyle` | `apa` | 引用/参考文献使用的 CSL 样式。 |
128
- | `defaultLocale` | `en-US` | 引用/参考文献使用的 CSL locale。 |
101
+ | 字段 | 默认值 | 含义 |
102
+ | ---------------------- | ---------------------------- | -------------------------------------------------------------------------------------- |
103
+ | `baseUrl` | `http://127.0.0.1:23119/api` | 本地 API 基础 URL。仅支持纯回环 HTTP。 |
104
+ | `provider` | `local` | 要选择的 provider id。 |
105
+ | `timeoutMs` | `5000` | 每个请求的 provider 超时时间。 |
106
+ | `maxSearchResults` | `20` | `zotero_search` `limit` 的上限。 |
107
+ | `maxNoteScanRecords` | `200` | `zotero_search` 补扫笔记正文的笔记数量上限。 |
108
+ | `maxEvidenceChars` | `6000` | 检索证据的总字符预算。 |
109
+ | `maxEvidencePassages` | `4` | 证据片段数量的上限。 |
110
+ | `maxDetailChars` | `3000` | `zotero_get` 摘要预览的字符预算。 |
111
+ | `maxNoteBodyChars` | `30000` | `zotero_get` 返回 note 条目自身正文的字符预算。 |
112
+ | `maxNoteChars` | `2000` | `zotero_get` 单条笔记预览的字符预算。 |
113
+ | `maxNoteRecords` | `50` | `zotero_get` 返回笔记数量的上限。 |
114
+ | `maxAnnotationRecords` | `100` | `zotero_get` 返回批注数量的上限。 |
115
+ | `fulltextChunkWords` | `200` | 进入证据排序的全文片段词数。 |
116
+ | `maxFulltextChars` | `250000` | 进入证据排序的全文大小上限。 |
117
+ | `maxResponseBytes` | `16777216` | 每个 API 响应的流式字节上限。 |
118
+ | `maxExportChars` | `1000000` | 导出输出的硬上限。不会中途截断。 |
119
+ | `maxExportRefs` | `1000` | 单次 `zotero_export` 的 refs 数量上限,保护请求行不超服务器 HTTP 头限制。 |
120
+ | `defaultStyle` | `apa` | 引用/参考文献使用的 CSL 样式。 |
121
+ | `defaultLocale` | `en-US` | 引用/参考文献使用的 CSL locale。 |
122
+ | `webEnabled` | `true` | 是否在会话顶部显示 Zotero 专属标签页;开关在每次页面加载时读取,切换后需刷新页面生效。 |
129
123
 
130
124
  ### Web 配置
131
125
 
132
- 插件在 dsh web 的 **设置插件插件配置** 页注册了一张 "Zotero" 卡片,列出上表全部 19 个字段。卡片绑定 `zotero` 设置命名空间:写入的内容落在 `$DSH_HOME/settings.yaml` 的 `zotero:` 段(与补丁 entry `config` 叠层,用户段优先),**保存即热生效**——传输层与 provider 会按新值重建,下一个工具调用或 `/zotero status` 立即使用新配置,无需重启 dsh
126
+ 插件在 dsh web 的 **SettingsPluginsPlugin configuration** 页面注册一张 "Zotero" 卡片,列出上表全部字段。卡片绑定 `zotero` 设置命名空间:写入落在 `$DSH_HOME/settings.yaml` 的 `zotero:` 小节(叠加在 patch 条目 `config` 之上,用户层优先),**保存即时生效**——传输层与 provider 按新值重建,下一次工具调用或 `/zotero status` 无需重启 dsh 即可使用。
127
+
128
+ - 非法值(非回环 `baseUrl`、非正数上限)在写入前被拒绝;卡片提示保存失败并保留草稿,插件继续运行在最后一个合法值上。
129
+ - 每个字段显示有效值;被设置文档覆盖的字段带有 "Overridden" 徽标,提供一键重置(清除用户层,回到 patch 条目值)。
130
+ - 直接编辑设置文档(如手工修改 `settings.yaml`)同样热生效。
131
+ - 没有设置服务的组合(纯 headless)不会注册命名空间,插件行为与未配置时完全一致。
133
132
 
134
- - 非法值(如非回环的 `baseUrl`、非正整数的限制)在写入前被拒绝,卡片提示保存失败并保留草稿,插件继续运行于上一个合法值。
135
- - 每个字段显示当前生效值;被设置文档覆盖的字段带「已覆盖」标记,可一键恢复默认(清除用户段,回到补丁 entry 值)。
136
- - 设置文档被外部编辑(如直接改 `settings.yaml`)时同样会热生效。
137
- - 无 settings 服务的组合(纯 headless)不注册命名空间,插件行为与未配置时完全一致。
133
+ ### Web 视图
134
+
135
+ dsh web 的会话视图是标签页环(Chat、Trajectory、…)。插件注册一个专属 **Zotero** 标签页(`conversation.view`,id `zotero`,位于 Trajectory 与 dsh-context 之后),不触碰 dsh 自带的聊天与轨迹视图:
136
+
137
+ - 标签页顶部是**连接条**:挂载时探测一次、每次手动刷新再探测一次(请求驱动,无轮询定时器);显示连接状态、API/Schema 版本、Server ID(Zotero 10+)与上次检查时间;Zotero 不可用时显示诊断信息。
138
+ - 下方是本会话的 **Zotero 工具活动**:每次搜索、精读、取证、附件解析与导出调用都渲染为富卡片(可展开、ref 可复制、证据段落标注来源),完全由会话快照重放驱动——同一段记录永远渲染出同样的卡片,meta 缺失时降级为原始内容。
139
+ - 设置页的 **Web → 会话工具卡片** 开关(`webEnabled`,默认开启)控制标签页的注册;开关在每次页面加载时读取一次,切换后需刷新页面生效。关闭后,Zotero 调用在轨迹中显示为 dsh 内置的通用卡片。
140
+
141
+ ### 限制
142
+
143
+ - 只读文献库:没有任何路径会修改条目、笔记、标签或合集。
144
+ - 全文证据依赖 Zotero 的索引:`everything` 搜索与 `retrieve` 的全文段落都需要已建立索引。
145
+ - 笔记正文搜索是客户端扫描:仅限 library/collection 作用域与第一页结果,受 `maxNoteScanRecords` 限制;超出上限的笔记永远不会命中。
146
+ - 附件深度取决于宿主组合:`zotero_attachment` 返回文件位置;继续阅读该 PDF 需要宿主具备对应的文件/PDF 能力。
147
+ - 证据排序是基于词项的相关性,而非向量或语义检索。
138
148
 
139
149
  ## 开发
140
150
 
@@ -175,7 +185,11 @@ pnpm dsh web --patch ./dsh-zotero/dev.cordis.yml
175
185
 
176
186
  #### 使用 npm 安装的 dsh
177
187
 
178
- **常驻实例**:打包为 tarball 并安装到 profile,插件以 tarball 中的副本运行;代码更新需重新打包安装。安装后运行生产栈 smoke 验证:
188
+ 本插件分两部分构建:**Node 端**(`lib/`,由 `tsc` 生成,包含服务、工具、provider 等逻辑)与**浏览器端**(`lib/client.js`,由 `esbuild` 生成,包含 dsh web 的配置卡片与 Zotero 标签视图)。下面三种开发流程覆盖了不同场景。
189
+
190
+ **① 常驻实例验证(tarball 安装)**
191
+
192
+ 打包为 tarball 并安装到 profile,插件以 tarball 内的构建产物运行;代码更新后需重新打包安装。安装后通过生产栈 smoke 脚本验证:
179
193
 
180
194
  ```sh
181
195
  npm pack
@@ -184,19 +198,23 @@ cd ~/.dsh/profiles/<name>
184
198
  node --input-type=module < /path/to/dsh-zotero/scripts/smoke.mjs
185
199
  ```
186
200
 
187
- smoke 需在 profile 目录内运行,裸导入由此从 profile 的扁平 `node_modules` 解析。脚本依次验证 `status`、`search`、`get`、`retrieve`、`export`、策略提示词分区与五个工具注册;输出 `SMOKE PASS` 表示打包后的插件通过安装路径验证。
201
+ smoke 脚本必须在 profile 目录内运行,这样裸导入才能从 profile 的扁平 `node_modules` 中解析。脚本依次验证 `status`、`search`、`get`、`retrieve`、`export`、策略提示词分区,以及五个工具的注册情况;输出 `SMOKE PASS` 表示打包后的插件通过了安装路径验证。
188
202
 
189
- **开发实例(热替换)**:`dev-lib.cordis.yml` 覆盖层禁用 profile 中的 tarball 副本(id `zotero`),插入 `zotero-dev` 指向本仓库的 `lib/index.js`,并重新启用 HMR。生产 web profile 默认禁用 loader HMR,且 HMR 的监视根位于 profile 目录,因此覆盖层显式设置了 `base`。构建输出变化后,HMR 在同一进程内销毁旧实例并重新构造插件,无需重启 dsh:
203
+ **② Node 端热替换开发**
204
+
205
+ `dev-lib.cordis.yml` 覆盖层会禁用 profile 中的 tarball 行(id `zotero`),转而插入 `zotero-dev` 行指向本仓库的 `lib/index.js`,并重新启用 HMR。生产 web profile 默认关闭 loader HMR,且 HMR 的监视根位于 profile 目录,因此覆盖层显式设置了 `base`。构建产物变化后,HMR 会在同一进程内销毁旧实例并重新构造插件,无需重启 dsh:
190
206
 
191
207
  ```sh
192
- cd /Volumes/Work/deepseek-harness/dsh-zotero
208
+ cd ./dsh-zotero # 从 deepseek-harness checkout 进入本仓库
193
209
  npm run dev & # tsc --watch:修改 src 后自动重建 lib
194
210
  dsh web --patch ./dev-lib.cordis.yml --port 3307
195
211
  ```
196
212
 
197
- 热替换仅作用于通过 `--patch` 启动的实例;常驻实例继续运行 tarball 版本。
213
+ 热替换仅对通过 `--patch` 启动的实例生效;常驻实例仍运行 tarball 版本,互不影响。
214
+
215
+ **③ 浏览器端开发**
198
216
 
199
- **浏览器半边的开发**:Web 端只扫描 Loader `name` 为裸包名(npm 可解析到 `package.json`)的条目——`dev-lib.cordis.yml` 的绝对路径行不会加载浏览器半边,因此卡片不会出现在 dev 实例中。开发卡片时把本仓库装进 profile(`npm install <本仓库路径>` 作为 file: 依赖,或 `npm pack` 后安装 tarball),再配合 `npm run dev:client`(esbuild watch)与热替换 overlay:浏览器 bundle 变化会触发 HMR 重新拉取 `/plugins/dsh-zotero/client.js`。
217
+ dsh web 只会扫描 Loader 行中 `name` 为裸包名(npm 能解析到 `package.json`)的条目来加载浏览器端 bundle。`dev-lib.cordis.yml` 使用的是绝对路径行,不会触发浏览器端加载,因此卡片不会出现在 ② 的 dev 实例中。开发卡片时需要先把本仓库装进 profile(`npm install <本仓库路径>` 作为 `file:` 依赖,或 `npm pack` 后安装 tarball),再配合 `npm run dev:client`(esbuild watch)与热替换 overlay 一起使用:浏览器 bundle 变化会触发 HMR 重新拉取 `/plugins/dsh-zotero/client.js`。
200
218
 
201
219
  ## 许可证
202
220
 
package/lib/ask.js CHANGED
@@ -30,7 +30,7 @@ const FAILURE_SPECS = {
30
30
  [ZOTERO_NOT_RUNNING]: {
31
31
  header: 'Zotero is not running',
32
32
  question: 'Zotero is not running, so I cannot read your library. What should I do?',
33
- detail: 'Start Zotero and check "Allow other applications on this computer to communicate with Zotero" under Settings → Advanced.',
33
+ detail: 'Start Zotero, then in Settings → Advanced check "Allow other applications on this computer to communicate with Zotero".',
34
34
  retryLabel: 'I started Zotero, retry (Recommended)',
35
35
  retryDescription: RETRY_DESCRIPTION,
36
36
  abortLabel: ABORT_LABEL,
@@ -39,7 +39,7 @@ const FAILURE_SPECS = {
39
39
  [ZOTERO_API_DISABLED]: {
40
40
  header: 'Zotero local API is disabled',
41
41
  question: 'Zotero is running but rejected the local API request (403).',
42
- detail: 'Check "Allow other applications on this computer to communicate with Zotero" under Zotero Settings → Advanced.',
42
+ detail: 'In Zotero Settings → Advanced, check "Allow other applications on this computer to communicate with Zotero".',
43
43
  retryLabel: 'I enabled the local API, retry (Recommended)',
44
44
  retryDescription: RETRY_DESCRIPTION,
45
45
  abortLabel: ABORT_LABEL,
package/lib/ask.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ask.js","sourceRoot":"","sources":["../src/ask.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,YAAY,EAAuB,MAAM,wBAAwB,CAAA;AAO1E,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,WAAW,GACZ,MAAM,aAAa,CAAA;AAEpB,kFAAkF;AAClF,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,kBAAkB;IAClB,mBAAmB;IACnB,kBAAkB;IAClB,cAAc;CACN,CAAA;AAkBV,MAAM,WAAW,GAAG,kBAAkB,CAAA;AACtC,MAAM,iBAAiB,GAAG,kEAAkE,CAAA;AAC5F,MAAM,iBAAiB,GAAG,gDAAgD,CAAA;AAE1E,MAAM,aAAa,GAAuC;IACxD,CAAC,kBAAkB,CAAC,EAAE;QACpB,MAAM,EAAE,uBAAuB;QAC/B,QAAQ,EAAE,yEAAyE;QACnF,MAAM,EACJ,0HAA0H;QAC5H,UAAU,EAAE,uCAAuC;QACnD,gBAAgB,EAAE,iBAAiB;QACnC,UAAU,EAAE,WAAW;QACvB,gBAAgB,EAAE,iBAAiB;KACpC;IACD,CAAC,mBAAmB,CAAC,EAAE;QACrB,MAAM,EAAE,8BAA8B;QACtC,QAAQ,EAAE,6DAA6D;QACvE,MAAM,EACJ,gHAAgH;QAClH,UAAU,EAAE,8CAA8C;QAC1D,gBAAgB,EAAE,iBAAiB;QACnC,UAAU,EAAE,WAAW;QACvB,gBAAgB,EAAE,iBAAiB;KACpC;IACD,CAAC,kBAAkB,CAAC,EAAE;QACpB,MAAM,EAAE,wBAAwB;QAChC,QAAQ,EAAE,oFAAoF;QAC9F,MAAM,EAAE,iEAAiE;QACzE,UAAU,EAAE,wCAAwC;QACpD,gBAAgB,EAAE,iBAAiB;QACnC,UAAU,EAAE,WAAW;QACvB,gBAAgB,EAAE,iBAAiB;KACpC;IACD,CAAC,cAAc,CAAC,EAAE;QAChB,MAAM,EAAE,kBAAkB;QAC1B,QAAQ,EAAE,iFAAiF;QAC3F,MAAM,EAAE,kDAAkD;QAC1D,UAAU,EAAE,qBAAqB;QACjC,gBAAgB,EAAE,6BAA6B;QAC/C,UAAU,EAAE,WAAW;QACvB,gBAAgB,EAAE,iBAAiB;KACpC;CACF,CAAA;AAED,SAAS,eAAe,CAAC,IAAY;IACnC,OAAQ,gBAAsC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;AAC/D,CAAC;AAED,SAAS,UAAU,CAAC,IAAiB;IACnC,OAAO;QACL,EAAE,EAAE,gBAAgB;QACpB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,IAAI,CAAC,gBAAgB,EAAE;YAC9D,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,IAAI,CAAC,gBAAgB,EAAE;SAC/D;KACF,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,GAAY,EACZ,IAAyB,EACzB,GAAqB;IAErB,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,EAAE,CAAA;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,MAAM,KAAK,CAAA;QAChF,MAAM,SAAS,GAAG,GAAG,CAAC,GAAG,CAAC,eAAe,CAAoC,CAAA;QAC7E,IAAI,SAAS,KAAK,SAAS;YAAE,MAAM,KAAK,CAAA;QACxC,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACtC,IAAI,MAA6B,CAAA;QACjC,IAAI,CAAC;YACH,MAAM,OAAO,GAA2B;gBACtC,SAAS,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;gBAC7B,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC1D,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB,CAAA;YACD,MAAM,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,sEAAsE;YACtE,kDAAkD;YAClD,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO;gBAAE,MAAM,IAAI,YAAY,CAAC,mBAAmB,EAAE,YAAY,CAAC,CAAA;YACnF,MAAM,KAAK,CAAA;QACb,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,gBAAgB,CAAC,CAAA;QAC9E,MAAM,QAAQ,GAAG,UAAU,EAAE,QAAQ,IAAI,EAAE,CAAA;QAC3C,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC;YAAE,MAAM,KAAK,CAAA;QACpD,yEAAyE;QACzE,OAAO,MAAM,GAAG,EAAE,CAAA;IACpB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"ask.js","sourceRoot":"","sources":["../src/ask.ts"],"names":[],"mappings":"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"}