Skip to main content

1.2.0

Added

  • International text support now extends across the editing and conversion pipeline. Unicode-aware line breaking, search, list numbering, font fallback, and IME composition preserve multilingual text more reliably, and text import now detects UTF-8 and UTF-16 files. HTML, DOCX, RTF, ODT, and plain-text conversions keep the expanded script coverage intact.
  • Orange and purple join the SDK highlight palette. The editor UI, find_highlights, style_blocks, agent HTML, and DOCX round trips now share the same seven canonical highlight colors.
  • Agent styling covers richer document structures. style_blocks adds span, descendant-wildcard, first-row, and named-paragraph-style selectors, with durable mappings for image sizing and alignment, paragraph borders, nested tables and lists, per-edge cell borders, cell fills, padding, and table-cell text styles.

Fixed

  • DOCX import follows Word’s effective paragraph and table styling. Unstyled paragraphs inherit the document’s default style; exact and at-least line rules use the resolved font size; paragraph before/after and contextual spacing replace the editor’s fallback gap; missing table borders remain borderless; and non-uniform cell margins survive import. The imported spacing persists through Yjs, paragraph splits and merges, HTML, DOCX, and PDF output, keeping compact Word layouts from growing onto extra pages.
  • Unavailable Word fonts use the document’s own substitution hints. DOCX import reads fontTable.xml alternate names and PANOSE/family/pitch metadata, persists them with the document, and chooses metric-compatible fallbacks before resorting to a generic family.
  • Multilingual PDF export embeds and shapes the scripts it needs. Chinese, Japanese, Korean, Indic, Southeast Asian, Tibetan, Ethiopic, Armenian, Georgian, Syriac, Thaana, symbols, emoji, and rare Han use bundled lazy fallback faces, while a hidden logical-text layer preserves search and text extraction.
  • Agent edits preserve complex formatting and pending review state. Styled tables, nested content, list-item formatting, explicit text overrides, comments, and edits layered over pending insertions now survive preview, acceptance, and rejection without duplicating or flattening structure.
  • Borderless layout tables stay visually borderless while editing. The canvas now reveals only the specific invisible vertical edge under a resize pointer instead of ghosting the table’s entire grid on hover or focus.
  • Imported pale-yellow highlights remain highlights. HTML clipboard import no longer remaps Word’s pale yellow to the editor’s green palette entry.

1.1.0

Added

  • First-class right-to-left editing. The built-in toolbar can set paragraph and list direction to automatic, LTR, or RTL. Mixed Hebrew, Arabic, and left-to-right text now uses bidi-aware line layout, visual arrow-key movement, stable caret and selection geometry, direction-aware alignment and indents, mirrored list markers, and RTL tab stops. Direction survives Yjs, clipboard and agent edits, HTML, and DOCX round trips; PDF export preserves mixed-direction reading order and positioning.
  • Multi-column sections. Page layout now supports one, two, or three newspaper-style columns with a configurable gap. Continuous section breaks balance their final columns, while page and next-page sections keep ordinary pagination. The canvas editor and PDF export share the flow rules, DOCX and HTML import/export preserve the settings, and set_page_layout exposes columnCount, columnGap, and section-scoped updates to browser and server agent sessions.
  • Native document-structure tools. insert_block can create a live table of contents with <toc levels="3"></toc>. The canonical remove_blocks tool can delete a counted range, exact IDs, a saved search result, or an explicitly authorized document tail with through_end.

Changed

  • Agent tool sessions avoid unproductive read cycles. Sequential reads now reject windows containing only blocks already returned in the same request with no_new_read_context; read_specific_blocks remains available for intentional revisits. remove_blocks is now the model-facing name, while the previous remove_block name remains an executable compatibility alias.

