更新日誌

每一版改了什麼,全部在這裡。

內容與 repo 的 CHANGELOG.md 同源;正文保留英文原文。預編譯檔在 GitHub Releases。

v1.3.0最新版本 · LATEST2026-09-01

新增 · Added

  • The interactive session knows a book is half finished. The book list marks any book with an unfinished job cache beside it ("8/12 chapters done"); the confirm screen prices and describes only the chapters still to run, and says plainly that the finished ones are not billed again; the progress board opens with those chapters already ticked and tagged cached, so they read as paid-for rather than impossibly fast; and the completion summary reports how many segments (and notes) came back from the cache — a number that previously only ever reached the flag-driven output. Two things the screen now says out loud instead of leaving to be discovered: changing the model or the depth voids the whole cache, and changing "Save to" moves the job cache with it and leaves the finished work behind.
  • A chapter counts as finished only when it is actually cached. The job's chapter status was recorded as done even when every call in the chapter had failed and nothing was cached. It is now partial in that case, so a resume can never quote a discount the next run does not honour (a whole book that failed against an unreachable endpoint would otherwise have priced as almost free).

改進 · Changed

  • Product term (zh-Hant / zh-Hans): annotations are 註解 / 注解 in the interactive session and docs/READER-PROFILE.md, matching README.zh-TW.md and the rest of the Chinese-language surface. English ("margin notes"), Japanese (注釈) and Korean (주석) are unchanged. Internal identifiers — the annotate subcommand, the --note-* flags, the annotation cache salt — are untouched, so no existing cache is invalidated.
  • The interactive session is fully localised again. The banner tagline and the main menu's own key legend were hard-coded English under a translated menu; so were two keys in the paragraph editor's legend. All now go through the same five-language table as everything else.

修復 · Fixed

  • The model picker no longer overwrites the model you saved. When the local Ollama scan came up empty (the ollama binary not on PATH, the daemon slow to answer) the picker fell back to a curated list; when it succeeded it offered the scan in its own order. Either way the saved model was silently replaced by the first entry, and written back to settings.json. Because the model is part of the cache signature, opening the config screen could void every finished chapter of a half-translated book and point the run at a model that is not installed — every unit then fails and the output is a book of untranslated text. The picker now always offers the configured model, and keeps the selection by name when the list changes.
  • Chinese and Japanese punctuation is normalised at the writer. Some runs came back with half-width , : between Han characters while others, same book and same model, did not. Marks are widened only when the visible neighbours on both sides are CJK, so quoted English, thousands separators, decimals, URLs and markup attributes are untouched; Japanese takes 、; Korean is deliberately left alone (modern horizontal Korean sets the Latin comma and full stop). The prompt says so too, but the writer is the fuse. Prompt wording has never entered the cache signature, so existing caches re-render through the fix at no cost.
  • Subscription runs report what they actually spent. Only usage.input_tokens was counted, and under prompt caching almost every input token arrives in the cache fields instead: one real annotate run reported 7 input tokens against an actual 10,851. All three buckets are now counted, weighted at the provider boundary into a billable figure (cache reads and cache writes do not cost the same as fresh input), with the honest total still reported as tokens_in. The Codex path had the mirror-image bug: its input_tokens already includes cached tokens, so the whole packet was being priced at full rate. claude-sonnet-5 was also priced at $3/$15 by a contains("claude-sonnet") match; the published rate is $2/$10.
v1.2.72026-08-24

新增 · Added

  • translatus doctor. One command (add --json for machines) that checks binary/PATH self-consistency (including the "install replaced while running" state), the subscription sidecar kit and Node, the default sidecar port, per-client MCP registration, and the settings file. Exit 1 only on a hard failure.
  • --client claude|codex on mcp install / mcp uninstall. Install records which registrations it created; a bare uninstall now removes only those — never a registration the user or another tool made — and --client targets one client explicitly.
  • --json runs open with a run_start header (version + timestamp), so interrupted-and-resumed runs appended to one log stay separable.

改進 · Changed

  • estimate accepts translate-only flags and ignores them (with a stderr note), so a translate command line can be replayed as an estimate by swapping the verb instead of hitting a hard argument error.
  • MCP tool schemas state the base_url credential rule inline: stored tokens are attached only when the value exactly matches the endpoint saved in Settings — agents no longer need the guide to explain a silent 401.
  • Subscription sidecar: the Claude SDK's native binary is resolved once at startup and pinned, its path logged; a port conflict at startup names LLM_SUB_KIT_PORT, and a mid-run "Native CLI binary not found" (install upgraded/removed underneath a running server) now explains itself.
