---
title: Changelog
built: 2026-10-30
source: api/CHANGELOG.md
html: https://insurgently.com/projects/library-hub/technical/agents/CHANGELOG
---
# Changelog

> Index of these files: https://insurgently.com/projects/library-hub/technical/agents/llms.txt

Changes to Library Hub's API, by version. Within 0.x the API only adds and deprecates, and removes at 1.0; a change in behavior is marked.

## 0.9.0

Served by Library Hub 0.1.0 and later; the first version published as a package (0.8.0 was never published). Everything new is an addition, except the changes marked below.

- `listBooks('all')`: every book in Calibre, by id.
- `BookSummary.context`: what the book is about, as plain text, from the note's `context` field, else Calibre's comments.
- `BookChapters.outline` (and so `BookText.outline`): whether the chapters follow the book's own structure, an EPUB's files or a PDF's outline. False for a PDF cut into even parts. Before, only the warning text said so.
- **Changed:** `bookText` and `bookChapters` give every chapter of an EPUB: past 30 they're no longer merged into their parts (the chapter notes still merge them, and `openBook`'s `chapter` number counts the notes' chapters, so open a chapter at its `location`). A file several contents entries point into is split at their anchors, each such chapter located `file#anchor`; a file no entry claims, with more than a page heading's text, is a chapter of its own; and contents entries are found when the NCX sits in another folder than the OPF. These chapter rules don't change the whole text (reading it with foliate-js, below, does, once).
- **Changed:** `bookText` and `bookChapters` read EPUB, AZW3 and MOBI files with foliate-js, the parser Library Hub's reader already shows them with, instead of Library Hub's own EPUB reading. An EPUB's contents come from its nav document or its NCX, as foliate-js gives them; HTML entities in a contents file no longer lose the contents. The whole text keeps its paragraphs and blocks as blank lines, and a line break (`<br>`) is now a single line break; spaces next to a paragraph break are gone and every HTML entity is decoded. So **every EPUB's `hash` changes once**, and offsets may move a little; a corpus re-reads the passages whose words changed.
- **Changed:** a book with only an AZW3 or MOBI has its text read from that file as it is, with no conversion, with or without the Content Server: `format` is `AZW3` or `MOBI` (`BookChapters.format` takes those two), and each chapter's `location` is the reader's own (`kindle:pos:…`, `filepos:…`, or a section's `epubcfi(…)`), which `openBook` opens. `needs-conversion` and `{ convert: true }` now only matter for an AZW3 or MOBI that can't be read (DRM, a damaged file), and the message names the cause. `openBook` opens a book's AZW3 or MOBI when it has no EPUB, before its PDF. `bookFile(book, 'EPUB', { convert: true })` still asks Calibre to make an EPUB.
- **Changed:** with Calibre's Content Server off and a library folder set, `bookFile`, `pdfText`, `bookText` and `bookChapters` read a Calibre book's files straight from the library folder, read-only, instead of rejecting with `server-off`. Converting an AZW3 or MOBI still needs the server: `bookFile` with `{ convert: true }` rejects with `server-off`, and `bookText`/`bookChapters` reject with `needs-conversion` when the book has a PDF (with `convert`, the PDF is read, with a warning) and with `server-off` when it has none. A file in a cloud-synced folder that isn't downloaded on this computer, or takes too long to download, rejects with `server-error` naming the file; a library file caught while Calibre is writing it is read again a few times, then rejects with `busy`. `status().calibre` in file mode now has `files: true` and says files and text are read from the library folder.
- **Changed:** `openBook` at a `quote`, and a highlight finding its text again, match through `@insurgently/text`'s `fold`: decomposed and precomposed accents, curly and straight quotes and apostrophes (with ʼ, ʻ, backticks and guillemets), soft hyphens, zero-width characters, Greek final sigma, Turkish İ, German ß, and hyphenated, closed or line-split words (`well-known`, `well-\nknown`, `wellknown`, OCR's `¬`) now match each other; a quote never matches half a letter. Whitespace and quote marks are still ignored altogether. `summarizeBook`'s chapter notes keep a quote found word for word by the same rules, so a quote a model typed with straight quotes is no longer dropped.
- **Changed:** with Model Hub 0.13 or later, `estimateSummary` and `estimateShelfFromDescription` are priced by Model Hub's `estimate()`, each call as it would be sent with its reply cap: `usd` is the high end of its range (every call with a full reply), and `text` gives the range, "about $0.02–$0.09". When Model Hub can't price a call, `usd` is null and `text` says why. With Model Hub 0.12, both work as before: the input at the role's first model's listed price.