Fixed

  • Strict-provider nulls behave like omitted optional tool fields. Browser and server tool execution normalize provider-materialized null values from the generated schema before dispatch, without stripping required or deliberately nullable values. Optional layout and mutation inputs therefore no longer take invalid branches merely because a provider filled them in.
  • Agent-generated Unicode escapes become the intended text. Literal \uXXXX sequences in agent HTML and replacement paths are decoded without disturbing escaped backslashes or code spans, preventing visible escape text in edited documents.
  • Review decisions preserve the reader’s place. Accepting or rejecting a tracked change no longer forces the embedded editor to scroll back to the active suggestion.
  • SDK PDF export keeps Hebrew and Arabic text self-contained. The package now bundles the Noto fallback faces used by RTL export, so embedding hosts do not need to mirror Revise’s public font directory to avoid dropped glyphs.

1.0.1

Fixed

  • Malformed model input can no longer crash a host’s agent loop. A bare string where search_document expects a queries array — the most common model slip — threw a raw TypeError out of execute() / executeDynamic() instead of returning a structured failure. Bare strings are now coerced to one-element arrays (also for read_specific_blocksblock_ids), anything uncoercible fails structurally, and both surfaces gained a safety net that converts an unexpected handler throw into an internal_error result. The documented contract — expected failures as results, only environment errors reject — now holds for arbitrary input.
  • Invalid enum values no longer mutate the wrong target silently. An insert tool called with position: "above" placed content after the reference block while reporting success, and set_header_footer with a misspelled or missing side edited the header. Both now return a structured failure naming the valid values.

1.0.0

One package, one tool contract. The browser and server surfaces now speak the same canonical envelope, so host result-handling code is shared verbatim between web and Node. That convergence is breaking on the browser side — every change is listed below.

Breaking

  • tools.execute() returns the canonical discriminated envelope. The flat { toolCallId, name, success, error?, agentFeedback?, output?, documentContent? } result is gone. Both surfaces now return { ok: true, value: { callId, tool, message, data, context, suggestionIds } } or { ok: false, error: { callId, tool, code, message } } (ReviseToolResult). Field mapping: toolCallId → value.callId, name → value.tool, agentFeedback → value.message, output → value.data, documentContent → value.context.html; view_image’s attachment lives on value.image. ReviseEditorToolExecutionResult no longer exists.
  • tools.execute() is typed and no longer routes document_id. execute()/call() take the generated per-tool input types and cover the shared 24-tool contract; untrusted model calls — including the browser-only get_selection, view_image, and revise_run_agent, and model-facing document_id routing — go through the new executeDynamic(), exactly as on the server.
  • Unknown tools and role denials are results, not throws. execute() returns { ok: false } with code: "unknown_tool" or "role_not_permitted" instead of throwing; the new tools.call() throws a typed ReviseToolError for hosts that prefer exceptions. ReviseRoleError is still thrown by agent.run() and UI controllers.
  • ID-keyed suggestion decisions return per-ID outcomes. review.acceptSuggestions/rejectSuggestions, review.acceptCommentSuggestions/rejectCommentSuggestions, and tools.acceptAllSuggestions/rejectAllSuggestions, and the server session’s acceptSuggestions/rejectSuggestions return { resolved, missing, unresolved } (ReviseSuggestionDecision) instead of a bare count. Stale IDs land in missing; a role that may not resolve reports everything unresolved. review.acceptAll() / rejectAll() keep their boolean UI-gesture contract.
  • Server sessions default to suggesting mode. createServerDocumentSession without mode now proposes tracked changes instead of applying edits directly — the same default posture as the browser surface, and the safe one. Pass mode: "editing" (or per-call directMode: true) for direct application.

Added

  • Browser mutation results report created suggestion IDs. Successful mutations carry suggestionIds — the tracked records that call created (empty for direct edits, null for read/search/measure tools) — with a concurrent human suggestion never attributed to the tool call. Feed them straight to review.acceptSuggestions().
  • The tool contract types ship from both entries. ReviseToolResult, ReviseToolResponse, ReviseToolFailure, ReviseToolError, ReviseSuggestionDecision, the generated ReviseToolInputMap, and friends are exported by @reviseio/sdk and @reviseio/sdk/backend alike.
  • Server mutation results report the tracked records they created. Successful mutation calls from createServerDocumentSession now carry suggestionIds — the tracked suggestion records that call created (empty for direct edits, null for read/search/measure tools) — so a host can persist per-edit IDs with its review workflow instead of diffing the document-global pending set.
  • listSuggestions() returns every pending suggestion as a reviewable record with authorship metadata (authorType, agentName, agentModel, source, label, createdAt), so hosts can decide on their own agent’s suggestions and leave collaborators’ pending work alone.
  • acceptAllSuggestions() / rejectAllSuggestions() make the whole-document decision an explicit, greppable call instead of the acceptSuggestions(getPendingSuggestionIds()) idiom.

