v0.8.7
Community-fix release: five contributed PRs plus two maintainer follow-ups, closing #32, #33, #35, #37, and #39.
Fetching and search
- Non-English pages are no longer discarded as binary garbage (closes #37, thanks @synqing). The junk filter counted every character above
ord(127)as non-printable, so CJK, Arabic, Cyrillic, and accented-Latin pages always tripped the threshold and were thrown away. The check now counts only true control characters and U+FFFD, shared between both fetch gates via onebinary_garbage_ratio()so they can't drift apart again. Regression fixtures use real Chinese/Japanese/Arabic/Russian/French prose. - Degenerate or failed searches error loudly instead of returning
[](closes #32, thanks @ankaggarwal94 for the report and @synqing for the fix). An empty query, a malformed query, and a broken FTS index were all silently swallowed and reported as "no results."search_ftsnow raisesSearchQueryErrorfor queries with no searchable terms (CLI exits 2 withBAD_QUERYin--jsonmode) and lets genuine index failures propagate. The shipped step skills and agent prompts that usedsearch "" --tagas a list-all idiom were rewritten tonote list --tag ... --all. - Patchright stealth actually engages now (closes #35, thanks @seanyoungberg). The crawler was built without an explicit strategy, so crawl4ai defaulted to plain Playwright and the stealth driver never ran. The provider now wires
UndetectedAdapterthroughAsyncPlaywrightCrawlerStrategyat both fetch call sites; Crawl4AI floor raised to 0.7.3. - PDF fetch failures are diagnosable instead of silent (closes #39, thanks @mcowan38 for the report and @synqing for the fix). Every
_fetch_pdffailure path now logs its reason — including a missing/broken pymupdf, which used to silently disable all PDF ingestion and present as every PDF on every domain getting junked. PDF identity now comes from%PDF-magic bytes rather than the content-type header or URL suffix, so mislabelled PDFs are kept and HTML masquerading as PDF is named in the log. - New
tavilyweb provider (thanks @tavily-integrations).provider = "tavily"in config plusTAVILY_API_KEY; optional install viapip install "hyperresearch[tavily]". Ships with offline tests that stub the SDK.
Lint
- New lint rule
citation-style-preservation(closes #33). Whenprompt-decomposition.json(or awrapper_contract.jsonoverride) declarescitation_style: "wikilink", the final report must contain at least one[[<note-id>]]wikilink that resolves to a vault note; for"inline", at least one numbered[N]marker plus a Sources/References heading. Presence-only by design — it catches the polish/synthesis regression that strips every citation, without the false-positive tail a density floor would have on short or quote-heavy reports. Skips cleanly when the style is"none", no decomposition exists, or the vault has no source notes.
Release readiness and deployability
- Version metadata is consistent again.
hyperresearch.__version__now tracks the version declared inpyproject.toml, fixing the state where the built wheel reported0.8.6whilehyperresearch --versionreported0.8.5. - CI installs the dependencies used by the tests. The
devextra now includesexa-py, so the Exa provider tests pass under the samepip install -e ".[dev]"command CI runs. Without it,mainfails 10 tests intests/test_web/test_exa_provider.pywithModuleNotFoundError: No module named 'exa_py'. - Optional extras match CLI guidance. Declared the
crawl4aiandwatchextras the CLI already directs users to install.pip install hyperresearch[watch]previously resolved to no extra and installed nowatchdog, sohpr watchstayed broken while telling the user to run the command that had just failed. - Publish workflow now gates on lint and tests before building. Tagged releases still publish via trusted PyPI publishing, but the publish job now fails before upload if ruff or pytest fails.
- Packaging regression tests added. The test suite now checks that runtime version metadata tracks
pyproject.tomland that dev/install extras cover the tested optional provider surface.