# Changelog

All notable changes to **dsh-mineru** are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/), and versioning follows [Semantic Versioning](https://semver.org/lang/zh-CN/). 版本号唯一来源是 `package.json`。

## [0.1.15] - 2026-10-24

### Fixed

- DSH `0.2.0-rc.1` is no longer silently skipped by the host compatibility gate: the six `@deepseek-ai/dsh-*` peers now declare `^0.1.5-rc.1 || ^0.2.0-rc.1` (previously `|| ^0.2.0-rc.2`), so every version from `0.2.0-rc.1` through the `0.2.x` line passes `evaluatePluginCompatibility`, while `0.3.0` prereleases stay excluded. Fixes #20.

## [0.1.14] - 2026-10-24

### Fixed

- Accept DSH `0.2.0-rc.2` through the actual runtime compatibility gate: all six `@deepseek-ai/dsh-*` peers now declare `^0.1.5-rc.1 || ^0.2.0-rc.2`. Preserve the legacy settings path, and pin development host packages to `0.2.0-rc.2` so regressions run against the target runtime rather than only mocked services.
- Validate live settings on the schema-derived settings path as well as the custom HTTP path. Numeric ranges, integer steps, language values, credential reference names and artifact directory names now reject invalid writes before persistence, including writes made through the native settings service.
- Restrict `/plugin/mineru/credential` writes to the configured `tokenCredential`. A request naming another reference is rejected instead of overwriting or deleting an unrelated credential; malformed credential bodies and array config patches return `400`.
- Register the browser's bilingual dictionaries only once. DSH rejects duplicate namespace/locale registrations, which previously skipped language binding and the locale watcher. Update browser declarations to use the current Cordis context instead of the removed `dsh-client-runtime` package.
- Keep JSON-request cancellation and timeouts active through response-body reads, and reject an already-cancelled call before sending it. Receiving headers alone no longer disables the deadline on a stalled response.
- Settings card (Web): the extra formats (docx/html/latex) checkboxes now read any persisted shape — an array, a scalar, or a string list — instead of treating every non-array value as "nothing checked", so a card saved on any host shows what is actually configured. The unsaved-changes indicator is now computed by diffing the current form against the server baseline read at load, not by a write flag, so touching a checkbox is no longer enough to light "有未保存的更改", and saving resets it even when the host echoes the same values back. Fixes #22.

### Development

- Add settings-card unit tests (`tests/settings-card.spec.js`): the shipped `client.js` bundle is sliced at its marked settings-view helper and executed in a sandbox, pinning the formats-normalization and dirty-flag contracts for the fix above.
- Add settings round-trip tests against the real 0.2.0-rc.2 host (`tests/settings-roundtrip.spec.js`): full and empty `extraFormats` patches persist and read back byte-identically through the settings service.

- Add real-runtime compatibility tests (`npm run test:compat`): package admission without exemptions, Loader activation, progressive tool visibility and disposal, settings persistence and stale-write conflicts, credential rotation and redaction, validation failures, and parsing through the real tool runtime with fetch-level simulated MinerU responses.
- Upgrade Vitest from `3.x` to patched `4.1.11+` and regenerate the lockfile against the target SDK. Development tests use supported Node.js 20.19+, 22.12+ or 24+ releases (24 recommended); the plugin runtime requirement remains Node.js 18+.

### Added

- Automatic chunking for oversized precision requests: when one request would ask for more than 200 pages (a workspace PDF larger than that, or a `pageRanges` selection over 200 pages), `mineru_parse` splits it into sequential `page_ranges` chunks, submits and polls them one by one, and merges the outcome into the layout a single request produces — no new parameter, and no local PDF rewrite (the file is uploaded once, only the requested range changes per request). `full.md` is concatenated with one provenance line per chunk, `content_list.json` is merged with `page_idx` mapped back to the original document (the API renumbers it from 0 for every requested subset), `images/` is the union of all chunks, and `layout.json` / `<uuid>_model.json` stay per chunk under `chunks/chunk-<n>/` because they are page-indexed arrays. `run.json` and the tool result's new `chunkCount` / `chunks` fields report each chunk's page range, `batchId`, state and duration; a failed chunk reports its page range in the error.
- `lib/page-ranges.js` (split planning, negative-index resolution), `lib/chunk-merge.js` (subset-index remapping, `full.md` stitching) and `lib/pdf-pages.js` (local page-tree count: `/Type /Pages` `/Count`, `/Type /Page` leaves, deflate object streams) — pure modules, with vitest coverage and a fake-client test of the whole split-and-merge path.
- `mineru_parse` (and `mineru_batch_parse` / `mineru_task`) accept an optional `anchor` boolean, default `false`. With `anchor: true` a precision result additionally gets a `document.md` next to `full.md`: every block of `content_list.json` in order, preceded by one `<!-- pN bK -->` anchor, where `pN` is the page in the original document (1-based, mapped through `requestedPages[page_idx]` so discrete `pageRanges` such as `"2,4-6"` and chunked requests stay correct) and `bK` is the block's position inside that page. Text blocks render their text (a cloud heading level becomes the matching Markdown heading), tables render as caption plus `table_body`, images as `![](<img_path>)`, and blocks the cloud marked `[Unreadable]` — or left empty — keep their anchor instead of disappearing. The option is purely additive: `full.md`, `content_list.json` and `layout.json` keep their current shape, an Agent-API result (no `content_list.json`) only produces a warning, and the new file shows up in the artifact list.
- `tests/anchors.spec.js` (synthetic `content_list` fixtures) pins the anchor contract: 1-based pages, per-page block counters, page-internal ordering, discrete and negative `pageRanges`, table/image/heading/unreadable rendering, and the `p?` fallback; `tests/anchors-tool.spec.js` covers the `anchor: true` wiring (file written next to `full.md`, listed as an artifact, absent when the option is off, warning-only under the Agent API).
- Chunk-level resume + billing protection for oversized precision parses (`lib/run-state.js`): each chunk is snapshotted as soon as its result zip is extracted — a `.complete.json` marker is written last, and the downloaded `result.zip` is kept while the run is unfinished so an already-paid result is salvageable — and `run.json` is persisted after every chunk with a composite cache key (source sha256 + requested page spec + model + OCR + language) and per-chunk state. Re-running the same instruction aligns chunks by their page spec: a finished chunk is reused from its snapshot (zero network), a done chunk with an incomplete snapshot is re-downloaded through the original batch (poll + download, never re-uploaded), and only a cloud-failed chunk is re-submitted. A finished run is reused with no network calls; changing the model / page selection yields a fresh run. `tests/chunk-resume.spec.js` runs a real client against a fetch-level fake cloud and asserts the exact submit/upload/download accounting.
- Zero-quota end-to-end test assets (tests only, no runtime change): a **fetch-level fake cloud** (`tests/helpers/fake-cloud.js`) — replace global `fetch` and the real `MineruClient` runs its whole submit → signed upload → poll → download → unzip → merge path with no network. It keeps one batch per request in a Map (pages derived from `page_ranges`), exposes `calls = { submit, upload, poll, download }` so tests pin one upload/download per chunk with no silent re-upload, and injects poll counts, per-page-range failures, a missing `content_list.json`, malformed entry names, corrupt archives and custom content lists. `tests/helpers/zip.js` hand-builds valid ZIPs and a minimal parseable PDF. `tests/zero-quota-e2e.spec.js` (7 tests) covers a single request, a 250-page two-chunk merge with continuous p1–p250 anchors, a failed chunk, path-traversal / truncated archives and a missing content list. A real MinerU `content_list.json` (6-page public-domain sample; provenance in `tests/fixtures/real-bcgm-6p/NOTICE.md`) is replayed to a byte-identical `document.md`, guarding the anchor/page rendering against drift. Addresses #10.

### Changed

- The tokenless Agent API is never split automatically: it is rate-limited per caller IP, so one call fanned out into a dozen requests would exhaust the caller's quota. Its `-30003` message now says so and points at Precision (where PDFs do split) or a manual `pageRange`.
- URL sources and formats whose page count cannot be read locally (docx/pptx/…) keep the single-request behaviour, and their `-60006` failure now names the reason no split happened. A single request — including one with an explicit `pageRanges` — keeps the cloud's own `page_idx`, exactly as before.
- A precision upload that settles as `failed` now raises the server's own reason (mapped `err_code`, else `err_msg`) as `MINERU_PARSE_FAILED`, the way `waitTask` already did, instead of the collector reporting a bare `state=failed`.
- A chunked parse no longer deletes its run directory on failure: it persists `status: partial` when at least one chunk finished (the completed chunks and snapshots are kept), else `status: failed`, then reports the error; re-running resumes in place and redoes only failed chunks. Single-request runs still clean up their directory on failure, exactly as before.
- Docs: bilingual README (page limits, `pageRanges`, long-PDF section, error table), the bundled `mineru-tools` skill content, and the AGENTS.md file map.

### Security

- Disclose that content leaves the machine: every parse (both Precision and Agent modes) uploads the full source file to a remote MinerU service and downloads the result, with no fully offline mode. The `package.json` description now states this (the one line visible in the plugin list / Settings), and both READMEs carry a top-of-page warning plus a new "Data flow / 数据流向" table — which operations leave the machine, the endpoints (`https://mineru.net`, `https://cdn-mineru.openxlab.org.cn`), token needs, and that no "local-only" mode exists. Addresses #6.

## [0.1.13] - 2026-09-25

### Fixed

- Fix total plugin failure on DSH 2.x: `ctx.settings.register` was removed from the settings service, so `apply()` threw `TypeError: ctx.settings.register is not a function` on its first statement and the entry never activated. Nothing was registered — no `mineru_*` tools, no `mineru-tools` skill, no `/plugin/mineru` routes — while the client half still mounted, so the settings card failed with `Failed to execute 'json' on 'Response': Unexpected end of JSON input` (the host answers an unmounted route with `404` and an empty body). The card is now derived from the exported `CONFIG_SCHEMA` the way DSH 2.x expects it.

### Changed

- Every `CONFIG_SCHEMA` field is marked live through the new `live()` helper, which prefers `.volatile()` and falls back to `.extra('volatile', true)`. DSH only offers the fields carrying `meta.volatile`, and an unguarded `.volatile()` would itself fail activation on schemastery < 3.18.4 — a version a profile can hoist above the installation's copy.
- `state.getCfg()` and `collectFacts()` re-project the entry config on every read (`plainConfig`), because live fields arrive as cosmokit volatile references rather than plain values. A settings write therefore still reaches the next call without a restart, and `exposeMode` stays a load-time switch.
- `lib/http.js` looks the card up under the profile entry id (`settingsNamespace(ctx)`, falling back to `'mineru'`) instead of the hard-coded `'mineru'` string, and `GET /plugin/mineru/config` now answers `503` with a `hint` when the card does not exist, instead of a `200` carrying an empty config.
- DSH 0.1.5 compatibility is retained through feature detection: when `ctx.settings.register` exists, the legacy scope seam is still used (register `'mineru'` + scope read/write), so settings cards and persistence keep working for users who have not upgraded.
- Docs: AGENTS.md settings convention and known traps, plus a migration note under `docs/plans/`.

### Added

- `tests/settings.spec.js`: the schema must flag every field volatile, a settings service **without** `register` must still activate the plugin and register its tools and skill, `settingsNamespace` must follow the entry id, `/config` must explain a missing card, and a settings service **with** `register` (DSH 0.1.5) must register the `'mineru'` namespace and serve `/config` through the legacy scope. The stub settings service throws if `register` is called, so the 2.x regression cannot return silently.

## [0.1.12] - 2026-09-13

### Added

- Client UI i18n: every user-facing string (settings tab, tool cards, toasts, drag-drop bridge) now comes from zh/en dictionaries registered with the host `ctx.locale` service; the zh dict remains the fallback when the locale service is absent.
- Vitest suite (`npm test`): rate limiter, daily counter, option resolution, HTML model forcing, and tool-registry shape (15 tests).
- AGENTS.md contributor guide and DSH upgrade-tracking notes under docs/plans/.

### Changed

- RELEASE.md: packaging validation steps — the shipped client bundle must register with `window.__ModuleLoader__.load`, and bare `--dsw-alias-brand-primary` must not appear in `lib/client.js`.
- package.json: `@deepseek-ai/schemastery` moved to direct dependencies; added `vitest` devDependency, `npm test` script and `engines.node >= 18`; client inject list now includes `locale`.

## [0.1.11] - 2026-09-13

### Fixed

- Fix invisible button labels in dark mode: the platform binds `--dsw-alias-brand-primary` to its near-black (light) / near-white (dark) foreground, so the primary buttons rendered white-on-white in dark themes. Primary buttons and the artifact preview link now use the platform accent token `--dsw-alias-brand-primary-new-colorprimary-new-color`. Fixes #3.

## [0.1.10] - 2026-09-11

### Fixed

- Fix boot failure on the DSH 0.1.5 line: `@deepseek-ai/dsh-settings` dropped `settingsNamespace()`, so `lib/index.js` now registers the plain lowercase namespace `'mineru'` (the old import failed during ESM instantiation and took down the whole plugin tree). Fixes #1.
- Raise the six `@deepseek-ai/dsh-*` `peerDependencies` ranges from `^0.1.0-rc.6` to `^0.1.5-rc.1`: under semver prerelease rules `0.1.5-rc.x` cannot satisfy the old range, so every install reported unmet peers.

## [0.1.9] - 2026-08-16

### Docs

- 快速开始章节按官方插件 README 规范重构：安装拆分为「从 npm 安装（推荐）」与「从源码构建」双通道；新增第 2 步 `dsh --profile web --dump-config` 验证安装；新增 headless 冒烟测试示例；前置条件改为 Note 提示（中英两版同步）。

## [0.1.8] - 2026-08-16

### Docs

- 重写双语 README 为详细版：新增三步快速开始、工具完整参数表、22 项配置参考、错误码速查与 FAQ（中英两版结构对称）。
- 新增 `CHANGELOG.md`（Keep a Changelog）与 `RELEASE.md`（发布 SOP 清单）。
- `CONTRIBUTING.md` 补充提交规范：commit message 描述一律使用英文。
- `package.json` `files` 字段加入 `CHANGELOG.md` / `RELEASE.md`。

## [0.1.7] - 2026-08-16

### Fixed

- 拖拽上传改为**填入输入框草稿（不自动发送）**：文件落到会话工作区后，其路径填入输入框，由用户补充需求后自行发送，完全绕开纯文本模型会拒绝的原生图片附件通道。
- 清除拖拽残留的全屏遮罩（拖拽结束或取消后遮罩层不再残留）。

## [0.1.6] - 2026-08-16

### Docs

- 双语 README：`README.md`（默认英文）+ `README.zh.md`（简体中文）。
- 统一仓库 / npm / MinerU 链接与介绍结构。

## [0.1.5] - 2026-08-16

### Fixed

- P3–P8 自检问题批量修复：
  - `presentCall` 未定义（工具调用上下文缺失时崩溃）；
  - 批量解析自动分批（每批 ≤50 文件 / ≤200 URL，单次 ≤1000）；
  - 错误码映射补齐（A0202/A0211、-60005/-60006、-30001~-30003、-60018 等全部映射为可操作提示）；
  - ZIP 解包 CRC 校验与截断防护；
  - 上传同名文件冲突处理；
  - 版本号改为单一来源（`package.json`）。

## [0.1.4] - 2026-08-16

### Fixed

- 工具结果序列化失败（`value is not lossless JSON`）；
- `mineru-tools` skill 加载失败（`source must be a string`）。

## [0.1.2] - 2026-08-16

### Added

- 首个可用版本：MinerU 多模态文档解析插件 for DeepSeek Harness。
  - 双 API 模式：精准解析 API（需 Token）/ Agent 轻量解析 API（免 Token）；
  - 工具：`mineru_parse`、`mineru_batch_parse`、`mineru_task`、`mineru_activate`（progressive 引导）与 `mineru-tools` 技能；
  - 限流感知（令牌桶 + 429 退避 + 每日限额）；
  - Artifact 落地与 Web 界面（设置卡片、工具结果卡片、HMAC 签名预览）。

[0.1.14]: https://github.com/Lee-Hilex/dsh-mineru/releases/tag/v0.1.14
[0.1.13]: https://github.com/Lee-Hilex/dsh-mineru/releases/tag/v0.1.13
[0.1.12]: https://github.com/Lee-Hilex/dsh-mineru/releases/tag/v0.1.12
[0.1.11]: https://github.com/Lee-Hilex/dsh-mineru/releases/tag/v0.1.11
[0.1.10]: https://github.com/Lee-Hilex/dsh-mineru/releases/tag/v0.1.10
[0.1.9]: https://github.com/Lee-Hilex/dsh-mineru/releases/tag/v0.1.9
[0.1.8]: https://github.com/Lee-Hilex/dsh-mineru/releases/tag/v0.1.8
[0.1.7]: https://github.com/Lee-Hilex/dsh-mineru/releases/tag/v0.1.7
[0.1.6]: https://github.com/Lee-Hilex/dsh-mineru/releases/tag/v0.1.6
[0.1.5]: https://github.com/Lee-Hilex/dsh-mineru/releases/tag/v0.1.5
[0.1.4]: https://github.com/Lee-Hilex/dsh-mineru/releases/tag/v0.1.4
[0.1.2]: https://github.com/Lee-Hilex/dsh-mineru/releases/tag/v0.1.2