Fixed

  • Server tool types now resolve for moduleResolution: "NodeNext" consumers. The packaged declarations re-exported the server tool contract through extensionless relative specifiers, which NodeNext cannot resolve — every tool input/output type silently degraded to any, and importing a contract type by name (e.g. ServerDocumentSession) failed to compile. The declarations now use explicit .js specifiers, which every supported resolution mode maps to the sibling .d.ts files.
  • Importing a document with code blocks no longer risks crashing a Node host. The canvas syntax highlighter tried to fetch its tree-sitter wasm under Node (the server DOM shims install a jsdom window, defeating its browser check), failing as an unhandled promise rejection. Highlighting now recognizes the server runtime explicitly and stays off, and a failed highlighter init in the browser no longer poisons later attempts.
  • Table and list mutations no longer spam Invalid access warnings. Building table rows, cells, and list items called Yjs push() on elements not yet integrated into a document, which logs a warning per child. Construction now inserts children at explicit indices — dozens of stderr lines per table insert in server logs, gone.
  • Editing mode applies server edits directly again. A server session in "editing" mode quietly recorded every mutation as a pending tracked suggestion instead of applying it. Editing-mode calls now run in the runtime’s direct mode and settle within the call, matching the documented behavior; an explicit per-call directMode still overrides in either direction.

0.9.0

Added

  • review.listChanges() now reports linked moves as moves. A move pair surfaces as ONE ReviseTrackedChange with kind: "move", moveSourceBlockIds/moveDestinationBlockIds locating each half, and deletedText/insertedText carrying the text as it left and as it arrived. Previously both halves were folded into a single change labeled "delete", leaving a host review panel no way to distinguish a move from a replacement. Accept/reject semantics are unchanged: resolving the change settles both locations atomically.

0.8.0

Added

  • Cut and paste now creates native linked moves in Suggesting mode. Cutting text from supported paragraphs and pasting that same internal clipboard payload elsewhere in the document produces one atomic “Moved from”/“Moved here” pair. Accepting or rejecting either half resolves both locations, undo cannot strand an orphaned half, and DOCX export/reimport preserves native linked move markup. Copy/paste, editing mode, reused or mismatched clipboard payloads, and unsupported structural selections continue to use ordinary insertion/deletion behavior.

0.7.0

Added

  • Server-side semantic tools. await createServerDocumentSession(ydoc, { documentId, mode }) in @reviseio/sdk/backend binds the canonical document-local agent tools — read, search, measure, mutate, layout, footnotes, tables, comments — to a host-owned Y.Doc under plain Node. mode: "suggesting" produces Word-compatible tracked changes with accept/reject; concurrent calls are serialized; literal tool calls infer their schema inputs and structured outputs. See the README’s “Server semantic tools” section.
  • Linked Word moves now round-trip as atomic move suggestions. DOCX import pairs native w:moveFrom and w:moveTo ranges across runs, paragraphs, and table cells. Accepting either half keeps the text at its destination; rejecting either half restores its original location; export re-emits native paired move markup. Suggestion cards distinguish “Moved from” and “Moved here”, and focusing either half highlights its partner even across paragraphs.