v1.2.62026-08-24

改進 · Changed

  • Bilingual is now the CLI default layout. translate without --mode produces original + translation interleaved, matching the desktop default; pass --mode replace for translation-only output. The cache signature does not include the layout, so existing job caches re-render either way at zero cost.
v1.2.52026-08-24

修復 · Fixed

  • Vertical books get their accents on the correct side. In vertical-rl (tategumi) and rtl-progression books the block axis runs right-to-left, so the left-edge accent that marks translations and margin notes landed on the far side of the block — visually attached to the following paragraph. Those books now receive mirrored stylesheets: right-edge accent, mirrored padding and inter-block margins. Horizontal books are unchanged, and the choice is regression-locked.
v1.2.42026-08-24

改進 · Changed

  • Tightened an internal source comment. No functional changes.
v1.2.32026-08-23

修復 · Fixed

  • Margin notes are tuned for e-ink readers. Monochrome panels collapse colour to luminance and usually implement dark mode as inversion; the low-alpha vermilion tint could vanish or dither there. A final @media (monochrome) layer now drops the tint and returns note text to the device's own ink; the accent border and note prefix carry the distinction. Every accent colour was audited to keep ≥5:1 luminance contrast against its ground (note ink >10:1), so greyscale and desaturating colour e-ink panels (Kaleido) stay legible in both light and dark modes.
v1.2.22026-08-23

