bookmark-research 0.5.0-beta.6 → 0.5.0-beta.8

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.
@@ -9,7 +9,7 @@
9
9
  "source": {
10
10
  "source": "npm",
11
11
  "package": "bookmark-research",
12
- "version": "0.5.0-beta.5",
12
+ "version": "0.5.0-beta.8",
13
13
  "registry": "https://registry.npmjs.org"
14
14
  },
15
15
  "policy": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bookmark-research",
3
- "version": "0.5.0-beta.6",
3
+ "version": "0.5.0-beta.8",
4
4
  "description": "Check bookmark URLs or Canvas packages, compare sources with host-led research, and retain verifiable reports.",
5
5
  "author": {
6
6
  "name": "kwenxu"
package/README.md CHANGED
@@ -22,13 +22,13 @@ Works in **Codex, Claude Code, Pi and DSH (DeepSeek Harness)**.
22
22
 
23
23
  ## Install
24
24
 
25
- **Current beta (`0.5.0-beta.6`)**, interactive on macOS, Linux and Windows:
25
+ **Current beta (`0.5.0-beta.8`)**, interactive on macOS, Linux and Windows:
26
26
 
27
27
  ```sh
28
- npx bookmark-research@0.5.0-beta.6 install --source npm:bookmark-research@0.5.0-beta.6
28
+ npx bookmark-research@0.5.0-beta.8 install --source npm:bookmark-research@0.5.0-beta.8
29
29
  ```
30
30
 
31
- This pins both the installer and the content. The `@latest` examples below follow the default channel, which remains `0.5.0-beta.3`; `@beta` currently points to `0.5.0-beta.6`. Changing only the npx selector does not change the installer's default content source.
31
+ This pins both the installer and the content. The `@latest` examples below follow the default channel, which remains `0.5.0-beta.3`; `@beta` currently points to `0.5.0-beta.8`. Changing only the npx selector does not change the installer's default content source.
32
32
 
33
33
  **Default-channel interactive installation:**
34
34
 
@@ -129,7 +129,7 @@ A bundle of one Skill, one local MCP server, and the remote MCPs and services th
129
129
  | Layer | Component | Runs as | Setup |
130
130
  | --- | --- | --- | --- |
131
131
  | Skill | [`bookmark-research`](skills/bookmark-research/SKILL.md) and 11 method references, each with a [Chinese copy](skills/bookmark-research/references/zh/skill-guide.md) | Loaded by the client | None |
132
- | Local MCP | `bookmark-research`, 37 tools (below) | Python process started by the client | Installed for you |
132
+ | Local MCP | `bookmark-research`, 40 tools (below; the list grows with capabilities) | Python process started by the client | Installed for you |
133
133
  | Remote MCPs, called by the local MCP | [Exa](https://exa.ai) (search, page reading), [Parallel](https://parallel.ai) (search, fallback reading), [Tavily](https://tavily.com) (search fallback) | HTTPS, no local process | Optional API keys |
134
134
  | HTTP service, called by the local MCP | [Jina Reader](https://jina.ai/reader) (fallback reading; search with a key) | HTTPS | Optional `JINA_API_KEY` |
135
135
  | Optional client MCPs | Exa Agent, Parallel Task MCP, Tavily Research | Added to your client by you | `bookmark-research setup` shows the steps |
@@ -138,12 +138,12 @@ Local MCP tools:
138
138
 
139
139
  | Group | Tools |
140
140
  | --- | --- |
141
- | Bookmarks | `sync_package`, `index_status`, `source_history`, `search_bookmarks`, `get_context` |
141
+ | Bookmarks | `sync_package`, `index_status`, `source_history`, `source_remove`, `source_merge`, `search_bookmarks`, `get_context` |
142
142
  | Web | `search_web`, `fetch_web`, `search_archive`, `search_providers` |
143
143
  | Research | `research_readiness`, `research_start`, `research_status`, `research_search`, `research_fetch`, `research_source`, `research_record`, `research_inventory`, `research_coverage`, `research_import_evidence`, `research_finish`, `research_route` |
144
144
  | Research services (optional) | `research_services`, `research_service_prepare`, `_start`, `_status`, `_result`, `_cancel`, `_attach`, `_import` |
145
145
  | Settings | `get_settings`, `update_settings` |
146
- | Wiki and evaluation | `wiki_write`, `wiki_get`, `wiki_list`, `wiki_search`, `wiki_lint`, `evaluate_research` |
146
+ | Wiki and evaluation | `wiki_write`, `wiki_get`, `wiki_list`, `wiki_search`, `wiki_lint`, `wiki_acknowledge`, `evaluate_research` |
147
147
 
148
148
  Tool descriptions: [details](docs/details.en.md#mcp-tools).
149
149
 
package/README.zh.md CHANGED
@@ -22,13 +22,13 @@
22
22
 
23
23
  ## 安装
24
24
 
25
- **当前测试版(`0.5.0-beta.6`)**,支持 macOS、Linux、Windows 交互安装:
25
+ **当前测试版(`0.5.0-beta.8`)**,支持 macOS、Linux、Windows 交互安装:
26
26
 
27
27
  ```sh
28
- npx bookmark-research@0.5.0-beta.6 install --source npm:bookmark-research@0.5.0-beta.6
28
+ npx bookmark-research@0.5.0-beta.8 install --source npm:bookmark-research@0.5.0-beta.8
29
29
  ```
30
30
 
31
- 这条命令同时固定安装器和插件内容。下面的 `@latest` 示例使用默认渠道,目前仍为 `0.5.0-beta.3`;`@beta` 当前指向 `0.5.0-beta.6`。只改 npx 的版本选择,不会改变安装器默认下载的内容来源。
31
+ 这条命令同时固定安装器和插件内容。下面的 `@latest` 示例使用默认渠道,目前仍为 `0.5.0-beta.3`;`@beta` 当前指向 `0.5.0-beta.8`。只改 npx 的版本选择,不会改变安装器默认下载的内容来源。
32
32
 
33
33
  **默认渠道的交互安装:**
34
34
 
@@ -129,7 +129,7 @@ setup 还会问研究报告和证据放在哪里:统一目录(默认 `<数
129
129
  | 层 | 组成 | 运行方式 | 需要配置 |
130
130
  | --- | --- | --- | --- |
131
131
  | Skill | [`bookmark-research`](skills/bookmark-research/SKILL.md) 及 11 份方法参考,每份都有[中文版](skills/bookmark-research/references/zh/skill-guide.md) | 由宿主加载 | 不需要 |
132
- | 本地 MCP | `bookmark-research`,37 个工具(见下表) | 宿主启动的 Python 进程 | 安装器自动完成 |
132
+ | 本地 MCP | `bookmark-research`,40 个工具(见下表;随能力增长) | 宿主启动的 Python 进程 | 安装器自动完成 |
133
133
  | 本地 MCP 调用的远程 MCP | [Exa](https://exa.ai)(搜索、网页读取)、[Parallel](https://parallel.ai)(搜索、备用读取)、[Tavily](https://tavily.com)(备用搜索) | HTTPS,不启动本地进程 | 可选 API Key |
134
134
  | 本地 MCP 调用的 HTTP 服务 | [Jina Reader](https://jina.ai/reader)(备用读取;有 Key 时可搜索) | HTTPS | 可选 `JINA_API_KEY` |
135
135
  | 可选的宿主 MCP | Exa Agent、Parallel Task MCP、Tavily Research | 需自行加到宿主里 | `bookmark-research setup` 会给出步骤 |
@@ -138,12 +138,12 @@ setup 还会问研究报告和证据放在哪里:统一目录(默认 `<数
138
138
 
139
139
  | 分类 | 工具 |
140
140
  | --- | --- |
141
- | 书签 | `sync_package`、`index_status`、`source_history`、`search_bookmarks`、`get_context` |
141
+ | 书签 | `sync_package`、`index_status`、`source_history`、`source_remove`、`source_merge`、`search_bookmarks`、`get_context` |
142
142
  | 网页 | `search_web`、`fetch_web`、`search_archive`、`search_providers` |
143
143
  | 研究 | `research_readiness`、`research_start`、`research_status`、`research_search`、`research_fetch`、`research_source`、`research_record`、`research_inventory`、`research_coverage`、`research_import_evidence`、`research_finish`、`research_route` |
144
144
  | 专业研究服务(可选) | `research_services`、`research_service_prepare`、`_start`、`_status`、`_result`、`_cancel`、`_attach`、`_import` |
145
145
  | 设置 | `get_settings`、`update_settings` |
146
- | Wiki 与评测 | `wiki_write`、`wiki_get`、`wiki_list`、`wiki_search`、`wiki_lint`、`evaluate_research` |
146
+ | Wiki 与评测 | `wiki_write`、`wiki_get`、`wiki_list`、`wiki_search`、`wiki_lint`、`wiki_acknowledge`、`evaluate_research` |
147
147
 
148
148
  ## 更多
149
149
 
@@ -44,7 +44,7 @@ flowchart TB
44
44
 
45
45
  subgraph Plugin["Bookmark Research Plugin"]
46
46
  Skill["Skill:Markdown 指令<br/>查询方法、研究规则与宿主路由"]
47
- MCP["本地 MCP 入口<br/>src/mcp_server.py · 35 个工具"]
47
+ MCP["本地 MCP 入口<br/>src/mcp_server.py · 工具随能力增长(本版 40 个)"]
48
48
  CLI["Python CLI 入口<br/>src/cli.py"]
49
49
  Core["共用 Python 运行时<br/>Python 3.9+ 标准库"]
50
50
  Sources["来源接入、增量同步与本地查询"]
@@ -0,0 +1,36 @@
1
+ # Diff 审查与修正记录(2026-10-09)
2
+
3
+ 本轮审查从 `a4b7630` 之后的工作树 diff 出发,通过 CodeGraph 定位实现与调用链,交叉检查 DSH 分发、检索、研究并发和 Wiki 证据生命周期。原始改动已单独保存为 `264d910`;其发布文件与 npm `0.5.0-beta.7` 的 120 个文件逐字节一致。
4
+
5
+ DSH 修复的方向正确:发布包具有入口与 `dsh.bundle.patch` 声明,路径从安装位置解析,ESM 范围限制在 DSH 目录,环境变量按明确列表转发。用户已确认当前 DSH 跑通,旧评估报告中的失效状态属于历史记录。本轮没有重新安装或修改用户的 DSH profile。
6
+
7
+ 原始 diff 的 561 项测试通过,但交叉审查发现了并发和兼容边界,特别是来源数据保护与证据复核。以下问题均有隔离复现,并已在后续源码提交中修正。
8
+
9
+ | 优先级 | 原始 diff 的问题 | 本轮修正 |
10
+ | --- | --- | --- |
11
+ | P1 | 研究取消或生成 incomplete 报告后,慢请求仍提交结果,导致最终报告与状态文件不一致;取消后还可能追加 fallback 请求。 | 提交与 fallback 预算预留都重新检查研究为 active、操作为 pending。已关闭或确认中断的操作拒绝迟到结果。 |
12
+ | P1 | merge 在事务外判断版本相同,同步可在检查后写入独有内容,再被 merge 删除。两个未版本化的 legacy 来源也会因 `None == None` 被误合并。 | 在写事务内重新读取并校验双方有效内容版本;legacy 来源须先同步。 |
13
+ | P1 | remove/merge 提交数据库后删除原目录,可能删掉另一连接刚以相同 source ID 重新导入的快照。 | 提交前将旧快照改名为唯一隔离目录,提交后只清理该目录;异常回滚恢复旧版本且不覆盖并发新版本。 |
14
+ | P1 | 已发布证据的响应制品被修改后,`wiki acknowledge` 可重新计算 hash,把 `invalid_evidence` 清成 current。 | 持研究锁校验上一修订的证据与链接;存在 error 时拒绝复核,保留原校验基线。 |
15
+ | P2 | 并发导入最终分配的 source ID 与提前写出的 manifest ID 不一致。 | 在提交锁内分配最终 ID 后生成 manifest,回放保留该 ID。 |
16
+ | P2 | 删除引用来源后,freshness 为 unknown,但 Wiki 仍显示 current。 | 不可解析的来源产生 `source_input_unavailable`,页面进入 needs_review。 |
17
+ | P2 | 旧版 DSH 包缺少新增的嵌套 `package.json`,无法进入承诺的旧包导出兼容分支。 | 仅在文件缺失时生成 ESM manifest;符号链接和错误文件类型仍拒绝。 |
18
+ | P2 | Codex、Claude、Agent Plugins 导出没有根 `package.json`,握手版本退成 `0+unknown`。 | 读取各分发格式自己的 manifest;存在但损坏的优先 manifest 不被其他文件掩盖。 |
19
+ | P2 | 含 `%`、`_`、反斜杠的画布线索在 SQL 命中后又被丢弃。 | SQL 保留转义,结果归属使用原始查询文本。 |
20
+ | P2 | doctor 泄漏探测文件描述符、可能删除预存同名文件,并把普通文件阻断的目录路径报为可写。 | 使用自动关闭的随机临时文件;遇到非目录路径组件立即报告不可写。 |
21
+
22
+ DSH 的重装提示也已改为明确指定 profile 和版本的原生命令,避免默认 `latest` 指向旧发布。Wiki 复核记录保存本次提交的说明。
23
+
24
+ 修正快照清理后,第二轮交叉复核发现了“目录改名后、数据库提交前强制终止进程”的恢复窗口。已补启动恢复:在写事务内,根据数据库仍引用的版本及 manifest/hash 校验恢复目录,保留已有新版本,不复活已删除来源。真实子进程强制退出及后续 partial 导入均纳入验证。
25
+
26
+ 验证结果:
27
+
28
+ - 最终本地全量测试:**587 项,0 失败,3 项按条件跳过**,Python 3.9.6 / SQLite 3.43.2。跳过项为原生 Pi/DSH 安装、显式启用的 Codex/npm 集成和全宿主 npm 下载测试;不将其计作本轮宿主安装通过。
29
+ - 独立下载真实 npm beta.5,使用当前导出器生成 DSH 包,删除源副本并搬到含中文、空格和引号的路径后,Skill 和实际 MCP 调用通过。
30
+ - 当前五种导出同样在删除源副本、搬迁后完成真实 MCP initialize,全部报告 beta.7;DSH 解释器、参数和环境转发契约通过。
31
+ - 用户画布样例验收:638 个书签实例、557 个唯一 URL、122 个文件夹、5 个栏目;完整库存分页保留全部 ID,二次同步识别未修改内容,原文件 hash 未变。测试未调用网络研究服务。
32
+ - 合成研究验收正确阻止未读完来源时宣称研究完成。已发布 npm 包的 CLI help、doctor、status 冒烟通过。
33
+
34
+ 实现仍有明确边界:取消后已经返回的响应可能留下未引用制品,但不能再进入已结束研究的状态或报告。启动恢复只处理数据库仍引用且校验通过的快照;未引用的 `.removed-*` 残留保守保留,不自动回收。普通异常回滚、并发重新导入、回滚后出现新版本,以及提交前/后强制退出均有回归覆盖。
35
+
36
+ 这些审查修正属于 `264d910` 之后的源码提交,**不包含在已经发布的 npm beta.7 中**。本轮只提交并推送 Git,没有重新发布 npm,也没有移动 dist-tag。复现所用本机报告、索引和私人样例未纳入提交。
@@ -19,7 +19,9 @@
19
19
 
20
20
  发布顺序:修改源码版本并完成 CI,发布已经验证的 npm 包,确认 registry 可取得该版本,再更新 GitHub marketplace 的固定版本。未发布源码不能通过 npm 入口安装;需要先用源码入口测试。
21
21
 
22
- 测试版使用 npm 的 `beta` 标签;若创建 GitHub 版本说明,则标记为预发布。不会自动移动 npm 的 `latest` 标签。安装测试版时同时固定安装器与内容来源,例如 `npx bookmark-research@0.5.0-beta.6 install --source npm:bookmark-research@0.5.0-beta.6`。仅将 npx 改为 `@beta` 不会改变默认内容来源 `latest`;已固定的旧版安装也不会自动换成测试版。
22
+ 候选版本在 `release/` 分支同步三个版本清单并运行 CI。该分支的 registry 集成测试使用当前已发布渠道版本,源码测试、原生安装测试和 npm 打包检查仍验证候选内容;主分支的 registry 测试始终验证 marketplace 固定版本。发布通过 CI 的 npm 包并核验 registry 后,再将候选分支合入主分支,避免默认安装清单提前指向未发布版本。
23
+
24
+ 测试版使用 npm 的 `beta` 标签;若创建 GitHub 版本说明,则标记为预发布。不会自动移动 npm 的 `latest` 标签。安装测试版时同时固定安装器与内容来源,例如 `npx bookmark-research@0.5.0-beta.8 install --source npm:bookmark-research@0.5.0-beta.8`。仅将 npx 改为 `@beta` 不会改变默认内容来源 `latest`;已固定的旧版安装也不会自动换成测试版。
23
25
 
24
26
  ## 官方资料与项目实例
25
27
 
@@ -300,10 +300,11 @@
300
300
  "host": "dsh",
301
301
  "profile": "desktop",
302
302
  "runtime_version": "0.2.0-rc.2",
303
- "package_version": "0.5.0-beta.5",
303
+ "package_version": "0.5.0-beta.6",
304
304
  "methods": [
305
305
  "scripts/install.py install --host dsh --profile desktop with the managed export from this source tree",
306
- "dsh plugin --profile <throwaway> add <packed release tarball> (registry-equivalent spec)"
306
+ "dsh plugin --profile <throwaway> add <packed release tarball> (registry-equivalent spec)",
307
+ "dsh plugin --profile desktop add bookmark-research@0.5.0-beta.6 (published registry release)"
307
308
  ],
308
309
  "observed": {
309
310
  "bundle_selected_into_profile": true,
@@ -320,8 +321,18 @@
320
321
  "unit_tests_passed": 506,
321
322
  "temporary_test_profile_removed": true
322
323
  },
324
+ "published_release": {
325
+ "version": "0.5.0-beta.6",
326
+ "dist_tag": "beta",
327
+ "tarball_shasum": "7b9a11c7ef89ae9bba442b0b59b9e5ad8d5c0242",
328
+ "tarball_matches_verified_local_build": true,
329
+ "ships_dsh_bundle_layer": true,
330
+ "profile_dependency": "0.5.0-beta.6",
331
+ "installed_as_real_directory": true,
332
+ "bundle_selected_by_dsh": true,
333
+ "mcp_tools_listed_from_installed_copy": 37
334
+ },
323
335
  "not_verified": [
324
- "a published npm release that carries bundle.patch.yml; the released 0.5.0-beta.5 predates it",
325
336
  "Windows or Pi installation",
326
337
  "the npm cache permission retry on a failing machine (unit-tested only; this machine used npm_config_cache instead)"
327
338
  ]
@@ -0,0 +1,57 @@
1
+ # Bookmark Research 0.5.0 beta 7
2
+
3
+ **Beta / 测试版.** Uses the npm `beta` channel. The npm `latest` tag remains unchanged.
4
+
5
+ ## Install this beta
6
+
7
+ Pin both the installer and the plugin content:
8
+
9
+ ```sh
10
+ npx bookmark-research@0.5.0-beta.7 install --source npm:bookmark-research@0.5.0-beta.7
11
+ ```
12
+
13
+ Choose Codex, Claude Code, Pi or DSH in the installer. Existing installations retain their recorded source and version selection. The GitHub Codex marketplace advances to this npm version after the registry confirms publication.
14
+
15
+ DSH installs the released layer directly:
16
+
17
+ ```sh
18
+ dsh plugin --profile <name> add bookmark-research@0.5.0-beta.7
19
+ ```
20
+
21
+ ## Changes
22
+
23
+ ### Correctness
24
+
25
+ - **One release-version source.** `src/release.py` reads `package.json`, and the MCP `serverInfo`, the remote client `clientInfo` and both outbound user agents use it. Runtime identity had reported `0.5.0-beta.3` while the manifests said `0.5.0-beta.6`. A regression test fails any build that reintroduces a hardcoded release version outside `release.py`, and asserts that the real handshake matches the manifest.
26
+ - **A copied data directory resolves its own research tasks.** `research-locations.json` carries absolute paths, so a same-machine clone used to read and write the *origin's* task folder even when `--directory` named the clone. The task folder inside the data directory in use now wins, the registry stays the fallback for tasks created beside their input, and a second valid folder for the same id is reported as `location_status: "conflict"` with `alternate_path` instead of being hidden.
27
+ - **The task lock no longer spans a network request.** A provider that answered slowly held the task lock for the whole call, so an independent process recording a finding timed out after ten seconds with a settings-worded error. Reservation, request and commit are now separate: only reserve and commit hold the lock, a mid-request fallback reservation re-reads state under it, and genuine contention returns a retryable "research task is being written by another process".
28
+ - **Every occurrence of a merged body can be enumerated.** `search_archive` truncated a body's occurrences at 50 with no marker and no continuation. Results now carry the true `occurrence_count`, `occurrences_truncated` and `occurrences_next_offset`, and `occurrence_limit` / `occurrence_offset` page through them in a stable order.
29
+ - **A legitimate source change can be resolved.** `source_input_changed` is a warning about the frozen research input version; re-reviewing a page could never clear it. `wiki_write` now accepts `reviewed_input_version`, `wiki acknowledge` records a review without editing content, acknowledgements carry forward while they are still current, and a later source change reopens the warning. `wiki_lint` also reports `severity: "info"` for an acknowledged change instead of leaving only the warning.
30
+ - **`wiki.directory` applies without a restart.** A long-lived stdio server cached the Wiki store built from the directory configured at first use, so `update_settings` appeared to succeed while reads and writes continued against the old location.
31
+
32
+ ### Retrieval
33
+
34
+ - **`items_fts` now answers queries.** The shipped FTS table used the default tokenizer, which treats a whole run of CJK characters as one token, so it could not match `推理` inside `推理加速` and the query path used `LIKE` only. The table uses the substring-based **trigram** tokenizer and is rebuilt in place when an existing database is opened. Terms of three or more characters use `MATCH`; one- and two-character terms keep the `LIKE` fallback, because a trigram index cannot answer them. Substring semantics are unchanged (`RAG` still finds `AutoRAG`). When trigram is unavailable the index stays usable and every term falls back to `LIKE`.
35
+ - **Whitespace-separated terms are required together.** A multi-word target was matched as one literal phrase, so `linux.do topic` returned nothing. Each term must now occur; `推理 加速` requires both words.
36
+ - **Canvas text is findable.** Group labels, edge labels and text cards were outside every search field, so asking about "the card labelled 123" returned unrelated short-substring bookmark hits with no sign that `123` was a group label. `search_bookmarks` now reports `canvas_matches` (attributed per target) and states explicitly that they are not bookmark items and never inflate `total`.
37
+ - **Counting guardrails.** A copy anchor reports the shared tree's count, so summing per-card counts over-reported 638 bookmarks as 1239. `context.sections[]` now carries `counted_in_totals` and `anchor_of`, `context.counts` and `search.counts` give instance and unique-URL totals for the scope, `search` warns when the requested card is an anchor, and items report `shown_in_anchors`.
38
+ - **Smaller gaps.** Folders can be enumerated (`item_types` / `--item-type folder`), the synthetic untitled tree root is marked `synthetic` and counted separately as `tree_roots`, `search` returns `next_offset`, `limit=0` / `count_only` returns counts without rows, tag colours are filterable, and Wiki search matches text whose spacing differs from the query (`唯一URL` finds `唯一 URL`).
39
+
40
+ ### Operations
41
+
42
+ - **Duplicate imports are no longer silent, and can be undone.** Importing the same canvas from a new folder used to add a second identical source with `warnings: []` and no way back. `sync_package` now reports `duplicate_source_ids` with a warning naming the source to reuse, and `source remove` / `source merge` (MCP `source_remove` / `source_merge`, CLI `source remove` / `source merge`) clean up. Both only report until `confirm: true` / `--confirm` is passed; merge refuses sources whose content versions differ and moves the duplicate's path aliases to the survivor.
43
+ - **`doctor` checks that it can actually write.** An unwritable data directory produced a fully green `doctor` followed by a raw SQLite error from the first import. `doctor.storage` now probes the data directory, the config directory and an existing database, and reports `next_step` with the exact environment variables to set. A read-only failure from any command is rewritten to name `BOOKMARK_RESEARCH_DATA_DIR` and `BOOKMARK_RESEARCH_CONFIG`.
44
+ - **An incomplete DSH install says so.** The DSH entry module preflights `src/cli.py` and the Skill before registering; when either is missing it writes the reason and the reinstall command to stderr and registers nothing, instead of letting the host fail later with a bare `ENOENT`.
45
+ - **`partial` retention is stated up front.** `status` returns `retained_missing_note` when imported files are no longer in the package.
46
+
47
+ ## New tools
48
+
49
+ `wiki_acknowledge`, `source_remove` and `source_merge` bring the local MCP surface from 37 to 40 tools. `search_bookmarks` gained `tag_colors`, `item_types` and `count_only`; `search_archive` gained `occurrence_limit` and `occurrence_offset`.
50
+
51
+ ## Validation and limits
52
+
53
+ 561 discovered local tests pass (three conditional integration tests skipped), up from 506, with new coverage for every change above. Verified end to end on the sample canvas package (638 bookmark instances, 557 unique URLs, 5 sections, 7 canvas nodes, 3 edges): index counts still match an independent parse, the copy anchor is not double counted, `complete` still refuses an incoherent export and keeps the previous index, a fabricated citation is still rejected, and `research_finish` still refuses to claim completion while original URLs are unread.
54
+
55
+ Not verified in this release: Windows and Pi installation; a fresh DSH Desktop profile (the running app still resolves the Skill from a native-install directory that a previous installer created and that no longer exists — restarting the app, or reinstalling into the profile, re-resolves it); ten-thousand-plus URL research budgets; and the still-open items listed in the evaluation report: `context()` N+1 queries, an index time field, and local-only facts entering a research report.
56
+
57
+ Install from [npm](https://www.npmjs.com/package/bookmark-research/v/0.5.0-beta.7). GitHub supplies source, version notes and installation catalogs; npm remains the shared release package for all four hosts.
@@ -0,0 +1,34 @@
1
+ # Bookmark Research 0.5.0 beta 8
2
+
3
+ **Beta / 测试版.** Published through the npm `beta` channel; the `latest` tag remains unchanged.
4
+
5
+ ## Install
6
+
7
+ Pin both the installer and the plugin content:
8
+
9
+ ```sh
10
+ npx bookmark-research@0.5.0-beta.8 install --source npm:bookmark-research@0.5.0-beta.8
11
+ ```
12
+
13
+ Choose Codex, Claude Code, Pi or DSH in the installer. For an existing DSH profile, the released layer can also be installed directly:
14
+
15
+ ```sh
16
+ dsh plugin --profile <name> add bookmark-research@0.5.0-beta.8
17
+ ```
18
+
19
+ ## Changes
20
+
21
+ - DSH exports support legacy npm packages that lack the nested ESM manifest. Repair instructions retain the selected profile and release version. The Desktop prerequisite test now resolves actual executable shims on Windows.
22
+ - Relocated Codex, Claude Code and Agent Plugins exports report their own release version during the real MCP handshake.
23
+ - Cancelled or incomplete research tasks reject late provider results and further fallback reservations. Concurrent evidence imports retain the final source IDs in their manifests.
24
+ - Source merging checks matching, nonempty synchronized versions inside the write transaction. Source removal preserves concurrent reimports; startup recovers hash-verified snapshots left by an interrupted removal without reviving committed deletions.
25
+ - Wiki acknowledgements cannot accept invalid evidence. Unavailable sources require review, and a new acknowledgement retains its review note.
26
+ - Canvas searches preserve literal `%`, `_` and backslash characters. Storage diagnostics close probe handles, preserve existing files and report paths blocked by files.
27
+
28
+ ## Verification
29
+
30
+ Regression coverage includes research cancellation, source merge/removal races, process-exit recovery, Wiki evidence integrity, exported release identity and DSH installation prerequisites. CI runs the Python suite and npm command smoke checks on Linux, macOS and Windows, plus isolated native Codex installation, migration and update tests.
31
+
32
+ Release branches test registry integration against the currently published package while validating the candidate's source and packed runtime. After publication, the main branch verifies the newly pinned npm version. Personal canvas packages and local evaluation reports are excluded from the published package.
33
+
34
+ Install from [npm](https://www.npmjs.com/package/bookmark-research/v/0.5.0-beta.8). GitHub provides source and release notes; npm is the shared release package for all four hosts.
@@ -1,4 +1,5 @@
1
1
  import { spawnSync } from "node:child_process";
2
+ import { existsSync } from "node:fs";
2
3
  import { readFile } from "node:fs/promises";
3
4
  import { fileURLToPath } from "node:url";
4
5
 
@@ -63,6 +64,17 @@ export function apply(ctx) {
63
64
  + "Set BOOKMARK_RESEARCH_PYTHON to select an interpreter.\n");
64
65
  python = { command: "python3", args: [] };
65
66
  }
67
+ // Preflight the files this provider advertises. A stale registration used to
68
+ // surface only as an ENOENT when the host tried to load the Skill or start
69
+ // the MCP child, with nothing telling the user how to recover.
70
+ const missing = [cli, fileURLToPath(skillUrl)].filter((path) => !existsSync(path));
71
+ if (missing.length > 0) {
72
+ process.stderr.write("bookmark-research: installation is incomplete; missing " + missing.join(", ") + ".\n"
73
+ + "Reinstall the intended version in the affected profile: dsh plugin --profile <profile> add"
74
+ + " bookmark-research@<version> (use the Desktop app's CLI for profile desktop), or remove the stale"
75
+ + " registration that points at a deleted install directory.\n");
76
+ return;
77
+ }
66
78
  const env = Object.fromEntries(FORWARDED_ENV
67
79
  .filter((key) => process.env[key] !== undefined)
68
80
  .map((key) => [key, process.env[key]]));
package/package.json CHANGED
@@ -1,14 +1,53 @@
1
1
  {
2
2
  "name": "bookmark-research",
3
- "version": "0.5.0-beta.6",
3
+ "version": "0.5.0-beta.8",
4
4
  "description": "Check bookmark URLs or Canvas packages, compare sources with host-led research, and retain verifiable reports.",
5
5
  "license": "GPL-3.0-only",
6
- "repository": {"type": "git", "url": "git+https://github.com/Browser-bookmark-hub/Bookmark-Research.git"},
7
- "keywords": ["bookmark-research", "mcp", "claude-code", "codex", "pi-package", "deepseek-harness"],
8
- "bin": {"bookmark-research": "bin/bookmark-research.js"},
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Browser-bookmark-hub/Bookmark-Research.git"
9
+ },
10
+ "keywords": [
11
+ "bookmark-research",
12
+ "mcp",
13
+ "claude-code",
14
+ "codex",
15
+ "pi-package",
16
+ "deepseek-harness"
17
+ ],
18
+ "bin": {
19
+ "bookmark-research": "bin/bookmark-research.js"
20
+ },
9
21
  "main": "./hosts/dsh/plugin.js",
10
- "engines": {"node": ">=18"},
11
- "files": ["bin", "src", "scripts", "config", "skills", "hosts", "docs", "bundle.patch.yml", ".codex-plugin/plugin.json", ".agents/plugins/marketplace.json", "install.sh", "LICENSE", "README.md", "README.zh.md", "!**/__pycache__", "!**/*.pyc"],
12
- "pi": {"skills": ["./skills"]},
13
- "dsh": {"bundle": {"patch": "./bundle.patch.yml"}}
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
25
+ "files": [
26
+ "bin",
27
+ "src",
28
+ "scripts",
29
+ "config",
30
+ "skills",
31
+ "hosts",
32
+ "docs",
33
+ "bundle.patch.yml",
34
+ ".codex-plugin/plugin.json",
35
+ ".agents/plugins/marketplace.json",
36
+ "install.sh",
37
+ "LICENSE",
38
+ "README.md",
39
+ "README.zh.md",
40
+ "!**/__pycache__",
41
+ "!**/*.pyc"
42
+ ],
43
+ "pi": {
44
+ "skills": [
45
+ "./skills"
46
+ ]
47
+ },
48
+ "dsh": {
49
+ "bundle": {
50
+ "patch": "./bundle.patch.yml"
51
+ }
52
+ }
14
53
  }
@@ -13,13 +13,15 @@ HOST_SOURCE_FILES = (
13
13
  )
14
14
 
15
15
 
16
- def read_asset(root, relative):
16
+ def read_asset(root, relative, default=None):
17
17
  """Read a curated asset without following source symlinks."""
18
18
  current = Path(root)
19
19
  for part in Path(relative).parts:
20
20
  current = current / part
21
21
  if current.is_symlink():
22
22
  raise ValueError("Source symlinks are not exported: " + relative)
23
+ if default is not None and not current.exists():
24
+ return default
23
25
  if not current.is_file():
24
26
  raise ValueError("Required host asset is missing: " + relative)
25
27
  return current.read_bytes()
@@ -62,7 +64,10 @@ def export_host_assets(source, stage, host):
62
64
  files["hosts/dsh/plugin.js"] = read_asset(source, "hosts/dsh/plugin.js")
63
65
  # Scope ESM to the entry module so a package root may stay CommonJS
64
66
  # (the npm package keeps a CommonJS bin and the pi field).
65
- files["hosts/dsh/package.json"] = read_asset(source, "hosts/dsh/package.json")
67
+ # Older releases predate the nested manifest. Supply the scope
68
+ # when absent, while still rejecting links or invalid file types.
69
+ files["hosts/dsh/package.json"] = read_asset(
70
+ source, "hosts/dsh/package.json", default=b'{"type":"module"}\n')
66
71
  files["workflows/bookmark-research/meta.json"] = (
67
72
  json.dumps(definition["meta"], ensure_ascii=False, indent=2) + "\n").encode("utf-8")
68
73
  relative = "hosts/dsh/workflow-call.py"