Fixed

  • Node-side DOCX imports preserve tracked table-row revisions without a browser FileReader. The backend DOM shims and table-revision postprocessor now work from Blob.arrayBuffer(), retaining row insertion and deletion metadata in server integrations.
  • Word comment anchors preserve their identity and semantic range. Safe native numeric IDs no longer shift, cross-paragraph and table-cell ranges emit one anchor triple instead of duplicates, and collapsed, overlapping, threaded, and resolved comments survive repeated round trips.
  • Visible drawing text survives DOCX import when shape geometry is flattened. Inline and anchored DrawingML, grouped shapes, legacy VML, headers, and standards-valid mc:AlternateContent Choice/Fallback content become editable paragraphs in reading order without duplicate fallback text.
  • DOCX archives no longer inflate and large imports avoid repeated work. Generated packages use DEFLATE compression, parse the main document XML once, and skip move indexing when a file has no moves, preventing the reported multi-fold output growth and superlinear no-move scan.

0.6.0

Added

  • Horizontal rules and ornamental dividers are now supported throughout the editor. Insert thin, thick, double, dashed, dotted, star, or diamond dividers from the toolbar or context menu, or create them with text triggers such as ---, ===, -**-, and -<>-. DOCX, HTML, Markdown, agent HTML, PDF, and plain-text output preserve the divider where the format permits; DOCX, HTML, Markdown, RTF, and ODT imports recognize their native divider or paragraph-border representations.

Changed

  • Em-dash autocorrection now waits for a word boundary. Typing -- remains literal until Space or Enter, allowing --- to be used for horizontal-rule insertion and preserving word-initial values such as --flag.

Fixed

  • Words no longer wrap in the middle at formatting or tracked-change run boundaries. A word split across differently styled text nodes now wraps as one unit and keeps a stable layout when suggestions are accepted.
  • Suggestion editing produces stable runs and caret placement. Continued typing or backspacing merges into the existing suggestion instead of creating one run per keystroke, replacement normalization no longer leaves the caret inside hidden deleted text, and Enter after a pending deletion creates the expected paragraph or list item without moving the deleted content.
  • Structural editing keeps the expected document shape and keyboard focus. Backspace exits or splits empty list items correctly, compatible lists rejoin when their separating empty paragraph is removed, Enter at the start of a leading heading or code block creates a body paragraph above it, and toolbar commands no longer steal keyboard focus from the editor.

0.5.1

Fixed

  • PDF table-of-contents page numbers stay accurate when a heading moves to the next page. Export now records a heading’s destination after paragraph pagination, so a keep-with-next or end-of-page preflight cannot leave the TOC pointing at the page the heading would have occupied before it moved.

0.5.0

Added

  • Comments now work on list items, including nested items. Selecting list text, expanding a word from the caret, and commenting on a whole item all create item-local anchors. Returning the caret to that text activates the thread, list-item threads survive agent-HTML round trips, and the leave_comment agent tool accepts list-item IDs.

Changed

  • PDF export now follows the editor’s layout and print semantics much more closely. It shares wrapping and pagination rules for tab stops and leaders, hyphenation, keep-with-next, keep-lines, widows and orphans, code blocks, nested tables, footnotes, page and section breaks, live TOC page numbers, line numbers, and watermarks. Editor-only placeholders and review chrome are not printed, and Word hidden text remains hidden in ordinary PDF output. The SDK now carries its metric-compatible PDF fonts itself, so an embedding host does not need to mirror Revise’s public font directory.
  • Agent tool sessions reject redundant full-document read loops. After a session has read the complete document, another sequential read_document returns no_new_read_context; agents can still revisit known content with read_specific_blocks.

Fixed

  • DOCX formatting exceptions survive a complete edit and export cycle. Explicit run-property clears over named styles, zero paragraph indents, paragraph border and shading clears, decimal font sizes, and mixed small-cap and all-cap overrides now remain distinct from inherited formatting and do not leak into adjacent text.
  • Complex Word structures no longer lose modeled content on re-export. Internal bookmark links stay internal; exact table widths, columns, alignment, cell padding, and vertical alignment survive; nested tables keep the paragraphs around them; display equations are not flattened when a file is immediately re-exported; and multi-block footnotes and endnotes can retain their lists and tables.
  • Accepting or rejecting a compound agent edit is atomic. Mixed text, formatting, and structural suggestions are resolved together, so accepting a rewrite no longer retains deleted fragments and rejecting it restores the original content and formatting.
  • Comment cards stay where users put them. A previously active card no longer drifts with the viewport, and selecting text inside a card no longer jumps focus back to the document or clears the selection. Inline code in comment bodies is also styled as code.
  • Package managers can no longer omit the Yjs runtime. Every editor session is Yjs-backed even without a collaboration provider, so yjs and y-protocols are now required peers instead of optional peers. This makes package managers install or validate them instead of letting bundlers substitute empty optional-peer modules and fail the consumer build.