修復 · Fixed

  • Translations and margin notes are readable in dark mode. The CSS injected into bilingual EPUBs hardcoded near-black text (#222 on translations, #5c2d24 on notes), which disappeared into the background on night themes. The default rules now inherit the reader theme's text colour everywhere; the indigo/vermilion accents are tuned per scheme behind prefers-color-scheme guards, with a pale-vermilion note ink on dark grounds. Regression-locked so no future default rule can hardcode a text colour again.
v1.2.12026-08-20

修復 · Fixed

  • A Homebrew install can find the sidecar it was just given. bin/translatus is a symlink into the Cellar and current_exe returns the link, so the libexec lookup landed on /opt/homebrew/libexec — where the formula installs nothing. The path is canonicalised before its prefix is derived.
v1.2.02026-08-20

新增 · Added

  • --provider subscription: translatus starts the sidecar itself. Reaching a Codex/Claude plan used to be homework — npm install, npm start, copy the token it prints, paste it into Settings, keep the terminal open — and every step was something the program already knew how to do: the kit ships in the release archive, the command is fixed, the token is generated by the server. One flag now allocates a free loopback port, generates a per-run token, starts the sidecar, waits for it, and shuts it down when the run ends.

    This is not only convenience. The pasted token had to live in the OS keychain, and an ad-hoc-signed binary cannot read its own keychain items without an authorisation dialog — which nobody can click in an agent session, so the MCP path returned 401 even when everything was configured correctly. A sidecar owned by the run needs no keychain at all.

    subscription is a CLI-level alias, not an engine provider, so the cache signature is unchanged: switching between a managed sidecar and a hand-started one never re-bills a book.

  • Homebrew installs the sidecar. The formula shipped the binary alone, which left the documented first install path unable to use the documented first model source. It now installs the kit under libexec and depends on Node, with dependencies resolved at install time so the first translation does not stall on npm.

改進 · Changed

  • The MCP server falls back to the managed sidecar instead of refusing. With nothing configured it now runs --provider subscription, so an agent handed a book and no other information can actually translate it.
v1.1.92026-08-20

修復 · Fixed

  • The MCP server no longer dry-runs a book in silence. --provider defaults to mock on the flag interface, which is correct there: a dry run is something you ask for. Over MCP it was not. An agent asked to translate a book has no reason to pass a provider — the parameter is optional and its description called mock the default — so a real request went to the mock provider, finished in a minute with zero model calls, wrote a 962 KB EPUB containing 509 placeholder translations, returned status: completed, and the agent told its user the book was translated. A wrong book reported as a right one is worse than any error.

    When no provider is named, the server now uses the model source saved in the user's settings, and refuses with an actionable message when there is none. An explicit provider (including mock) and cache_only are untouched.

v1.1.82026-08-20

修復 · Fixed

  • The confirm row agrees with the estimate above it. 1.1.5 moved subscription runs from money to work volume, but only in the block; the row the user actually presses still read ~$0.83. One screen carried two framings of the same run, and the money one was the one being agreed to.
v1.1.72026-08-20

Carries 1.1.5 and 1.1.6, whose releases never built (see below).

修復 · Fixed

  • CI and release runs can no longer burn the six-hour job ceiling. apt-get on a hosted runner stalled with no timeout anywhere in either workflow, so a single stalled mirror ran to the 6h maximum and was cancelled: on 2026-08-19 that killed one CI run and both release runs, and the v1.1.5 and v1.1.6 tags ended up with no published artifacts at all. Every job now has a timeout-minutes, and the apt step bounds each attempt at 180s and retries three times before giving up. The macOS builds in the same matrix finished in under two minutes throughout — only the Linux job hung.
v1.1.62026-08-20

Found by translating a whole real book rather than an excerpt.

修復 · Fixed

  • A cached translation can no longer put the copyright page back. 1.1.5 stopped sending apparatus to a model, but the writer still used whatever the job cache held — and a cache written before the rule existed had the page translated. Apparatus is now emitted from the source regardless of the cache.
  • The run log says skipped (publisher apparatus, sent to no model) instead of 0 translated, which was indistinguishable from a fully-cached chapter.
  • Margin notes get the same interpunct normalisation as the translation. Notes name people constantly; 28 of 33 stray separators in a 1,850-segment book were inside notes, which the 1.1.5 rule never touched.
  • A name split by inline markup is still a name. Publishers hang endnote anchors off the exact word they annotate, so 大衛.<span id="…"/>佩雷爾 is a normal shape; looking only at the adjacent character saw < and declined. Neighbours are now read past tags, with attribute values left alone. A Latin initial inside a Chinese name (約翰‧D‧洛克斐勒) counts as a name part too.

    Result on the same book: 306 separators, all U+2027, none left over.

v1.1.52026-08-19

新增 · Added

  • The copyright page is never sent to a model. It is the one page in a book that must not be rewritten: the publisher's rights statement, ISBN and cataloguing data. Translating it produces an unauthorised-looking restatement of the text that says the work may not be restated, costs tokens, and gives the reader nothing. Identified from what the book itself declares — EPUB 3 epub:type="copyright-page" landmarks and the EPUB 2 <guide> — and, for books that declare nothing, by a deliberately narrow content check: a short document carrying two or more rights markers at once. Japanese books put the same thing (奥付) at the back, so position is not the test. imprint and colophon are treated the same way. Skipped pages are named in the estimate and on the confirm screen, and the run log says skipped (publisher apparatus, sent to no model) rather than 0 translated — which would have been indistinguishable from a fully-cached chapter. They are written out byte-identical and are never annotated.

改進 · Changed

  • A subscription run is quoted in work, not dollars. A subscription is a flat fee, so a dollar figure was fiction dressed as precision; what is actually being spent is quota and wall time, and token volume is the honest proxy for both. API-key runs still show money.
  • The number is labelled a floor, not an estimate. Measured against real runs it is always low and never high (1.55x on a Latin book at sentence level, far worse on a CJK book at expert level), because prompts, retries and repairs are not modelled. "At least" is what the number actually means.
v1.1.42026-08-19

改進 · Changed

  • The pre-run estimate counts CJK at CJK rates and expert at expert's number of passes. Two things it did not model. Every script was priced at 2.5 characters per token, which is roughly right for Latin and about 2.5x wrong for Han, kana and Hangul. And --level was not an input at all, so a run that reads the whole book for a glossary, drafts, reflects against the source and then re-reads everything for the consistency check was priced as a single pass. Measured on a Japanese book at expert level with notes, the quoted figure moves from $0.35 to $1.42.

    It is still an under-estimate, knowingly. The same run really cost about $24: 154 segments of ruby-dense Japanese produced 276,535 output tokens, or ~1,800 per segment, which no character count predicts. Closing that needs per-phase token accounting inside the engine — the numbers to calibrate against do not exist yet, and inventing a multiplier to cover a 17x residual would only move the error. A Latin book at sentence level is 1.55x out; that one is within the "rough numbers" the screen already claims.

v1.1.32026-08-19

Second pass over the same end-to-end walk: the things the first pass surfaced but did not yet act on.

修復 · Fixed

  • The interactive session honours ^C during a run. It held raw mode for the whole session and polled no keys while translating, so the board's own "^C stop" legend was inert for exactly as long as a run lasted — measured at 20 minutes before the process had to be killed from outside. Raw mode exists to read keys; the board only writes, so it is handed back for the duration and the terminal turns ^C into the signal the process already handles.
  • The session no longer fans out against an endpoint that serves one request at a time. It set --concurrency 4 for every non-Ollama source, including the subscription sidecar its own guide says to keep at 1. Three of every four batches came back 429, which (before 1.1.2) silently cost those units.
  • Foreign-name interpuncts are consistent in Traditional Chinese output. Measured over one 534-segment book: U+30FB (the Japanese katakana middle dot) 30 times, U+FF0E (a fullwidth full stop) 17, U+00B7 (the mainland middle dot) 16, and U+2027 — the Big5 間隔號, the one Traditional Chinese wants — only 4. Between two Han characters the first three are now normalised to U+2027; anywhere else they are left exactly as written.
  • The keychain can no longer hang a run forever. A macOS read can raise an authorisation dialog, routinely after a rebuild changes the binary signature, and the call blocked until somebody clicked it — indistinguishable from working if the display is asleep. It now gives up after 20s and says what to do.

改進 · Changed

  • estimate prices against the model the run will use. It defaulted to gpt-5.4-mini regardless of configuration, so an agent that estimated and then translated with a Claude model reported a number 3.5x under what the run cost. Naming --model still overrides. (The remaining under-count — the estimator models one pass and has no notion of --level — needs measurement, not a new multiplier, and is not addressed here.)
  • Expert level says what "progress is saved" means. It checkpoints at chapter boundaries, so an interruption restarts the chapter in flight; the stop legend and the resume line now say so instead of implying batch granularity.
  • The flag form of a run is printed where it can be copied. The confirm screen truncates it to the width of a box, which made "graduate to the CLI" unkeepable; the full command now also lands in scrollback when the run ends.
v1.1.22026-08-19

修復 · Fixed

  • A busy endpoint is waited for, not counted as a failure. The subscription sidecar serves one paid completion at a time and a book-length one runs for minutes, but its 429 went through the ordinary transient path — two retries, 1s and 2s — and then failed the unit. Starting a second book while one was running therefore put gaps in it. 429 now backs off on the scale of the thing being waited for (15s → 2m).
v1.1.12026-08-19

Found by an end-to-end pass over the real install paths on macOS: Homebrew and the release archive, the interactive session, and an agent driving the MCP server. Every item below reproduced on a clean machine.

修復 · Fixed

  • A failed segment no longer poisons the resume cache. When a provider call failed, the unit fell back to its source text — correct for assembling the book — but that fallback was then written to the job cache. One transient 401 or timeout was therefore permanent: every later run of the same command reported units_failed: 0, restored the source from cache, and handed back an untranslated book, with deleting the .etjob as the only undocumented way out. Failed units are now excluded from both the streaming checkpoint and the chapter-boundary write, so re-running retries them. This is the guarantee in the README ("re-running never re-bills a translated segment") actually holding for the case where it mattered most.
  • A prefixed OPF is read like any other. <opf:item> / <opf:itemref> are the same elements as <item> / <itemref> — the namespace prefix is arbitrary, and real toolchains emit it — but the manifest and spine scanners matched the literal strings. Such books parsed as zero chapters.
  • A book with no readable chapters is an error, not a free success. The above surfaced as chapters: 0, est_cost_usd: 0, status: "completed", which a caller cannot tell apart from an already-finished book.
  • The run screen's estimate no longer reads "~38h56m left". Time remaining was extrapolated from wall-clock elapsed divided by characters completed; expert level's whole-book pre-scan landed entirely on the few front-matter characters that finish first. It now measures throughput from the chapters' own durations and says nothing until 5% of the text has been timed.
  • The run screen names the whole-book passes. The pre-scan and the note planning pass showed a 0% bar and nothing else, for minutes.
  • A dropped inline-emphasis marker costs one extra call, not the paragraph. When the source italicises a word the target language has no separate word for — "there *is* one right way" — the model returns a good sentence with the marker pair missing, validation rejects it, and the unit falls back to its source: one English paragraph inside a translated book, reproducible on the same paragraph every run. The engine now sends one targeted repair naming the markers that went missing, which recovers it; re-rolling the same prompt does not, so this is not simply more retries.
  • Connection-test rows no longer collide. The label column padded by character count, so "Access token" (12 chars in a 10-wide column) rendered as Access tokenpresent; every CJK label was misaligned for the same reason.

改進 · Changed

  • The MCP server can reach the subscription sidecar without extra wiring. A caller-supplied base_url still receives no ambient credential, but the token bound to *the user's own saved endpoint* is now sent when the requested URL matches it exactly — read from the OS keychain, never copied into another program's config file. Previously translatus mcp install produced a registration that could not authenticate against the sidecar it documents, so every subscription translation an agent started came back 401 while the same settings worked from the CLI. TRANSLATUS_MCP_ENDPOINT_URL/_TOKEN still override.
  • The subscription model picker offers the current generation. It listed claude-sonnet-4-6 first and had no Claude 5 entry at all, so a new install silently landed a generation behind what the same subscription serves. claude-sonnet-5 and claude-opus-5 are offered (and priced); the older ids stay selectable, since a job cache is keyed by model.
  • The subscription sidecar's request timeout defaults to 10 minutes, up from 150s. Measured completions for one chapter batch run 48s, 86s and 121s; the old ceiling clipped the largest, and each clip cost the full 150s before the caller could split and retry.
v1.1.02026-08-18

新增 · Added

  • translatus mcp install / mcp uninstall: registers the MCP server with the agent clients found on this machine (Claude Code, Codex) through each one's own CLI rather than by editing its configuration file. The interactive session offers to do it once on first run, so a normal install is one step; package installation itself never writes into another program's config.
v1.0.02026-08-17

First public release.

安全 · Security

  • The Codex and Claude subscription children run with a reduced environment, every built-in tool disabled, and no session persistence; a contextual trusted-book reminder is shown when users choose Codex subscription mode.
  • Hardened bearer-token redaction, redirect handling, settings-file permissions, MCP write paths, CLI path collision checks, and release-workflow permissions.
  • Client disconnects cancel the provider call, and the sidecar serves one paid request at a time.
  • Final book output is an owner-only atomic replacement, target-language filename traversal is blocked, and MCP-selected loopback endpoints never see environment or OS-keychain credentials.

新增 · Added

  • Reader-profile contract (--note-profile, MCP note_profile): one JSON document — purpose, cognitive anchors, presets, voice, language, density, style — that a reader or the reader's own AI assistant fills to steer the margin notes. Schema and the standard hand-to-your-AI prompt: docs/READER-PROFILE.md. Explicit flags override document fields; over MCP only inline JSON is accepted (file paths would read arbitrary server paths).
  • Service menu & explanation level. The preset help angles grew into the single "what should the notes do for you" layer — three new services join the six existing ones: world (connect passages to the real world: later developments, other fields, historical parallels — verified facts only), methods (unpack methods with their conditions, costs and limits) and research (prioritise citable facts, sources, the book's structure, and points of dispute). Picking is enough: with at least one service ticked the free-text profile becomes optional, and the interactive session leads with the picker, demoting free text to "In your own words". A new --note-level beginner|general|insider dial sets how much the notes assume: beginner explains in everyday language with examples; insider only adds what an insider could not easily look up. The session also collects anchors ("You already know") and the voice register.
  • Cognitive anchors (--note-anchors): short labels of what the reader already knows; notes explain new concepts by bridging from that familiar ground (with an accuracy guard: a wrong analogy is worse than none), and are still never allowed to mention the reader.
  • Note voice registers (--note-voice study|companion): the restrained study register stays the default; the companion register writes in a friend-at-your-side tone with a higher share of short reaction notes. Hard rules (neutrality, no reader-addressing, no spoilers) identical in both.
  • Book-wide thread map: the planning pass now also samples mid-chapter text and returns cross-chapter threads (concept first appears → pays off), so the per-chapter selection pass can place notes AFTER an insight lands and reach back to already-read chapters instead of discovering the connection too late. Note text still never previews unread content.
  • Reader-boundary output check: every accepted note (writing and review passes) is program-scanned for reader-addressing phrasings; violations are rejected and rewritten, making "the notes never talk about you" a machine guarantee rather than a prompt promise.

改進 · Changed

  • Annotation cache signature salt bumped (v6 → v7): existing note caches re-annotate under the new prompts; translation caches are untouched and are never re-billed.

新增 · Added

  • Interactive session. Running translatus with no subcommand on a terminal opens a guided flow: it finds the books around you, asks how they should read, quantifies the run (chapters, characters, estimated cost), waits for an explicit confirmation before spending anything, and paints a live progress board with per-chapter status and a remaining-time estimate extrapolated from measured throughput. It prints the equivalent command as you go, so the flags stay learnable. In a pipe or a script a bare translatus prints usage exactly as before, and the --json agent contract is unchanged — the interactive path builds the same arguments and calls the same engine. The session speaks five languages (English, 繁體中文, 简体中文, 日本語, 한국어), following the terminal locale with a TRANSLATUS_LANG override; it offers real model sources only (subscription sidecar, API key, or Ollama — the Ollama model list is read live from ollama list), while the mock provider stays available behind the --provider flag for offline format checks. In subscription mode the Settings screen takes the sidecar's printed access token (stored in the OS keychain like a key) and accepts a sidecar on any loopback port, not just the default 8765.
  • MCP server (translatus mcp): a stdio Model Context Protocol server so agents (Claude Code, or any MCP client) can call estimate_book, translate_book, and annotate_book as first-class tools. Results reuse the --json schema by construction (each call self-execs the CLI in --json mode); long calls stream per-chapter notifications/progress; re-calling a tool with the same arguments resumes from the job cache. Registration: claude mcp add translatus -- translatus mcp. Covered by e2e tests (handshake, tools/list, mock estimate/translate with progress) and declared in CAPABILITIES.toml (mcp-server, CLI-only with reason).
  • Human-mode CLI experience: a cost-estimate line before every real run (estimate logic reused; --json untouched), a fail-fast setup card when a hosted provider has no API key (subscription sidecar / API key / Ollama — the same three choices the interactive launch), a mock-provider dry-run notice, per-chapter and per-note progress lines, a completion summary (segments, notes written/edited/dropped, tokens, cost, resume tip), and a Ctrl-C message explaining that re-running the same command resumes without re-billing. translatus --help now ends with a provider setup card; estimate prints a human summary without --json. All CLI messages are English.
  • docs/GUIDE.md and docs/FAQ.md: a full user guide (install, providers including the subscription sidecar, every flag with examples, cache/resume behavior, JSON mode for agents, troubleshooting) and an honest FAQ (costs, subscription compliance, funding model, data flow, prior-art differences). docs/ANNOTATION-TUNING.md is now in English.
  • Preset help angles for margin notes (--note-presets chips): six fixed ids — terms, history, author, culture, characters, concepts — each mapping to one guidance sentence injected into the selection pass (angle guidance) and the writing pass, right after the reader's free-text profile. Unknown ids are ignored with a warning. The canonical preset set joins annotation_signature (tick a chip → re-annotate; the translation cache is never touched).
  • Per-book annotation settings support in the shared Settings struct (annotations.presets / annotations.density global defaults + annotations.books per-book overrides) for per-book onboarding.

改進 · Changed

  • Margin-note writing framework landed in the annotation prompts: DEFAULT_NOTE_STYLE now encodes the ten craft rules (concrete first sentence, one note = one thing, length varies with content, judgements must anchor to specific words, the reader's likely misreading gets priority, claims must be checkable), the selection pass prioritises spots a reader of this background would misread or miss, and the review pass flags notes that are all the same length. Derived from a cross-tradition study of real annotators (金聖嘆/脂硯齋/毛宗崗, Gardner's *Annotated Alice*, high-voted Genius notes) and validated by a persona blind-review loop. A second pass added: a no-forward-spoiler rule (cross-chapter links may only look back at what the reader has already read, never reveal later plot), a factual-accuracy rule for real-world connections (a note may tie the text to the wider world — later history, other fields — but only with verifiable facts, never fabricated ones), selection guidance that climaxes and famous scenes are not note-spots and every note must map to a concrete reader need, and a native-register requirement (write in the target language's natural voice, no translationese). The annotation cache salt bumped through inkferry-anno-v5 to inkferry-anno-v6 (the prompt texts changed but do not enter the hash on their own); translation caches are untouched.
  • Public mode name: "fast" → "standard" (App: 一般/一般/Standard/標準/ 표준 across the five UI languages). The default single-pass mode is now called *standard mode* everywhere user-facing; --level standard was already accepted and is now the documented spelling, and 一般 joins the accepted aliases. Nothing internal moved: the sentence level value, the cache signature, and the historical aliases (fast, 快速…) all remain valid, so existing caches and scripts are untouched.
  • README: leads with the engine/CLI; product positioning moved to the end and shrank to a short note. English docs now use English-reader examples (--to English); the zh-TW README keeps --to "繁體中文".
  • Project rename: InkFerry → Translatus. The open-source engine + CLI is now Translatus (Latin *translatus*, "carried across" — the root of *translate*). The CLI binary is translatus, the repository is doka-software/translatus. Cache/signature salts (inkferry-anno-*) keep their historical strings so no existing cache is invalidated by the rename; older entries below use the old name as written at the time.
  • Product term rename: the zh-Hant product name for annotations is now 眉批 (was 夾註); en "Margin notes", zh-Hans 批注, ja AI注釈, ko AI 주석 (ja/ko revised 2026-07-19 after a native-corpus audit: in Japanese and Korean ebook UIs メモ/노트 mean the user's own notes, while 注釈/주석 are the native words for provided explanatory notes). Prompts, CLI messages and docs follow. Internal identifiers (annotate module, etc-note class, CLI subcommand/flags) and the in-book 「〔註〕」 marker are unchanged. The annotation cache salt bumped to inkferry-anno-v4 (prompt texts changed + presets joined the recipe); translation caches are untouched.
  • Repository split: non-engine code moved out. This repository is the MIT-licensed engine (et-core) + CLI (inkferry), free forever, and is the single upstream any embedding host consumes as a pinned dependency.
  • Hostile EPUB entry names can no longer ride into the output. Reading was always safe (entries are held in memory, never extracted), but the writer rebuilt the archive from the same names, so an entry called ../../../../tmp/x survived translation verbatim. The translated book was therefore a zip-slip payload carried by a tool the reader trusts, and the next program to unpack it took the hit. Absolute and ..-bearing names are now dropped when the book is read; OCF forbids them anyway, so nothing legitimate is affected.
  • The input file is size-capped before it is read. Every other guard describes decompressed bytes and so could not fire until the file was already resident in memory. A large file with an .epub name — not even a valid archive — was a memory spike before the parser had an opinion.
  • Provider error bodies are scrubbed of credentials. --base-url points at whatever OpenAI-compatible gateway you choose, and a gateway that echoes request headers in an error would put your API key into stderr and, over MCP, into an agent's context. Key-shaped tokens and bearer headers are redacted before the body reaches an error message.
  • A remote base_url must use HTTPS. The rule existed and was tested but nothing called it; --base-url http://… would have sent your key in clear text. It is now enforced where every caller funnels through.
  • The job cache is created owner-only (0600) on Unix. It stores translated passages and margin notes as plaintext and previously inherited the ambient umask, which on a shared machine can mean world-readable.
  • The subscription sidecar refuses to bind anywhere but loopback. It was a default rather than a guarantee: LLM_SUB_KIT_HOST could move it onto the network, and requests without an Origin were trusted on the strength of the Host header, which any client can set to localhost. A non-loopback bind is now fatal at startup, and peer identity is read from the socket.

修復 · Fixed

  • translatus exits non-zero when segments fail. A run that failed every segment still printed done: and exited 0, so a script or CI job checking only the exit code read a total failure as success. The output file and the resumable cache are still written, and the --json summary is unchanged.
  • --provider anthropic is refused instead of silently failing. It built an OpenAI client pointed at api.anthropic.com, which speaks POST /v1/messages with an x-api-key header rather than /chat/completions with a bearer token, so every request failed after the docs had promised it would work. It now fails immediately with a message pointing at the two routes that do work: an OpenAI-compatible gateway via --provider openai --base-url, or the subscription sidecar for Claude.
  • MCP progress and results no longer echo book-controlled filenames, provider diagnostics, subprocess output, or model-authored text into the calling agent's context; only fixed status and numeric metadata cross that boundary.
  • Cost estimates read $0.00 for Claude Opus, Fable and Mythos models, which were never priced in the estimator. All Claude tiers are now priced (verified against the official pages 2026-07-21) and covered by a test that fails if any model the UI offers falls through to zero.

開源之前的版本紀錄保留在 repo 的 CHANGELOG.md 末段。