## 0.8.0


Never published as a package on its own: it ships inside 0.9.0.

- `bookText(book, { convert? })` and `bookChapters(book, { convert? })`: a book's text as chapters. Each chapter has `n`, `title`, a `location` that `openBook` lands on (`OEBPS/c3.xhtml` in an EPUB, `chars a-b` in a PDF), `start` and `end` in code points, and (from `bookText`) its `text`. A SHA-256 `hash` of the whole text changes only when the text does. EPUBs, AZW3 and MOBI through Calibre's conversion (asked first), and PDFs laid out, with their outline as chapters.
- `status()`: whether Calibre's Content Server and the other providers answer, and what the library holds.
- A `BookRef` wherever a book is named: `listBooks`, `bookFile`, `pdfText`, `shelvesOf`, `addToShelf`, `removeFromShelf`, `createShelf`'s `books`, `suggestShelves`, `estimateSummary`, `summarizeBook`. Numbers work as before. `bookFile` and `pdfText` also read a book file in the vault.
- New error codes: `needs-conversion`, `declined`, `not-calibre`, `busy`.
- `hasMethod(api, name)`, and `getLibraryHub`'s `opts.by` and `opts.for` to name your plugin, and what it needs books for, in its messages.
- `LIBRARY_HUB_TOOLS` is also at `@insurgently/library-hub-api/tools`. Import it from there: at the root it's deprecated, and goes at 1.0.
- New error code `needs-topic-map`: the call works from what books are about (book vectors), which Library Hub no longer keeps; the Topic Map plugin serves it.
- **Changed:** `suggestShelves` is served by Topic Map: with Topic Map's `rankGroups` it ranks the shelves by what the book and their books are about (`score` and `match` are Topic Map's score, `because` says it in words); without Topic Map, or with one that doesn't offer `rankGroups` yet, it rejects with `needs-topic-map` after checking its arguments, and with `no-vectors` when Topic Map hasn't indexed the book. The `library-hub.suggest_shelves` tool is offered to Model Hub only while Topic Map serves it.
- **Changed:** `shelfFromDescription` and `estimateShelfFromDescription` find the books to read by their words: those that best match the description and its terms (title, authors and context, by a keyword ranking), and any naming a term. Before, the books nearest it by meaning. Neither rejects with `no-vectors`. With Topic Map's `rankGroups` there, the books it finds closest to the description in meaning are read too (Topic Map embeds the description); without it, no embedding is asked.
- **Changed:** the shelf keys have new names. A shelf note's id is `shelf_id` (was `collection_id`); a book note's shelves are in `shelves` and its exclusions in `shelves_exclude` (were `collections` and `collections_exclude`); `clusters` and `auto_shelves` are gone, and rule shelves with them: the plugin moves old notes once, putting every book a rule shelf held in `shelves`. `listShelves`, `shelfMembers`, `shelvesOf` and the write operations name shelves by `shelf_id` as before and keep the keys to themselves; a plugin that reads book notes' frontmatter itself reads the new keys.
- **Changed:** `shelfBaseQuery` gives `list(shelves).contains(…)` and the `shelves_exclude` test, with no rule and no older keys.
- **Changed:** `createShelf` with a `rule` rejects with `needs-topic-map`. `NewShelf.rule` and `ShelfInfo.rule` are deprecated (`rule` is never set), until 1.0.
- **Changed:** `no-vectors` now means Topic Map hasn't indexed the library, or the book, yet.
- **Changed:** `pdfText` lays the page out: a line on its own line, a wider gap a blank line, a word broken at a line's end joined. Before, a page's pieces were joined by spaces. `chars a-b` offsets count in the new text.
- The names from before the rename (`getBooks`, `onBooksReady`, `BooksApi`, `BooksErrorCode`, `BooksTool`, `GetBooksResult` and the `BOOKS_*` constants) still work, deprecated. Within 0.x the API only adds and deprecates; they go at 1.0.

## 0.7.0 and before

Not published as a package. Plugins copied `api/index.ts` from the repo; the plugin's own CHANGELOG lists what each version added.