0.4.5

Fixed

  • Starting a list no longer drops the font. Typing a list trigger (- , 1. , [] ) in a paragraph set in a non-default font produced a list item that fell back to the default font: clearing the trigger text left an empty item with nowhere to carry its formatting. The formatting at the trigger’s trailing edge — the font family included — is now preserved on the empty item and applies to the next character typed.

0.4.4

Fixed

  • Suggestion cards now hang from the suggested text. The floating accept/reject card anchored to the caret; it now follows the suggested fragment (or your selection) and sits centered below its line, the same placement the revise.io app uses, falling back to the caret only when the fragment cannot be measured. Wide cards are clamped to the page edges instead of a fixed margin.

0.4.3

Identical in content to 0.4.2; republished.

0.4.2

Fixed

  • Word-level diffs misplaced edits next to repeated words. When the text adjacent to an edit repeated a word from the edit itself (replacing “[Berkshire County / appropriate Massachusetts county],” with “Suffolk County, Massachusetts,” just before “Massachusetts will”), the unchanged suffix could be drawn as deleted and retyped. Unchanged repeated words now stay anchored as unchanged, in tracked changes and the diff view alike.
  • Writing a comment no longer collapses the comments margin. Finishing a draft that had itself opened the margin always collapsed it — even when submitting had just created a thread, so the pages jerked sideways in both directions and hid the card the user just wrote. The margin now stays open on the new thread; only an abandoned draft with no other open thread collapses it.
  • Comment card placement around drafts and tracked changes. A comment draft started while hovering the card stack could pin its card — and drag the viewport — to the top of page 1; it now anchors where the draft was made. A caret inside a resolved thread’s highlight, or inside an imported thread’s replies, activated no card at all; it now activates the right open thread. And when a commented paragraph also contains tracked changes, the card describes the change under the cursor instead of jumping to the top of the paragraph.
  • The selection card no longer chases the pointer mid-drag. While the mouse button is down nothing pops up under the pointer; the card appears on release, anchored below the selection the user meant.

0.4.1

Fixed

  • Opening the export menu crashed. An icon component in the export menu referenced React without importing it, which the packaged build has no global to fall back on.

0.4.0

Changed

  • The default entry no longer ships Tree-sitter syntax highlighting or the embedded WASM payload. Code blocks still render as code, and every WASM hot path has a TypeScript implementation. This keeps the normal integration smaller and compatible with strict Content Security Policies. Optional features now load as async chunks inside the package.

Added

  • @reviseio/sdk/full — an opt-in entry point that keeps token-coloured code blocks and the WASM hot paths. The API and stylesheet are identical between the two entries.

0.3.4

Fixed

  • The SDK no longer attempts any telemetry. Earlier builds emitted editor health metrics toward a Revise backend that does not exist in your deployment, which could only fail — loudly, as console CORS errors — and represented network calls you never asked for. Telemetry is now something only the revise.io application itself can switch on; embedded editors send nothing anywhere: no metric POSTs, no analytics vendor code, no beacons.

Added

  • onAnalyticsEvent — the editor’s own instrumentation, delivered to the host instead: <ReviseEditor onAnalyticsEvent={(event, properties) => ...} />. Forward events to your own analytics service; a throwing handler never breaks the editor. Event names and property shapes are internal and version-unstable.

0.3.3

Four suggesting-mode fixes, two of them content corruption. If your users edit in suggesting mode — the mode this SDK exists for — take this release.

Fixed

  • Pasting in suggesting mode corrupted the document. The paste path predated suggesting mode: it duplicated the rest of the paragraph and inserted the pasted text without tracked-change marks, so the paste was invisible to review and survived reject. Pasting is now recorded exactly as if the pasted characters had been typed — an insertion suggestion, plus a deletion suggestion for any replaced selection.
  • Formatting across a pending change destroyed it. Applying bold (or any inline format) over a range touching a pending insertion or deletion overwrote the change’s identity; resolving the format then silently turned a pending insertion into accepted text, or resurrected deleted text. Formatting now leaves deletion spans alone, folds into insertion spans (as Word nests run properties inside w:ins), and records a reviewable format change only on unmarked text.
  • Accepting a deletion that crossed a paragraph boundary left the paragraphs unmerged. The boundary is now part of the suggestion, as the pilcrow is in Word: accepting merges the paragraphs, in any resolution order, for any number of paragraphs in the selection.
  • Copying a selection inside your own pending insertion copied nothing. It now copies the selected text. Clipboard payloads are also sanitized so tracked-change marks never travel with copied content.

Console

  • Opening a document no longer logs to the console (previously one Yjs warning per block plus two internal debug lines). console.warn and console.error remain for genuinely actionable problems.

0.3.2

Fixed

  • editor.whenReady() was missing from the published type declarations. It worked at runtime, but TypeScript rejected the call the documentation tells you to make.

0.3.1

Fixed

  • Using the editor handle from onReady no longer throws. onReady fires before any document exists, and “subscribe to everything on ready” is the first thing a host writes — a subscription placed then now waits and attaches when a document arrives. selection.observe() was affected too. Methods that act on a document still fail loudly, and the message now names the fix.

Added

  • editor.whenReady() — resolves once a document is open and its controllers are usable.
  • REVISE_SDK_VERSION and REVISE_SDK_BUILD exports, and a bugs entry in the manifest: a support ticket can name the exact build it came from.

0.3.0

Collaboration

Multi-user editing over a Yjs transport you own. The editor builds the document; you attach the connection:
Remote carets, a presence roster (editor.collaboration), seed-or-join semantics, and validation of any document you supply. See the collaboration guide.

Roles

role="editor" | "suggester" | "viewer", editor-wide or per document, and enforced on every surface: typing, the built-in chrome, the handle, and agent tool calls. A suggester cannot leave suggesting mode, apply anything directly, or accept and reject.

Tracked changes you can list

review.listChanges() and review.getChange(id) return each pending change with its kind, author, timestamp, affected blocks, and text — enough to build your own review panel. Imported Word redlines keep their original reviewers.

Server-side primitives

New subpath export @reviseio/sdk/backend (Node only): parseDocument, fileToYDoc, seedYDocFromFile, ydocToDocx, encodeYDoc/decodeYDoc. Your server can create a collaborative room from a .docx before any browser connects, and export a live room without one. Requires jsdom.

Breaking

  • yjs and y-protocols are now peer dependencies and are no longer bundled. Install them yourself, and make sure your bundler resolves ONE copy — Yjs identifies its own types with instanceof, so a second copy breaks a shared document. Only collaboration needs them; they are optional peers.
  • ReviseDocumentInput.docx is now optional, since a document joining a collaborative room needs no source file. Inputs must supply docx, collaboration, or both.

Fixed

  • search_result_id, which the agent tool schemas advertise for bulk edits, was unusable: tool state did not survive between tools.execute() calls.
  • exportDocx() omitted TOC page numbers, which only the live layout knows.
  • Exports lost imported named styles and reset core property dates in any non-browser environment.
  • collaboration.synced was read once at mount, so a host reporting sync later was ignored and the document never opened.
  • The margin insert-comment chip was missing from the embedded editor.
  • Presence colours: an explicit currentUser.color was ignored whenever the user had an id.

0.2.0

Multi-document sessions, host-owned chrome, skinning props, per-embed theming, the selection controller, and comment-thread agents.

0.1.0

First release: the editor, DOCX in and out, tracked changes, comments, and the agent tool surface.