Changelog
Stable releases, newest first, with notes for every version. Also published on GitHub.
Patch Changes
- Clicking a second empty tab in the Skills editor now switches to it. Every empty Skills tab shares one route (the Skills home), so re-resolving that route while you already had one open snapped you back to the leftmost empty tab. Activation looked frozen: clicking another empty tab did nothing, the + button appeared to open a tab it never took you to, and Cmd-2 wouldn't move either. Empty Files tabs were unaffected. The Skills home now keeps whichever of its tabs is already active instead of always picking the first.
Patch Changes
Desktop: harden updates, reclaim the updater cache, and remove packaged-startup warnings.
- Linux: when no graphical administrator authorization is available (no pkexec or PolicyKit agent), clicking Relaunch now shows a dismissible manual-install dialog with a copyable, shell-quoted package-manager command (
sudo apt install -- '<installer>'/sudo dnf install '<installer>') and an unconditional Relaunch button, instead of failing through terminal sudo. Cancelling the authorization prompt re-arms the update banner without the dialog; the staged installer is preserved until an install succeeds or a newer update replaces it. - All platforms: the updater cache's staged installer (
pending/, ~250 MB) is reclaimed once the installed version is running. Windows additionally stops retaining the NSIS installer's inert differential-update seed copy (installer.exe, ~250 MB) at install time. macOS keeps its separateupdate.zipdifferential-download seed — that copy actively shrinks future mac update downloads and is intentionally untouched. - Packaged startup no longer emits the duplicate-Yjs warning (the
@inkeep/open-knowledgelibrary entry now resolves the sharedyjsfrom node_modules; the standalone CLI binary still inlines it) or theNo handler registered for 'ok:...:renderer-ready'errors (a permanent mount-ack sink now absorbs unarmed renderer-ready pings).
- Linux: when no graphical administrator authorization is available (no pkexec or PolicyKit agent), clicking Relaunch now shows a dismissible manual-install dialog with a copyable, shell-quoted package-manager command (
The editor no longer jumps to a different file on its own while you're reading. Follow-the-file — the feature that walks the editor along as an agent creates and edits pages — was doing two things it shouldn't:
- A background agent could steal your place. Every open agent conversation stays live at once, so an agent working in a tab you weren't looking at could still yank the editor onto whatever file it wrote next. Now only the conversation you're actively viewing (its tab selected, the dock on screen) moves the editor.
- It overrode where you navigated. If you opened another page to read while an agent kept working, its next write pulled you back. Now, once you navigate somewhere the agent didn't send you, follow steps aside and leaves you there for the rest of that turn. It picks back up on the next turn, or when you toggle follow off and on.
Follow still does its job on the golden path: start an agent, watch the editor track its work. Turn the follow toggle off in the conversation header to opt out entirely.
Minor Changes
Comment on mermaid diagrams, math, images, wiki links, and tags.
Commenting on ordinary prose failed with "The quoted passage is not in the document" whenever the paragraph merely contained a highlight, an image, a wiki link, a tag, inline math, a footnote marker, an underline, or an autolink. In a wiki-style document that is most paragraphs, not an edge case, so a comment on a normal sentence often just produced an error and was lost. Each of those constructs spends characters on markup that renders as less than it spells, and the anchor matcher only knew how to look past some of them. It now looks past all of them, including an aliased wiki link's hidden target and a link's heading fragment.
OpenKnowledge now speaks your language. On first launch it follows the language your operating system is set to, and Settings → Appearance → Language lets you choose a different one at any time. The switch is immediate — the interface, the native File/Edit/View menus, and the app's dialogs all change while you keep working, with nothing to restart and nothing to reload.
Eleven catalogs ship: English, Simplified and Traditional Chinese, Spanish, Hindi, French, Arabic, Bengali, Brazilian Portuguese, Indonesian, and Urdu. Three are offered in the picker today — English, Español and 简体中文 — because a language appears there once someone who reads it has reviewed it. The rest are complete and reachable by name, so a translator can run the app in the language they are checking; Arabic and Urdu stay reachable but unlisted until right-to-left layout lands.
Detection is script-aware rather than a language-code match, so a Traditional Chinese system in Taipei gets Traditional rather than Simplified, and
es-MXgets Spanish rather than falling back to English. Choosing System keeps following your OS from then on, including if you change it later — the choice is stored as "system" rather than as whichever language that happened to mean the day you picked it.Your own writing is never touched. Document text, titles, file and folder names, frontmatter, tags and link targets stay exactly as you wrote them, in whatever language you wrote them in; only the application's own chrome changes.
For anyone adding UI copy: strings are extracted and translated in the same change, and a lint rule now fails the build on a new hardcoded user-facing string, so a missing translation is caught before it ships rather than discovered in the interface.
A markdown document can now be opened as a slide deck in its own OpenKnowledge window, via a new Slidev plugin (beta). Rendering is handled by Slidev, which OpenKnowledge neither renders itself nor downloads or bundles — so install it first:
npm install -g @slidev/cli @slidev/theme-default(global), or add those packages to the project (a project-local install takes precedence). Slidev themes ship as separate packages from the CLI, and a deck cannot open without the theme it declares —@slidev/theme-defaultcovers decks that use the default theme or declare none.Then turn on the Slidev plugin in Settings → Plugins, add
slides: trueto a document's frontmatter, and an Open in Slidev action appears in the editor toolbar; activating it opens the deck in a dedicated window you can present from. Editing the document in OpenKnowledge updates the open deck, because Slidev watches the file OpenKnowledge is already saving to.The plugin's own settings page reports whether OpenKnowledge found Slidev, and offers the install command to copy or to run in a terminal when it did not. When no
slidevresolves, the toolbar action is simply absent rather than failing on click. Closing a deck window stops that deck's process, and quitting the app stops all of them. The plugin is off by default, desktop only, and adds nothing to the app bundle for anyone who does not enable it.Docs: Plugins → Slidev.
Patch Changes
In-app agent (ACP) reliability overhaul from the July 31 bug bash. Startup failures now tell the truth: "sign in first" only appears for real authentication failures (classified by the protocol's auth-required code), other errors show the agent's own message with wire-level details behind a Show details disclosure, and failed threads keep their transcript instead of erasing it when the tab closes. Sign-in works in place — auth failures render a button per advertised sign-in method, and completing one reopens the session on the same agent process with your drafted message intact. Agents and their terminals now inherit your login shell's PATH on every launch (not just when the top-level command is missing), so tools installed via nvm and friends resolve; a Retry button on failure notices re-probes the PATH and respawns. Queued messages survive Stop (they fold back into the composer), editing a queued message holds it until you resubmit, the queue says when it delivers, and a new Steer now button interrupts the current run and sends your correction as the next turn — falling back to the front of the queue if the agent won't stop.
In-app agent (ACP) polish from the July 31 bug bash — eleven quick wins.
- Code blocks in agent replies render correctly again. A stale Tailwind source path (left over from the package-manager migration) silently dropped every style that ships inside the markdown renderer, leaving a large dead band between a code block's language label and its content, with the copy button floating in the gap. The copy button now sits on the language row and blocks size to their content, capped at a scrollable max height.
- Syntax highlighting in agent replies. Code output was uniform monochrome; known languages now get theme-aware highlighting in light and dark (via the renderer's shiki plugin, code-split so it stays out of the main bundle).
- Thinking text is collapsed by default. An agent's reasoning stream now folds to a one-line preview — the latest line while it streams, the first line once settled — with a click to expand. No more walls of internal monologue burying the reply.
- "Default" now says what it does. Claude's model selector reads "Opus (1M context) · default" instead of "Default (recommended)" — resolved from the agent's own option data, so it tracks whatever the recommendation becomes. Bare defaults with no wire data (Claude's reasoning effort, Codex's collaboration mode) get a short built-in hint, and unknown raw ids render as readable labels ("bypassPermissions" → "Bypass Permissions"). Adapter-supplied names and descriptions always win when present.
- Agent picker: predictable order and consistent names. Agents you've picked recently sort first (alphabetical after that), and registry display names are normalized at ingest — a manifest that ships an all-lowercase name ("cursor") is title-cased, while branded capitalization (GitHub Copilot, VT Code, goose) passes through untouched.
- Installing/starting states are visibly alive. The agent mark now breathes alongside the shimmering status line (it was static), and the resume-row spinner no longer risks inheriting an invisible stroke. Both respect reduced-motion.
- One word for agent chats: "chat". The dock mixed four nouns for the same thing — "Restore sessions", "Delete this conversation?", "New thread with…", "New … chat". Agent surfaces now say chat everywhere: the history button is "Reopen a past chat", the delete confirm "Delete this chat?", the resume fallback "New chat with…". Terminal tabs keep "session" — they aren't chats. The delete icon also no longer sticks open on the history menu's first row.
- Clearer agent-visibility copy. Settings → Configure agents now names the menus an enabled agent appears in instead of "Turn one off to hide it everywhere."
- The "Archived" badge is gone. A reopened past conversation no longer shows an ambiguous "Archived" chip — the chat was never read-only, and the composer's "Pick up where you left off" placeholder already says what matters: send a message and the conversation resumes.
In-app agent (ACP) threads: three improvements.
- Tool-call rows say what the agent did, not which tool it called. A row used to show the adapter's raw tool identifier, so an OpenKnowledge write read as
mcp__open-knowledge__writeand never named the document. OpenKnowledge's own tools now read as plain sentences — "OpenKnowledge wrote to meetings/standup", "OpenKnowledge searched for bordeaux blend", "OpenKnowledge edited articles/malbec" — recognized across the different names each harness puts on the wire (Claude, Codex, and Pi each spell the same tool differently; Cursor sends no tool name at all, so those rows are unchanged). Every other tool keeps the title its agent wrote and now gets an icon matched to what it does: moving, switching mode, and fetching each had a wrong or missing glyph before. - Deleting a conversation that is open in a tab is blocked. Reopening an archived conversation from the history menu doesn't unarchive it, so the same conversation could be listed in history and displayed in a tab at once — and deleting it there pulled the transcript out from under the view showing it. Delete is now disabled on those rows, with a tooltip saying to close the tab first. Reopening still works, and conversations with no open tab delete as before.
- The presence bar no longer blinks on every agent turn. An in-app thread published a presence entry when it became ready, when a turn started, and again when it ended; each expires after five seconds, so a chip flickered in and out around every prompt. Agents connected over OpenKnowledge's tools already publish continuous presence, so those entries were redundant. Presence for a thread now appears only when the agent actually writes a document through the thread, and that entry finally carries the agent's own brand icon instead of a generic one.
- Tool-call rows say what the agent did, not which tool it called. A row used to show the adapter's raw tool identifier, so an OpenKnowledge write read as
Open the command palette with
Cmd+P/Ctrl+P, in addition toCmd+K.Cmd+Kis dual-role — with text selected in the visual editor it adds a link instead of opening the palette — which meant that in exactly that state there was no keyboard route to the palette at all.Cmd+Pis unconditional: it opens the palette from anywhere, including mid-selection, so the palette is always one chord away.It is also the chord most editors use to find a file, so it should do roughly what you expect if you arrive from one. On the web this now opens the palette rather than the browser's print dialog; printing is still available from the browser's own menu.
Gate the F0 shell-snap e2e on a contention-invariant coupling ratio instead of a fixed millisecond budget. The old
shellMs < 500assertion encoded "finishes within 500ms on whatever machine runs it" as a stand-in for "the shell is decoupled from editor mount cost" — those coincide only while runner speed is constant, and the constant had eroded from 1.9x local-healthy to ~0.99x CI-healthy, so it reddened on correct code.Fenced code blocks tagged
gherkin(or thefeature/cucumber/bddshorthand) now render with syntax highlighting —Feature,Scenario,Given/When/Then/And/But,Examples, and tags all get their keyword paint, and indentation, data tables, and doc strings pass through as authored..featurefiles opened from the sidebar pick up the same grammar.A file you edit now stays open when you click the next one in the sidebar. Single-clicking a file opens it in a preview tab (italic label) that the next click reuses — but editing it left the tab provisional, so a document you had just typed into disappeared as soon as you opened something else. The edit itself was never lost, since it lands in the CRDT the moment you make it, but the tab vanishing read as lost work.
Editing a document now makes its tab permanent: typing in the visual editor or in source mode, cutting or pasting in source mode, applying a lint fix from the Problems panel or either editor, and editing frontmatter through the property panel — including nested fields, renames, and drag-reordering. Switching a document between source and visual mode promotes it too, as does double-clicking its row in the sidebar, matching double-click on the tab itself. Agent and remote-peer writes deliberately do not: a tab you are only reading stays provisional even while something else writes to the document.
Preview tabs are otherwise unchanged — clicking a file you never touch still gives up its slot to the next one.
The frontmatter schema editor no longer re-presents a typed field as an enum when you give it allowed values. Typing into a
stringfield's allowed-values box added the values correctly but flipped the field's Type select toenum, which read as a silent type change — and switching it back tostringthen deleted the values you had just entered. Allowed values are a constraint, not a type: a field keeps showing its declared type, and theenumoption now shows only for a field the schema leaves untyped (a bare{"enum": [...]}), or while you are explicitly picking it. Pickingenumno longer writes astringtype in, and moving a field tostringkeeps its allowed values instead of clearing them. Any other target type still clears them, because it could never satisfy them — a string vocabulary left on anumberfield makes that field reject every possible value. The same rules apply to an array field's element type.Shutting the server down now waits for in-flight history writes to finish before releasing the history repo. Content-loss checkpoints are written in the background and are never awaited by the editing path, so a burst of them could still be running its git work when shutdown released the repo out from under it. Those writes then landed after the server considered itself stopped, re-creating history state moments after teardown — and anything removing the project directory right after a shutdown could fail partway through. The wait is bounded by the existing shutdown timeout, so a stuck git process still cannot hang exit.
The sidebar's file list now fills the space between the toolbar and the project switcher instead of stopping short and leaving a blank strip above the footer. That strip existed to catch the click that clears the creation target — clicking empty space so the next New file lands at the project root rather than inside the folder you have selected. The gesture is unchanged and now works where it always used to: the empty area below the last file, inside the list itself.
The skills OK ships now point at each other using the
/skill-nameform, so a reference from one skill to another is a real link instead of plain text. A pack's orientation skill links to each of its member skills and to the platform/open-knowledgeskill, and the member skills link back. Before this, every cross-skill mention was authored as a bare name, which drew no edge in the knowledge graph, got no decoration in the editor, and could not be repointed automatically when a skill was renamed. That is what left prose in the starter packs pointing at names nothing answered to after the July rename. A test derived from the shipped skills on disk now holds the convention, so a new pack or member skill is covered the day it lands.
Patch Changes
- Workspace search now finds content written in non-Latin scripts. Previously the search index kept only Latin-script words, so document bodies in Hebrew, Cyrillic, Greek, Arabic, Thai, Chinese, Japanese, Korean, and other scripts indexed to zero searchable words — the omnibar and the MCP
searchtool couldn't find them at all, and in a mixed-script query only the Latin words did any work. Search now additionally segments those scripts with the runtime's built-in word segmenter (dictionary-based for unspaced scripts like Thai, Japanese, and Chinese), with no new dependencies and no change to how Latin-only content is matched or ranked. In scripts where a single character carries a whole syllable — Chinese, Japanese, Korean, Thai, Khmer — queries are matched exactly rather than with the one-character typo tolerance applied elsewhere, since there a one-character difference is a different word and near-misses would crowd out the exact match. Thanks to @Guohao1020 for the report, root-cause analysis, and proposed approach, and to @AsafDafna for the implementation and validation (#903).
Patch Changes
- Follow-the-file no longer opens a blank "create-on-open" tab for a document the agent never actually worked on. A read/search/exec tool call whose newest file location resolves to a doc that does not exist (for example a git branch name or a path echoed from the prompt, like the phantom
maintab) is now ignored — matching the existing gate onexec/shell command reads. Edits and other write-shaped targets stay ungated because they legitimately name docs that are about to be created.
Patch Changes
Desktop now reports why a project's server failed to start, instead of only that it timed out.
When the managed server exited before binding a port, Desktop waited the full 15 seconds and then showed
OpenKnowledge server did not bind a port within 15000ms— with no exit code, no signal, and often an empty capture log. A crash 200 ms in was indistinguishable from a slow start, and the actual cause was unrecoverable after the fact.The spawn now observes the child's exit, so failures report their real reason (
OpenKnowledge server exited before binding a port (pid=123, killed by SIGKILL)), and the wait ends as soon as the child is gone rather than running out the deadline.A child that is alive but still starting keeps the full deadline, and its message now says the process was still running, so a slow start no longer reads the same as a crash. The wait also still runs to the deadline when our child lost a startup race to another process that is mid-bind, so a contended launch keeps attaching to the winner instead of reporting an error.
Patch Changes
- Skill relocation now runs a single sibling symlink re-point spine. The promote path's placement-ledger sweep was a hand-maintained second copy that claimed a narrower set of links and wrote link targets by a different rule; it now routes through the same helper as the host-dir sweep. A recorded placement that pointed at the old source is re-pointed directly at the new one instead of being left chained through it.
No notes for this release.
View on GitHubPatch Changes
- Clicking a folder's pinned header in the Files sidebar now collapses the folder. When you scroll down inside a large expanded folder, its header row pins to the top of the sidebar. Clicking that pinned row did nothing visible: the sidebar treated it as a click on an unselected folder and re-opened the folder instead of collapsing it, while the same row clicked at its natural position in the list collapsed as expected. The pinned row now collapses the folder and scrolls back to it, so the row stays under your cursor.
Patch Changes
- The new-tab empty state is vertically centered again. Since the split editor workspace landed, the "Create something great." block was pinned to the top of the pane instead of sitting in the middle, because the pane wrapper it sizes itself against was no longer a flex column. This also restores the header pose when a session panel is open: centered beside the agents panel, bottom-anchored above the terminal dock. A folder overview shown in an unfocused split pane is repaired by the same change: its scroll region now fills the pane and scrolls internally instead of growing past it.
- WYSIWYG lint squiggles no longer vanish permanently when an agent write replaces a document with an unchanged body (for example a frontmatter-only fix). The replace could reach the editor as new nodes carrying identical content; the decoration pass compared content, read it as "nothing changed", and never repainted the marks the replace had destroyed. The pass now reschedules on any transaction that carried real steps.
Patch Changes
- Agents that run through
npxoruvxnow start on machines where Node or Python tooling is managed by nvm or fnm, instead of offering to download a copy of Node the user already has. A desktop app opened from the Dock or Finder inherits a minimalPATH, and a version manager like nvm has no fixed directory to add to it — the active Node lives under a versioned path that only a shell function puts onPATH. When a command can't be found, OK now asks your login shell what itsPATHis and tries once more before offering to download a managed runtime. The same fallback also rescues custom agents installed in shell-configured locations. The probe runs at most once per session, only after a launch has already failed, and never overrides aPATHthat an agent's own configuration set. - Broken images keep their "Image failed to load" pill, without side effects on copy or on anything else that reads the document. The failed
<img>now stays mounted (hidden) behind the pill, so copying a selection with a broken relative-path image emits the markdown source-fallback block again instead of silently dropping the image, the pill's own text no longer pastes into other apps as if it were document content, and a broken-but-portable web image pastes as a normal<img>whose URL may still resolve at the destination. Error detection now trusts the image's error event alone, so a successfully loaded dimensionless resource (an SVG sized only by CSS) is no longer misreported as broken, and screen readers always hear the failure notice with the image's alt text or source. - Blank lines at the very top or bottom of a document now stay in sync between the visual editor and markdown source in both directions. Previously they only propagated when added in the visual editor: blank lines typed in source mode (or arriving from disk or an agent) never appeared in any collaborator's visual editor, deleting them in source mode left phantom blank paragraphs behind, and deleting them in the visual editor never reached the file. On frontmatter-bearing documents, blank lines added above the first paragraph now survive too, with the separator line below the frontmatter kept intact. This also fixes a lock-up where a document whose two representations disagreed about an edge blank line would silently stop showing source-mode edits in the visual editor and revert visual-editor deletions.
- The Mermaid and HTML code-preview edit modals now show the live render on the left and the source editor on the right, matching how the reader mentally locates their edit — outcome first, mechanism next. On stacked (mobile-width) viewports the source editor stays on top so the surface the user came to edit is what they land on, and the body scrolls if the two panes exceed the dialog height.
- Internal test diagnostics: when the check that an
ok mcpserver exits after its launching host dies fails on CI, it now reports the server's parent pid and captured stderr instead of a bare assertion. The parent pid is what separates the two ways that check can fail — the process never being reparented at all, versus being reparented and then not finishing shutdown — which previously could not be told apart from the failure output. No change to how OK runs.
Patch Changes
- Relaunching with several projects open no longer drags you back to OpenKnowledge over and over. Restoring a multi-window session used to reveal each window with a focusing show, and on macOS that pulls the whole app forward, so a restore of seven projects (which materializes over several seconds while each project's server starts) would background whatever you had switched to once per window, then take focus one final time at the end. Now the app comes forward once, as it always did, and from then on stays out of your way: if you switch to something else while the rest of the session is still loading, the remaining windows open quietly behind you and the app does not pull you back. The window you were last working in is still the one waiting for you when you return.
Patch Changes
Ctrl+` now toggles the bottom dock, matching the chord VS Code and Zed both use for the terminal. It is literal Control on every platform (not Cmd on macOS, which the OS reserves for window cycling), it works from inside a focused terminal so you can dismiss the dock without reaching for the mouse, and it appears alongside ⌘J in Settings → Hotkeys. It shares ⌘J's handler, so with text selected in the editor it stages that passage into the terminal rather than toggling.
⌘J is unchanged, but its command is renamed from "Show/Hide Terminal" to "Show/Hide Bottom Dock" in the View menu and the ⌘K palette — it toggles the dock, and the dock is where the terminal lives. Searching the palette for "terminal", "show terminal", or "hide terminal" still finds it.
Patch Changes
- A project-wide Fix all now keeps running when you switch away from the Problems tab. Previously the sweep lived inside the Problems panel and ended the moment that panel stopped being rendered, so clicking over to Timeline or Comments mid-sweep left the project partially fixed with nothing to say that anything had stopped, or why. The sweep now belongs to the operation rather than to the panel: it runs to completion in the background, any panel you open mid-sweep picks up its progress, and it reports how it ended — finished, stopped, or failed — wherever you happen to be looking. Completion is now announced too, so a long sweep you walked away from tells you when it is done.
Minor Changes
Comment on code blocks and table rows.
Dragging a selection across a code block and asking for a comment failed with "The quoted passage is not in the document." A fenced block's backticks were already invisible to the anchor matcher, but the language tag after them was not, so any selection crossing into or out of a
```tsblock could not be located. The same gap closed for thematic breaks, setext heading underlines, table delimiter rows, table cell boundaries, and task-list checkboxes — every source line that renders as nothing.A code block's sparkle button used to hand the whole block straight to a fresh agent session, with no way to file the note for a later batch. It now opens the same composer the text toolbar's Ask AI opens, offering both. A selection already inside the block is what the comment is about; with nothing selected, the block itself is.
Table rows and columns can now be selected by clicking the handle above a column or beside a row, so a row can be commented on, copied, or deleted as a unit. Previously the handles only opened a menu and the sole way to select cells was sweeping the pointer across them.
Commenting on a selected row quoted a single cell. The composer read the selection's first range, which for a table selection is the anchor cell alone, so picking a three-column row and commenting on it filed the comment against one word of it.
The 15 starter-pack skills drop their generated
open-knowledge-pack-…names and take marketplace short names, taken from SKILL.md frontmatter: the orientation skills becomenote-taking(was…-plain-notes),writing-workflow(was…-writing-pipeline),personal-crm(was…-entity-vault),okf-knowledge-base(was…-okf), andworldbuilding,codebase-wiki,knowledge-base,software-lifecycle(prefix dropped); the member skills becomeframe-a-proposal,record-a-decision,write-a-spec,review-a-design,write-a-postmortem(prefix dropped) plusresearch-with-sources(was…-knowledge-base-research) andconsolidate-notes(was…-knowledge-base-consolidate). Every shipped skill now carriesmetadata.author/metadata.repository.Skills you already have are left exactly as they are. Nothing is renamed on your disk, your
.ok/skills-lock.jsonis not rewritten, and no boot pass touches your project. These are project-level skills that normally live in your git repo, and silently renaming one you are already using would show up as an unexplained diff for you and for everyone who pulls. An existing install keeps its current name, keeps working, and still updates from source: "Update" resolves the old name to the renamed bundle in the mirror. Re-seeding a pack recognizes the skill you already have and will not author a second copy of it under the new name. Only newly installed skills use the new names.Note for maintainers: this is the reason there is no migration. If you later want existing installs on the new names, it needs to be something a user opts into and can see, not a rename that happens under them at boot.
Four Claude Code marketplace plugins are renamed to match their published skill names:
entity-vaultbecomespersonal-crm,okfbecomesokf-knowledge-base,plain-notesbecomesnote-taking, andwriting-pipelinebecomeswriting-workflow. The old/plugin install <name>@open-knowledge-skillscommands keep working for one more release: the four old handles remain in the marketplace as aliases, marked outdated and installing the same skills as the entry that replaced them. Switch to the new names — the aliases are removed in a later release. The other seven plugins keep their names, but every plugin's skill paths move as the published repo regroups intoskills/core/andskills/starter-packs/.ok seed --pack <id>flags are unchanged.Switching a project's OpenKnowledge skill off in Settings now sticks. It previously came back on the next open, because the project-open sweep recreates the skill for any editor already wired for OpenKnowledge and nothing recorded that you had turned it off. Your choice is now remembered per project, on your machine only, so it does not follow the repo to teammates. A project where you never expressed a choice still gets the skill seeded as before.
Installs of OpenKnowledge's own skills are now counted on skills.sh wherever they actually happen. Previously only some routes reported, so most installs were invisible: creating a project, the desktop seeding its built-in skills on first launch, opening a wired project that had no project skill, switching the skill on in Settings, installing for Claude Desktop, and importing our own repo by any route other than the Explore tab all counted nothing. What is NOT counted: an app launch that installs nothing, reopening a project, a failed write, a skill bundle you switched off, and — unchanged — any third-party repository you typed yourself. Reporting still honours
telemetry.skillInstallReports.enabled,DO_NOT_TRACKandDISABLE_TELEMETRY.A skill installed into a project is now counted once per project rather than once per machine, because each project gets its own copy in its own editor directories. The project path is used only as a local key to avoid double counting and is never sent.
Also: seeding now reports name collisions with your own skills instead of silently skipping, and rejected install reports are logged. skills.sh install counters for the old names reset under the new names.
Install reporting to skills.sh now retries after a rejection. Previously a report the collector declined was recorded as sent and never retried, so any install rejected while a newly renamed listing was still being indexed was lost for good. Only rejections the collector application issued (400, 404, 410, 422) are retried: a 5xx, a 429, or an edge 403 may have been raised after the event was already counted, so those keep their claim, as do reports the collector never received. A flaky network or a struggling collector can never inflate a count.
Register
<Toggle>as a canonical block descriptor — Notion-style vocabulary alias for<Accordion>. Same props (title,defaultOpen,icon,description,id,name, children), same expand/collapse component; the two descriptors serialize under their own JSX names so<Toggle>and<Accordion>both round-trip without rewriting./togglein the slash menu now inserts a first-class<Toggle>instead of routing to<Accordion>.Add a resizable vertical split workspace with one shared header for tabs and workspace actions. Drag tabs or sidebar files into pane edges, or use tab actions, to create and populate panes. Pane sizes, open tabs, and focus restore with the project session, while editor actions and the right document and agent panels follow the focused pane below the header.
Polish tabs across single and split panes with stable sizing, horizontal overflow scrolling, edge fades, clearer drag feedback, and shared actions for documents, folders, and assets. Existing preview tabs keep their replaceable and pinned behavior while supporting the same drag, split, and overflow interactions as regular tabs.
Patch Changes
Queued review comments can now be sent into the agent thread you already have open. The agent panel's composer carries the same
+ Commentschip as the Ask AI composer: click it to attach everything waiting in the queue, expand it to review or uncheck individual comments, and send.You can now pick one color theme for light mode and a different one for dark mode. Every tile in Settings → Plugins → Themes carries a sun and a moon: press the sun to make that palette your light theme, the moon to make it your dark theme, or both to use it either way. Whichever mode you're in — including when your theme setting is System and your OS decides — the matching palette applies, and it swaps as the appearance changes without touching your config.
Any palette can sit in either slot; it still forces its own light/dark variant, so choosing a dark scheme as your light-mode theme does what it says. Existing setups are unchanged: a single palette chosen before this release applies to both modes until you pick a new one.
Switching themes is also faster than it was. Repainting the terminal palette used to re-measure each color separately against styles the switch had just invalidated, which stalled the window for a moment on every change; the whole palette is now measured in one pass and refreshed once per frame.
A crash report that arrives without its crash dump now explains itself. Until now, a bug report whose crash dump was missing looked identical whether you had unchecked the box on purpose, the app had found no crash dump it could prove was its own, or the dump had been cleaned up in the moment between choosing to send it and the report being packed. All three arrived as an empty slot, and there was no way to tell them apart afterwards, so a report that should have carried the most useful evidence about a crash could not even be shown to be missing it. The app now writes a line into its own log recording which of those actually happened, and, when a dump was looked for, how many crash dumps were skipped because they belonged to a helper process or could not be read at all. The line is written before the report is packed, so it travels inside the very report it explains rather than turning up in the next one.
A dump that was on hand is the one case that line cannot settle by itself, since whether it survived being packed is only known once packing is done. The report answers that on its own: the line says a dump was going in, and the report either carries it or it does not. Relatedly, a dump cleaned up mid-pack used to abort the whole detailed report; it is now recorded as a dump that did not make it, and the rest of the report is still produced.
This changes nothing about what gets attached. The crash-dump checkbox behaves exactly as before: it appears only when a crash dump for that crash is on disk, comes pre-checked because that dump is the evidence the report exists to carry, and can be unchecked. This only observes the choice already being made. The record names no file: a crash dump is raw program memory that cannot be scrubbed, and its filename identifies the individual crash, so the line carries only the outcome, the counts, and a size in bytes.
A bug report filed after a crash now names the version that actually crashed. The report is composed by the session that notices the crash on the next launch, and if an automatic update landed in between, that is a different build from the one that died. Reports were being attributed to the new build, which in some cases had only been running for a fraction of a second before it was asked to explain a crash it never saw. The crashed version is now read from the crash dump's own metadata, or from the marker the previous session left behind, and it appears in the report alongside the version you are running now. Seeing the two together is the point: when they differ, an update happened between the crash and the report, and the crash belongs to the older build. When a report cannot establish which version crashed, it says nothing rather than guessing, so a stated version can always be trusted.
Agents can now reach a project opened from a development build of the desktop app. The dev launch path bound its server to
localhost, which macOS resolves IPv6-first — so the server listened on[::1]while the MCP server, the agent keepalive, andok psall dial numeric IPv4 loopback. Nothing was listening at the address they called, so tool calls against that project failed and the agent's connection retried forever, even though the editor window itself worked normally. Development launches now bind the same numeric IPv4 loopback address thatok startalready uses, so a signed release and any number of local development builds can run side by side with each project reachable by its own agent.Hovering an editor tab now shows the file's full path immediately in an app-styled tooltip, so two tabs with the same file name —
SPEC.mdfrom two different folders, say — are no longer indistinguishable. Asset and skill-bundle-file tabs now disclose their paths too, and the tooltip stays out of the way while you drag a tab to reorder it.A graphics glitch that fixed itself no longer asks you to file a bug report. Chromium draws each window from a separate process and replaces that process on its own when it dies, restoring the picture in about a second — usually before there is anything to notice. OpenKnowledge was treating every one of those deaths as a crash worth interrupting you over, so the report dialog could arrive moments after nothing visibly happened, leaving you to describe a failure you never saw. One such report was filed for a session that had recovered a full second before the invitation appeared and then ran normally for the rest of its life.
Those deaths are now recorded quietly instead. If the graphics process keeps dying — three times inside five minutes, the point where it is no longer recovering and the window really does degrade — the invitation appears exactly as before. Either way every death is still written to the log, so a report filed for something else still carries the whole picture, and a recovered one says in as many words that it was suppressed and why.
The project server now exposes standard health-check endpoints:
GET /healthzanswers 200 as soon as the listener is up (process liveness), andGET /readyzreports 503 while the project runtime is still initializing, then 200 with adegradedlist naming any subsystems that failed to start. The moment shutdown begins,/readyzflips to 503 with statusdrainingso probe-driven routers stop sending traffic before the listener closes. Both endpoints are exempt from the Host/Origin admission checks so container orchestrators and reverse-proxy health probes (which send IP Host headers and traverse proxies) work without configuration.Agents in the in-app agent panel no longer answer with a
localhostpreview link to the app you are already looking at. After creating or editing a doc, an agent would sometimes end its reply with a barehttp://localhost:<port>/#/...URL instead of just bringing the doc up on screen. The steer that prevents this already existed, but it only recognized agents running in the desktop app's built-in terminal, so the agent panel never received it and fell back to handing over a raw URL. Every way a panel agent reaches OpenKnowledge now carries the same signal, and the guidance is explicit that the URL should not be pasted into a reply when you are already in the app. Agents outside the app are unaffected and still get a URL they can open.Images with a missing
src(404, broken URL, deleted asset) now render a visible placeholder card — icon + "Image failed to load" + the truncated path — instead of leaving the reader with the browser's default 16×16 broken-image glyph. Same behavior on cached-broken images that fail before onError fires (detected viaimg.complete && naturalWidth === 0). Placeholder stays inline (<span>) so it's safe inside<p>and inside<Zoom wrapElement="span">.Typing a markdown shortcut around an image, wiki link, tag, inline math, footnote reference, or inline JSX no longer breaks the editor. Closing a shortcut whose text spanned one of these — typing the last
*of**see  here**, for example — computed a replacement range that was five positions too wide per object, so the keystroke either threw and was lost, or, in a longer paragraph, silently rewrote the wrong span and ate the text in front of it. Inline JSX was skewed by an amount that grew with its own length, so a short one could work and a realistic one could not. Every inline object now reports its width to the shortcut matcher as the single position it actually occupies, which fixes the range for every shortcut rather than any one of them.Managed Node.js and uv runtimes now install correctly when the temporary directory and OpenKnowledge home are on different filesystems. Runtime archives are staged beside their destination for an atomic same-filesystem rename, and concurrent launches adopt an already completed install instead of removing it.
ok init --no-skillsnow only skips installing the built-in skills for that run. It no longer turns them off across your whole machine.Previously the flag recorded a machine-wide opt-out and deleted the built-in skills from your user-global skill folders, so running it once in a throwaway directory disabled them for every project. The recorded opt-out also persisted, so the desktop and
ok repair-skillssweeps removed the skills again on every launch — reinstalling them by hand appeared to work until the next launch deleted them.The flag now installs nothing and records nothing. Skills already on your machine are left alone. If you want to turn the built-in skills off for real, use the Settings toggle or the first-launch prompt, which is where that choice was always meant to live. If you have already been affected, re-enable them from Settings or run
ok initwithout the flag.Side panels no longer get stuck in a non-resizable state. Two separate faults could wedge them, both triggered by a drag that the browser ended with a cancel rather than a release — which happens when a scroll or zoom gesture takes over, or the system invalidates the pointer mid-drag.
The document panel and agents column tracked whether a drag was in flight but only ever cleared that flag on pointer release. After a cancelled drag the flag stayed set, and while it was set the layout correction that opens, closes and re-pins the right rail refused to run — so the document-panel toggle, ⌥⌘B, the avatar-click expand and the sticky panel widths all silently stopped working. The bottom terminal dock had the same gap, where a stuck flag instead made every later resize look like a user drag: the dock could hide itself unprompted and overwrite its saved height. Both handles now end a drag on cancel as well as release, restore the panel widths when a gesture is aborted rather than committing a drag-to-close the user never finished, and detach cleanly if the panel unmounts mid-drag.
Separately, a fault in the resizable-panels library could wedge things harder: after an error while a panel was mounting, every panel in the window could stop responding to drags until a full reload. A bundled patch now lets the library recover from that on its own, without the reload.
Fixed the problem count on the document panel's Problems tab painting over the Comments tab next to it. The count hung off the outside of the tab's top-right corner, which was fine while Problems was the last tab in the row, but the Comments tab now sits to its right. The count is drawn inside the tab it belongs to.
A window whose renderer stops responding now recovers itself instead of going blank. Chromium occasionally tears down the process that draws a window; the frame stays and nothing paints in it. OpenKnowledge noticed this and filed it for a crash report, but did nothing about the window, so it stayed empty until you happened to try Cmd-R. There was no message and no visible way back — one report described coming back from a short break to an empty window and reloading on a guess, unsure whether that would take the running agent and a session of unsaved comments with it.
The window is now reloaded automatically the moment its renderer dies. If it dies a second time within a minute, the reload stops rather than looping, and a dialog offers to reload it or leave it alone. A window that dies repeatedly but slowly enough to keep dodging that minute is caught as well, after a few recoveries. That dialog also says what the blank window could not: documents and running agents live in the OpenKnowledge server, not in the window, so reloading restores the view without interrupting them.
Adding a frontmatter property now knows what the document's schema declares.
The Add properties button already counted the schema-required properties a document was missing, but clicking it opened one blank row — you still had to go read the schema and retype every name it had already stated. It now stages a pre-named row for each missing property, with the widget type taken from the schema and the cursor in the first value. Nothing is written until you fill a row in and add it, so a half-finished batch never leaves empty properties behind (and never clears the "required" warning with a blank value).
The name field on any add-row also offers the fields the governing schemas declare — filtered as you type, showing each field's type and description, and marking the required ones. Picking one fills in the name and its type together. It stays a free-text field: schemas don't own the whole vocabulary, and a document governed by no schema is unchanged. Enum-constrained fields now offer their vocabulary while being added, not only after.
Add a Delete action, with a confirmation dialog, to skill bundle files. It shows up wherever a skill file's actions do: the Skills sidebar row and the file's editor tab. Deleting closes the file's open tab and reports a miss instead of a silent success when the file is already gone.
Editable
.mdreference tabs previously carried no skill-file actions at all; they now offer the same Rename, Reveal in Finder, Copy Path and Delete set as the sidebar row for that file.Also fixes a pre-existing hazard in
DELETE /api/skill-file: the live-doc teardown for a project.mdreference ran before the file's existence was known. Because bundle doc names are extension-less, deleting a path that was not on disk tore down the live doc of a same-stem sibling (references/x.mdvsreferences/x.mdx) that survived the no-op unlink.Typing in a large knowledge base no longer stutters while the template list is being gathered. Building the list of templates you can create from means walking the project's folders, and on a big repository that walk is long enough to hit its internal limit. It ran start to finish without pausing, on the same loop that carries your keystrokes between the editor and the server, so a burst of requests left the editor unable to keep up with typing for a noticeable stretch. The walk now pauses between folders (the longest uninterrupted pause it causes drops from roughly 84 ms to about 1 ms) and reads each folder with a single system call instead of one per entry. Symlinked folders are still followed.
The desktop app now ships for Windows and Linux, with auto-update on every platform. Each release publishes signed Windows installers (x64 and ARM64) and Linux
.deb/.rpmpackages (x64 and ARM64) alongside the macOS DMG, all attached to the GitHub Release. Installed apps update themselves: Windows updates install silently like macOS, and Linux updates download the new package and ask for your password to install it (via the system's polkit prompt) — no apt/rpm repository to configure.
Patch Changes
- Moving a skill between project and global scope no longer leaves a broken copy behind at the old scope. The move itself was correct, but the skill's editor document stays open in the app, and its autosave could fire a couple of seconds after the move and write
SKILL.mdback to the path the skill had just left. Autosave carries one document and none of the bundle's other files, so it recreated the folder holding onlySKILL.md, withreferences/andscripts/missing. That half-bundle then showed up as a real skill everywhere skills are listed, which was most visible for skills that have those extra folders. Persistence now writes only into a folder that still exists, so a document whose skill was moved or deleted drops the write instead of recreating it. The same rule covers skills you edit in place from another tool's folder, which could be rebuilt the same way after being deleted outside the app. If you already hit this, the leftover folder at the old scope is not cleaned up for you: delete it by hand. It is the one holding onlySKILL.md.
Patch Changes
Frontmatter schema problems no longer underline body text that isn't wrong, and now report next to whatever can actually fix them. When a schema required frontmatter that a document didn't have, the error had no property to attach to, so the red squiggle landed on the document's first line — usually a perfectly correct heading, with no hint that the real problem was a missing frontmatter block.
Required properties the document is missing now show as a warning count on the toolbar's properties button, since adding a property is the fix and there is no row to point at yet. Properties that are present but don't match the schema — a wrong type, a value outside an allowed set — show as a warning count beside the Properties heading, next to the existing property count rather than replacing it. Both carry the specific messages in their tooltip, and the Problems panel continues to carry the full explanation. Clicking a frontmatter row in the Problems panel no longer jumps the cursor to that unrelated first block.
The Problems tab's own count badge picks up the same warning styling, so the three read as one family. Body-anchored rules (hard tabs, heading levels, and the rest of markdownlint) are unchanged and still mark the block they're about.
Patch Changes
Comments say what they are going to do rather than calling themselves a queue. The Comments tab's second scope is To send and its panel is headed Comments to send; a comment that is in the batch reads Ready to send, one that isn't offers Send later, and the composer chip that picks the batch up reads Comments. Commenting on a frontmatter property files it with Add Comment, the same words the passage composer uses. Messages follow the same vocabulary: a send that fails now tells you the comments are still waiting to send.
Crash reports no longer blame the app for a crash in some other program, and a bug report can no longer carry another application's process memory.
On macOS a task's Mach exception ports are inherited across fork and exec, so every descendant of the desktop app runs under the crash handler the app started: the in-app terminal's login shell, anything launched from that shell, MCP servers, agents, and unrelated GUI applications. When one of those aborted, the handler wrote its minidump into the app's own crash database and stamped it with the app's product name and version, and nothing downstream told the two apart.
- A crash dump is now checked against the app bundle before it counts as ours. Detection reads the dump's own module list to find the crashed process's main executable and requires it to resolve inside the app bundle. A dump from a foreign process no longer arms the "the previous session crashed" invitation, which previously fired after a perfectly clean quit whenever an unrelated app had crashed under the inherited handler.
- A foreign dump can never be attached to a bug report. This is the more serious half. The "include the crash dump" checkbox describes a memory snapshot of this app, and the collector copies the dump into the bundle byte for byte, so an unrelated application's raw process memory (plus its loaded-module inventory and library search paths) could be uploaded under a consent the reporter was never asked for. The attachment lookup now only returns dumps proved to belong to this app.
- A dump too damaged to identify still prompts, but is never attached. A dump truncated by the crash that produced it is most likely ours, and the invitation only asks a question the user can dismiss, so the prompt errs toward asking. Egress errs the other way: memory whose owner cannot be established is memory the consent dialog cannot honestly describe, so the checkbox is not offered.
- Ignored dumps leave a log breadcrumb naming how many were skipped, so a suppressed prompt is distinguishable from detection never running.
Fix "Open file" wiping out the app when a file preview fails to load. Opening a file the built-in viewer could not show — a text file over the 1 MB limit, a binary one, or one it declined for any other reason — offered an "Open file" button that pointed straight back at the request that had just failed. Clicking it navigated the whole window there, so the editor disappeared and left an empty window with nothing but the traffic-light buttons and no way back.
"Open file" now hands the actual file to your Mac to open in whatever app it belongs to, the same way it already works elsewhere in the preview pane, and the editor stays put. In the browser it opens in a new tab instead. When the file simply is not there, the button no longer appears at all, since there is nothing to open. The desktop app also refuses outright to navigate the window to an internal address like that, so a stray link cannot blank the app again.
Patch Changes
Files whose name carries a doubled markdown extension now get their own row in the Files sidebar. A
notes.md.mdsitting next to a realnotes.mdresolved to the same sidebar path as its neighbour, and the sidebar kept only the first of the two, so the other file had no row, no badge, and no warning even though it was still on disk and still reachable everywhere else. Each file now appears under its real name, so a stray doubled extension is visible and can be renamed or deleted. Same-name.mdand.mdxpairs are unchanged, since those already resolved to separate rows.With "Show .ok folders" turned on in the sidebar, clicking
.okor one of its subfolders such astemplatesno longer opens an error tab reading "This file could not be found" for a name liketemplates.md. Those rows are folders, but OpenKnowledge was reading the folder path as the name of a missing file, and the bad tab came back on its own every time the file list refreshed.The
.okfolder view still lists no documents of its own, because.okcontents stay out of the file index.Dropping a file into a document now explains why an upload failed instead of showing a bare "Upload failed" toast. When the dropped file's contents can no longer be read — it was moved, deleted, or had not finished downloading between the drop and the upload — the message names the file and says so, rather than implying a server problem. Genuine connectivity failures are now reported as such. The same distinction applies to uploads started from the property panel's file picker.
Both paths also record the file's name, type, size at drop time, size at send time, and the underlying error, so a failed upload leaves enough evidence in a diagnostic bundle to identify which file failed and why. Previously the entire record of a failure was
TypeError: Failed to fetch, which is the same message the browser produces for an unreachable server, a denied read, and a vanished file alike — and because the request never leaves the app in these cases, nothing was recorded on the server either.
Minor Changes
Comments are the queue for your agent. Highlight a passage, say what's wrong, keep reading — the comment is filed against the exact words you picked, and nothing is sent until you decide the pass is done.
Before After Five things to fix meant five trips to a chat window Comment on each, send once Retype the passage so the agent can find it again The comment carries the words it points at Lose your place in the doc every time Never leave it The queue spans files. Comment on three passages here, open another file, comment on two more, then send. One agent turn, edits landing in both documents. Uncheck anything you changed your mind about before it goes.
Send now or send later. Writing a comment offers both: Add Comment files it for the batch, Send to AI hands it over immediately. Either way the comment box takes
@mentions of files, the same as the Ask AI composer, and mentions reach the agent as real paths.Sends find the conversation you're already in. With a chat open, the queue lands in it rather than starting a detached one; with none open, it starts one. The send button says which before you click it.
Comments are stored per-machine next to your content and are not committed — a comment here is a note to your own agent, not a message to a teammate.
A config key that was removed in a newer version no longer discards the rest of the file it appears in.
Config files accumulate keys that later releases retire. Until now, a single retired key anywhere in a file — your committed
.ok/config.yml, your user-global~/.ok/global.yml, or the per-machine.ok/local/config.yml— invalidated the whole file: every unrelated setting in it silently reverted to its default. A long-dead cosmetic setting left behind in a config could therefore switch off something you actively depend on, with nothing reporting a failure. In one case a retired sidebar preference sitting in a project-local config threw away an explicitautoSync.mode: full, so Git sync stopped running and stayed off.Now every config layer strips only the retired keys it finds, keeps every other setting at the value you wrote on disk, and reports each stripped key together with what replaced it. Genuinely broken files are unaffected by this change: invalid YAML and settings that fail validation are still rejected exactly as before. Only the handling of retired keys changed.
ok startno longer fails to start over a retired key in your committed.ok/config.yml. It strips the dead key, prints what it found and the replacement guidance, and starts. Previously one stale key could stop the tool from starting at all.ok config migratecan now reach your per-machine config. It defaults to every layer, so the bareok config migrateeach error message tells you to run now fixes the key wherever it lives. A new--scope project-localtargets.ok/local/config.ymlalone,--scope allis the explicit form of the new default,--scope bothkeeps its old project-plus-user reach, and--dry-runstill previews without writing. Migration only ever removes a retired key and reports its replacement — it never rewrites your other files on your behalf (for example, it will not create or edit.okignorefor a removedcontent.include, because that rewrite is not a faithful one-to-one translation).ok config validatenow reports all three config layers. It previously described only your user-global and committed-project files, so a retired key in the per-machine.ok/local/config.yml— the layer the reported bug lived in — got an unqualified✓. It now reads that layer too, so a stale key is findable without a running server. Reporting is read-only: an unreadable layer is described, never moved aside.- New endpoint
GET /api/config/diagnosticslists the retired keys currently present across your user, committed-project, and project-local configs, each identified by its scope, file, key path, and replacement guidance. It reads the files fresh on every request, so fixing a file is reflected on the next request without a restart, and it never returns your actual config values.
Settings that a retired key was suppressing now take effect. Some features deliberately fall back to a safe value when a config file cannot be trusted, and until now a retired key made a file untrusted. Those features therefore ignored what you had written in the same file. Now that a retired key leaves the file readable, your settings in it apply — which for an affected config is a real change in behavior, in both directions:
- External link previews. A project-local config carrying a retired key had link previews forced off, even when you had explicitly enabled them. Your explicit setting is now honored. If that file has no
linkPreviewssetting at all, the default (previews on) now applies, and hovering an external link sends its URL to the destination site where previously it did not. - Semantic search. A
search.semanticblock sitting alongside a retired key was discarded, leaving semantic search off. Your configured provider and model now apply, and enabling semantic search sends content to that provider. - Editor bridge safeguards. An explicit
bridge.*opt-out alongside a retired key was overridden back on. Your opt-out now takes effect.
If you rely on any of these being off, check the config file the diagnostics endpoint or
ok config validatereports a retired key in. A file that is genuinely unreadable (invalid YAML, or settings that fail validation) still falls back to the safe value exactly as before.Code and plain-text files (
.ts,.json,.css,.py,.yaml,.txt, and friends) now open as real editors instead of read-only previews — line numbers, syntax highlighting, and live collaborative editing, with every keystroke saved straight back to the file on disk. HTML, SVG, and lockfiles open the same way, straight from a click in the file tree. Markdown keeps its rich editor and Mermaid files keep their diagram editor.Editing the globs that scope a frontmatter schema to a set of docs got three fixes, all from the same root: once you pressed Enter, the pattern became something you could neither read accurately nor change.
Patterns read as you typed them. A committed pattern was displayed in capitals —
blog/**rendered asBLOG/**. The pattern on disk was always correct, so nothing was ever scoped wrong by this, but globs are case-sensitive and the display gave you no way to confirm what you had actually saved. The same fix reaches every place these value pills are used, including the schema editor's "Allowed values", where an enum ofDraft/Publishedwas shown asDRAFT/PUBLISHED.Double-click a pattern to edit it. Correcting
blogtoblog/**meant deleting the entry and retyping it from scratch. Double-clicking a pattern now lifts it back into the input with the text selected; the corrected pattern is written back in its original position, so an exclude that only applies to the includes before it stays where you put it. Enter or Space does the same thing from the keyboard, and Escape cancels.Problems point at the glob that caused them. A pattern matching zero docs was reported in a "Configuration problems" list that named the pattern but not which schema's input it came from — with several schemas and several globs each, you could see something was wrong without being able to tell what to fix. The offending pattern is now marked on the input itself, with the reason on hover, and that is its only home. Deleting a flagged pattern no longer flashes a warning about it on the way out: the problems channel is composed from the file on disk and briefly lags the edit, so a pattern you just removed still had a live finding for a moment. Problems about the schema file rather than a glob still appear in the list.
Skills become a first-class surface: find them, bring them in, and own them as versioned content.
Find a skill. Skills is now a destination rather than a settings tab. Its home leads with describing the skill you want, with Upload a skill and New from scratch as quiet actions beneath. The six most-installed skills on skills.sh render inline with a Browse all link, so installing something that already exists no longer starts by opening a modal.
Adding a skill opens one dialog with two tabs:
- Explore browses skills.sh — a most-installed grid on an empty query, keyword search otherwise — and imports a repository-backed or website-backed result in one click. Website publishers install through their
.well-knownskill index, including every declared file. When skills.sh is unreachable the surface degrades to a GitHub-topic search and the last-good grid, so it is never blank on a cold network. - Upload takes a
.zip/.skillbundle, a folder, or a remote source: a GitHubowner/repo, a git URL, a skills.sh skill-page URL, or a local path.
Skills you already have are left alone. Skills OK finds in your other tools (Claude plugins, plus the Claude, Codex, Cursor, OpenCode, Copilot, Pi, and Agents skill dirs) are never imported. They appear as read-only Detected rows, expand to their file tree (loaded lazily, so the sidebar never fetches a preview for every detected skill up front), and are editable in place — changes save straight to that editor's own copy.
One skill, one folder, many places. Every path lands the skill in the project's skill home (
.agents/skills/<name>/when you have one) as versioned content: history, restore, search, and attribution, with provenance in.ok/skills-lock.json. An identical re-import is a no-op; a name collision lands under<name>-importedrather than overwriting. A new location is a symlink to the source by default, or follows the form the skill's other locations already use; convert any single location when you want an independent copy. Imported scripts are stored as content and never executed.Installing a set, not a file at a time. When a preview belongs to a plugin or a website index, it discloses the siblings and opens a picker — each with its description, a select-all, and the level to install at — rather than a one-click "install all 41". The selection imports through a single server-side clone, so ten skills from one repo no longer re-clone it ten times, and one oversized bundle or misspelled name reports itself instead of failing the rest. Website installs also fetch bundle files concurrently and read the origin index once per selection: two skills from
open.feishu.cnwent from 39s to 9s. A plugin's hooks, commands, and MCP servers are named, never installed and never run.The
workflowMCP tool is retired. Its five procedures move into skill guidance, so they load on description match instead of costing tool-list tokens every turn.importtakes the freed slot, keeping the surface at 21 tools.ingestand brownfield onboarding move into the platformopen-knowledgeskill, which ships with everyok init.researchandconsolidateship with theknowledge-basepack;wikigenerate/refresh withcodebase-wiki. The platform skill now says plainly when a procedure needs its pack.
Starter packs ship focused skills. A pack directory may hold a root
SKILL.mdplus one subdirectory per member skill.software-lifecyclegainsframe-a-proposal,write-a-spec,record-a-decision,write-a-postmortem, andreview-a-design;knowledge-basepromotesresearchandconsolidateto member skills. Pack skills also gained task-based triggers, sosoftware-lifecyclefires on "record an architecture decision" rather than only on a folder-layout match.ok seed --pack <id>installs every skill a pack ships, and no longer reports "already seeded, nothing to do" when the folders exist but a skill is missing.Fixes
- Installing a skill into the
.agentshub no longer fails with "Placement path must be a project-relative directory outside .ok/." Asking for a location a skill already occupies is a satisfied request that changes nothing. - Skill preview tabs no longer duplicate. A preview's identity includes its source path, and that path moves (a plugin-cache path carries the version; a detected skill relocates when its installed copy is deleted), which minted a second tab with the same label — one of which looked impossible to close. Reopening reuses the tab already open for that skill and level, and a visible tab is always closable.
- Settings → Skills is slimmed to the install-targets picker; authoring and browsing live in the sidebar.
- Install counts read
2.7Mrather than2733k, in the active locale's units and separators.
Under the hood: a read-only cross-harness enumerator (
ok skills installed,GET /api/skills/installed), the skills.sh discovery and leaderboard proxies with defensive row-level parsing, the acquire/import pipeline, and the shared skill and Pack model — all exported from the new@inkeep/open-knowledge-core/skills-catalogsubpath. Remote imports require explicit opt-in before upstream changes auto-apply; local filesystem sources keep automatic refresh. Rename and scope-move preserve provenance, update eligibility, and copy projections. The canonical-component text in tool descriptions is split sowriteandeditstill list every component id while the authoring reference rideswritealone, trimming roughly 940 bytes of always-on context per turn.- Explore browses skills.sh — a most-installed grid on an empty query, keyword search otherwise — and imports a repository-backed or website-backed result in one click. Website publishers install through their
The terminal and agent conversations are now two separate panels instead of one shared dock. The terminal owns the bottom of the editor and still opens with ⌘J; agent conversations own the right panel and open with ⌘L. Both can be open at once, each keeps its own size, tabs, and reload state, and closing one leaves the other alone — so you no longer have to give up your terminal to see an agent. The dock-position toggle is gone with the split; a terminal that used to sit in the right panel moves to the bottom on first launch, and the right panel keeps the width you had set.
A new tab in the terminal panel opens a plain shell by default — you no longer get dropped into a CLI you never chose. The New button's dropdown still lists every terminal CLI, though, so you can start a tab directly in one; that choice sticks, and the + button, ⌘J, and ⇧⌘J repeat it until you pick Terminal again. The agents panel's New button remains dedicated to in-app agents. Sending a passage to AI — from the selection bubble, a code block's Ask action, or ⌘J with selected text — still uses your preferred AI and reveals the panel where it runs.
The agents panel now keeps a small tab on the right edge whenever it is closed, so a conversation is always one click away even if you have never opened the panel. The terminal no longer has an edge tab of its own — ⌘J and the View menu open it — which clears the bottom-right corner it used to share with the Ask AI composer.
The bottom Ask AI composer moves from ⌘L to ⇧⌘L (Ctrl+Shift+L on Windows and Linux), and now hides whenever either panel is open — both are already places to type a request, so it stepped aside rather than offering a third.
Patch Changes
The agent thread composer no longer carries an add-context
+. It opened a one-row menu whose only row was the comment queue, and that row was inert unless the queue had already loaded and had something in it, so in practice the button opened a menu that could not do anything. Attaching a batch there was also the weaker of the two paths: it folded the comments into your next message but left every thread queued, while the Comments panel's own Send to chat dispatches to the open session and actually closes the review requests. That panel is now the single way to send queued comments to an agent, and the composer's bottom bar is just the agent settings on the left with the context ring and send button on the right.The agent composer now shows one action button instead of Stop and Send at the same time. While the agent is working the button is Stop; as soon as you start typing it becomes Send, and your message queues behind the current turn the way it always has. This matches how other agent chats behave and keeps a single clear action in the corner of the composer. Because Stop is hidden while you have a draft, pressing Escape in the composer now stops the agent, and your draft is kept so you can still queue it. Hovering Stop shows the shortcut. A stop that hasn't taken effect yet keeps showing its spinner even if you keep typing, so you can always tell whether the agent heard you.
Picking a value in an agent thread's settings menu now applies. Choosing a model, permission mode, or reasoning effort from one of the menu's submenus closed the submenu and jumped focus to the message field without ever sending the change to the agent, so the setting silently stayed where it was. The composer card focuses its message field when you press its whitespace; because React delivers a portaled menu's events through the component that opened it, that handler was also claiming presses inside the menu. It now acts only on presses that land in the card itself.
Agent chat threads now scroll like a real chat client. The transcript keeps itself pinned to the newest output while an agent is streaming, but the moment you scroll up to read something it stops fighting you — and a button appears to jump back to the live edge when you're ready.
Before After Scrolling up mid-stream was undone by the next chunk snapping you back to the bottom Scrolling up holds; new output no longer yanks the view No way back to the latest message except scrolling by hand A jump-to-bottom button returns you to the live edge Reopening a past thread dropped you at the very bottom Reopening lands you at the last turn, with the previous message in view Sending a message still brings you straight to the bottom so you see your prompt and the reply. Long threads with many tool calls also render more cheaply, since off-screen rows are no longer laid out until they scroll into view.
The agent thread's working row is now the OK mascot changing shape instead of a spinner. It holds each pose, changes into the next, and holds again, with a squash and a squint on top so a long wait reads as effort rather than a stall. The status line beside it says what the agent is actually doing when a tool is running (reading, drafting, retrieving) and otherwise drifts through a pool of phrases on a randomized beat, so the row keeps moving without ever implying progress it cannot see. The line fades between phrases rather than cutting, and the whole row fades in when a turn opens. Under reduced motion the mascot holds a single pose and only the fades remain.
Color themes are now authored in base16, the format the wider theming ecosystem already uses, and they reach considerably more of the app.
The built-in palettes (Dracula, Catppuccin Frappé and Latte, Monokai, Gruvbox, Solarized) are reproduced from their canonical upstream schemes, and Settings → Plugins → Themes gains a "Paste a base16 scheme" field: drop in any of the several hundred published schemes as YAML or JSON — in either the current Tinted Theming layout or the original one — and it applies as-is. The sixteen slots are also editable by hand. An existing custom theme built from the older six-color picker is upgraded automatically rather than reset.
The reason for the format change is coverage. The previous palette used ad-hoc names like "surface" and "accent" that several surfaces had no way to consume, so picking a theme left them on hardcoded colors. base16's slots carry fixed roles that map onto those surfaces directly, and the following now follow the selected theme:
- Source-mode syntax highlighting. The CodeMirror editor was pinned to one of two bundled palettes regardless of theme. This also covers the diff view, the text viewer, the text-file editor, and the Mermaid editor.
- Terminal colors. All sixteen ANSI slots, so program output is themed, not just the terminal's background.
- Callouts. All fifteen accent colors.
- Fenced code blocks in the WYSIWYG editor, plus inline code, blockquotes, tables, links, highlight marks, and horizontal rules.
- Lint squigglies, broken-link underlines, and the file tree's problem indicators.
html previewembeds, which now receive the host's live token values instead of a build-time snapshot of the default theme, so an embed matches the surrounding editor.- The desktop window chrome on Windows and Linux — the titlebar overlay and window background take the active theme's colors rather than staying neutral.
Also fixes three keyframe animations (the sidebar push pulse and two settings flashes) that painted nothing in any theme, including the default one, because they wrapped a color token in
hsl().Screenshots attached to a bug report now actually appear in the report. The previous release added this and it did not work: the screenshot was still left out of every report, silently.
Before After Report says a screenshot was included; nothing visible The screenshot is displayed in the report The cause was a dropped field. The app asks "does this bundle include a screenshot?" and passes the answer along when sending, but the layer that hands the request from the window to the app's background process was rebuilding it field by field and had never been taught about that answer. The background process saw no answer, took that to mean the reporter had declined, and left the screenshot out. Because "reporter declined" is a perfectly normal outcome, nothing was logged and nothing looked broken.
Unchanged: the screenshot is still opt-in via the checkbox in the report dialog, still previewed before you send, and unchecking it still means no screenshot leaves your machine. Re-sending an older report from the report list still sends it without the inline screenshot, since the capture is no longer in memory by then.
The config-sharing "Local only" choice is now labeled Only me across every desktop surface that names it — the create/open-folder dialog's radio card (
SharingModeField), the Settings → Config sharing card (SharingSection), and the explanatory info tooltip (ConfigSharingInfoTooltip). Both radio cards now carry the same description spelling out the consequence: your project's OpenKnowledge setup stays on this computer and is not committed to git.(The
ok initCLI prompt keeps its own terminal-context wording and is unchanged.)The first-launch "Connect your AI tools to OpenKnowledge" dialog is easier to scan and more transparent about what it changes.
- Per-tool location tooltip. Each AI tool now has an info affordance that discloses exactly which config file and entry OpenKnowledge would write — for example
~/.cursor/mcp.json→mcpServers.open-knowledge, or~/.codex/config.toml→[mcp_servers.open-knowledge]. This matches the disclosure Settings → AI tools already provides, so both surfaces tell you the same thing before you commit to anything. - Detected tools first, the rest tucked away. The MCP connections list now shows the tools detected on your machine up front and collapses the rest behind a "Show N more tools" toggle, so the list isn't a wall of tools you don't use. If nothing is detected, every tool still shows (with its setup link) so you're never left with an empty list.
- A pointer after Skip. Because the dialog only appears once, choosing "Skip" now leaves a short note telling you these same choices live in Settings → AI tools & CLI, so the surface isn't lost.
No change to what gets written or when — this is about legibility of the choice, not the behavior behind it.
- Per-tool location tooltip. Each AI tool now has an info affordance that discloses exactly which config file and entry OpenKnowledge would write — for example
Stopped building the Linux AppImage. Its launcher requires FUSE 2, which current mainstream distributions no longer ship, so the file failed to start out-of-the-box on stock systems (verified on Debian 13, both architectures). Linux ships as a
.deb; updates will be delivered through the system package manager (apt repository), and the in-app auto-updater is now disabled on Linux accordingly. Users without install rights can use the npm CLI or the web app.Fixed the Create-new-project dialog showing "No location selected" instead of the remembered location on systems where the OS cannot resolve the Documents folder (seen on headless Windows Server sessions). The location probe no longer fails outright in that case: it uses the remembered parent when one exists, and falls back to
<home>/Documents/OpenKnowledgeotherwise.Make project-wide Fix all fast and keep the app usable while it runs. On a 2,400-document knowledge base the Problems panel took several seconds to open in project scope and a full sweep took minutes with the app locked up for most of it; the panel now opens in well under a second and the same sweep finishes in around a minute, with the window on screen, and the app stays responsive throughout. File groups start collapsed, so the panel shows a list of files and their problem counts instead of mounting every problem in the project at once — a new control next to Refresh expands or collapses them all in one click. Progress now advances in batches rather than on every file, which also stops screen readers announcing thousands of updates during a sweep. The sweep paces itself and retries when the server is briefly at capacity, so it no longer starves other work happening in the project and no longer reports files as unfixable when they were only refused. A running sweep can now be stopped: previously the only way out of a Fix all started by mistake was reloading the page. Files already fixed stay fixed, and the panel re-checks the project so the remaining count is accurate.
Fixed a crash that could take down the whole app window, replacing it with the error screen. It hit when the set of open side panels changed (switching between files, clicking a folder, closing the right chat panel, or collapsing the right side panes), and was most likely with the document panel collapsed and a right-hand column open. The layout engine ended up validating a saved multi-panel split against fewer panels than were on screen and threw
Invalid 2 panel layout: ....The underlying resizable-panels library is now patched to recover from that momentary mismatch, re-deriving a valid split for the panels that are actually present instead of throwing, so opening, closing, and switching panels no longer risks crashing the shell. The recovered layout keeps each panel at its intended width rather than snapping everything to an even split.
Fix a family of frontmatter-boundary bugs that could silently remove body content from a document's view while leaving the bytes on disk intact.
A document whose body opens with a
---rule pair and carries no frontmatter was re-read as if the span between the rules were YAML, so that content stopped reaching the editor. Compositions that derive one of their two sides now keep the boundary unambiguous — a freshly serialized body re-spells its leading rule, and the property panel keeps an explicit empty block when removing the last property would hand the body's own bytes to the frontmatter parser. Documents that carry real frontmatter are unaffected, and content the agent writes still lands byte-for-byte.Also fixed at the same seam: a frontmatter block with no trailing newline was destroyed when a body was appended to it (reachable from ordinary typing and from an agent append), and agent
append/prependsilently dropped a leading fenced span of the payload — eating body content when that span was not valid frontmatter. Such a payload is now refused with the same 400 the identical payload already returns forreplace; payloads carrying well-formed frontmatter are unchanged.Fixed path displays mangling Windows paths that resemble escape sequences. A folder such as
C:\Users\x64qarendered asC:\Usersdqain the "Will be created at" caption (and any other message that shows a path), because the localization runtime decoded\x/\uescape sequences inside interpolated values. Decoding now applies only to the translated message text itself; values pass through verbatim. The actual files on disk were always created at the correct path — this was display-only.Declared three missing runtime dependencies in the Linux
.debpackage (libasound2,libcups2,libgbm1). All three are directly linked by the app binary but were absent from the package's dependency list, so on a minimal system the package installed successfully and the app then failed to start with no window and no error dialog. Systems that already have these libraries (most desktop installs) were unaffected.The Linux desktop build now also produces an
.rpmpackage, so Fedora, RHEL, and other RPM-based distributions can install OpenKnowledge with their own package manager instead of unpacking a.debby hand. The.rpminstalls the same way the.debdoes — it puts the app in the launcher, registers theopenknowledge://links, and puts theokcommand on your PATH — and it declares its system library requirements under their Fedora/RHEL names so the package manager pulls in anything missing. Both x86_64 and aarch64 packages are built.Prose containing a brace expression — an MDX comment like
before {/* note */} after, or any{…}span with a*,_,~or an HTML entity inside it — no longer accumulates backslashes. The serializer was escaping the inside of those spans as if they were ordinary prose, but the parser reads a matched brace pair as an opaque expression and never removes the escape, so each backslash was escaped again on the next pass: one delimiter went from\*to three backslashes, then seven, then fifteen, doubling forever. Because every mode toggle, reload, remote refresh and agent write re-derives the document, a paragraph could grow without anyone editing it. Those spans are now written out exactly as typed and are byte-stable from the first save. Documents that already picked up extra backslashes stop growing; their existing bytes are left alone rather than rewritten.Switching to source mode no longer flashes the landing highlight on a block it could not verify, so a flash now always means you landed on the text you selected.
The underlying mis-landing is unchanged for now: on documents whose editor block structure and markdown source disagree about how many top-level blocks exist (for example after deleting the paragraph between two bullet lists, which leaves two adjacent lists the markdown source cannot distinguish from one), the "View in source markdown" jump and the plain mode toggle can land one or more blocks past the intended target — one block for each such divergence in the document. Those unverified landings simply no longer paint the highlight.
Stop background sync from popping a GitHub sign-in window on Windows.
On Windows, a project with a GitHub remote could open a "GitHub — Select an account" window every few minutes, unprompted, for as long as the app was running — even while syncing was healthy and pushing successfully. The window came from Git Credential Manager, the credential helper Git for Windows installs by default, not from OpenKnowledge's own sign-in flow. OpenKnowledge already told git not to prompt, but that setting only silences git's own terminal prompt and does nothing about a credential helper's graphical prompt, so every background fetch that missed the credential store put a dialog on the desktop.
OpenKnowledge's own git commands now run fully non-interactively, so a missing or expired credential can no longer surface as a surprise system dialog. Instead it appears where it belongs: the sync status shows "reconnect to resume syncing" with a Sign in button, on your schedule rather than interrupting whatever you were doing. The same applies to opening a shared branch, where a credential dialog could previously outlive the fetch that raised it and sit on your desktop with nothing behind it. macOS and Linux were unaffected in practice, since their usual credential helpers never draw a window.
Fix the skills.sh page link on imported skills, and correct the
writetool's skill bundle-path description.The provenance link in the Skills UI built its skills.sh URL as
/<publisher>/skills/<skill>, but that path's middle segment is the source repo, not a literalskills. It resolved only for publishers who happen to name their reposkills; for everyone else it pointed at a page for a repo that doesn't exist. skills.sh answers those with a placeholder stub rather than a 404, so the link looked fine and quietly showed the wrong skill. It now derives from the recorded source viaskillsShSkillLinks, which also covers website-catalog sources (/site/<hostname>/<skill>). Same correction applied to theimporttool'ssourcedocs and theopen-knowledge-write-skillguide.The
writetool advertised skill bundle files as accepting "any path —references/**,scripts/**,assets/**, …", but onlyreferences/andscripts/are accepted; anything else is rejected. An agent following the description got an error contradicting it.Show an Update button on OK's built-in skills, and make
ok init --no-skillsstate that it opts out machine-wide.The built-in skills (
open-knowledge-discovery,open-knowledge-write-skill) had no way to surface an available update. The server already synthesizes their provenance and supports re-pulling them, but the built-in preview tab hardcoded its header actions to nothing, so the hook that checks for updates never mounted. The tab now shows a source link and an Update button when the upstream copy differs. Updates stay manual — built-ins never auto-apply.ok init --no-skillsreportedskipped (opted out via --no-skills). Nothing was skipped: the decline is recorded against your home directory and both bundles are removed from every user-global skill directory, so running it once in a throwaway project turned the built-ins off for every project on the machine. The summary now says that, and names how to undo it.installUserSkillalso enforces the opt-out itself rather than trusting each caller to check first, so a declined bundle can't be reinstalled by a caller that forgets the gate.Count skill installs on skills.sh, and let the MCP verbs reach every file a skill ships.
Installing a skill you found on skills.sh never registered with the marketplace you chose it from, so its install count — the signal its listing ranks on — stayed flat no matter how many people installed through OK. Explore installs now report to skills.sh. So do built-in and starter-pack skills, once per skill per machine, never on the launch reclaim. A hand-typed
owner/repois not reported: you never visited the marketplace, so it is not told what you installed. Turn it off in Settings → Preferences, or withDO_NOT_TRACK/DISABLE_TELEMETRY. Private and local sources are never reported.Separately, the MCP
write/edit/deleteverbs and theskills({ name, file })read only accepted paths underreferences/orscripts/, while an import writes a skill's whole directory verbatim. OK could install a skill and then refuse to open its own files —mattpocock/skills/grill-meshipsagents/openai.yaml. Any path that stays inside the skill dir is now accepted; the traversal, absolute-path and NUL guards are unchanged.Two rough edges on the new text-file editing: large-but-ordinary files like an 800 KB lockfile now open editable (only pathologically large files stay on the read-only viewer), and the visual/markdown mode switcher no longer shows for code and text files, which only have one editing surface.
Fix the slash, wiki-link and tag pickers destroying typed text inside code blocks and inline code spans. All three menus used to open in a code context, and choosing an item deleted the characters you had typed there: the query vanished out of the fence and a chip landed after it, while a slash command replaced the whole code block with the chosen node and lost the fence's language tag. The menus now stay closed inside code, matching every other typing shortcut in the editor.
Patch Changes
- Renaming a file in the Files sidebar no longer doubles its markdown extension. The rename box hands you the whole filename with only the name part selected, so typing or pasting a name that already ends in
.mdleft the original.mdin place and committednotes.md.md. That produced two files for one document:notes.md.mdis filed under the document namenotes.md, which resolves back to a different file, so the next save wrote the same document out again as a plainnotes.mdbeside it. Both names also land on one row in the sidebar, so only one of the two files was visible there. A typed.mdor.mdxnow replaces the retained one instead of stacking on it. Names that merely contain dots (v1.2.md,report.2026.md) and non-document files are untouched. This stops new doubled names from being created;.md.mdand.mdx.mdxfiles already on disk are left alone and still need renaming by hand.
Patch Changes
Turning on a plugin now tells you where to configure it. Enabling a plugin in Settings → Plugins raises a confirmation with an Open settings button that jumps straight to that plugin's own page — the page where the plugin actually does something, and which previously you had to know to go looking for in the sidebar.
Before After Enable Frontmatter schemas, nothing visibly happens "Frontmatter schemas enabled" with a button to its settings Its settings section appears in the sidebar, possibly scrolled off screen One click lands you on it This applies to every plugin — markdownlint, Frontmatter schemas, and the user-scope Themes plugin — rather than being wired up per plugin. Each plugin's settings page also now carries a Learn more link to its documentation, for whoever arrives after the confirmation has gone.
Patch Changes
- Keep validation state current instead of waiting for each file to be opened. Sidebar highlights and problem counts are now correct when you open a project, when you enable a plugin, when you toggle a rule, and when you switch git branches — previously a file only turned yellow once you clicked into it, switching a rule off left its counts standing, a branch switch kept showing the previous branch's problems, and project-wide state appeared only after visiting Problems → Project. Repeat audits over unchanged files are also much faster, and concurrent audits of the same project are coalesced into one. Audits no longer stall the server while they run: on a large knowledge base a project-wide audit previously blocked saving, search, opening files, and collaboration for its whole duration, and now runs in the background instead.
- Fixed the Linux desktop packages (
.deband AppImage), which installed successfully and then failed to start. Launching the app did nothing at all — no window, no error dialog — because the packaged bundle was built without its runtime dependencies, so the app exited on its first import before it could show anything. Linux packages now ship their dependencies and launch normally. macOS and Windows builds were never affected.
Patch Changes
Show agent icons in dark mode instead of near-invisible black marks.
Most of the agents in the catalog rendered as a black smudge on a dark background: Devin, DeepAgents, Factory Droid, Cortex Code, Dirac, fast-agent and every other agent whose icon comes from the ACP registry. Only Claude, Codex and Cursor looked right, because those three are the ones the app draws itself.
The registry requires every icon to be a monochrome SVG painted with
currentColor, so it can take the color of whatever surface it sits on. Loading one through an<img>tag defeats that: the SVG becomes its own document, andcurrentColorthere falls back to black no matter what the app's theme is. The icons now lift to white on dark themes. Light themes are unchanged.This covers every place these icons appear, not just the settings list: the agent launcher menus, the composer, the terminal's new-chat picker, thread history, and the thread empty state.
The bug report dialog also got a pass. It is wider, its "Previous reports" disclosure has room around the label on hover rather than a background flush against the text, and expanding it no longer leaves that background stuck on while the list is open.
Patch Changes
- Long values in the frontmatter properties panel no longer spill out of their pill background. A list property holding a long URL used to wrap across several lines while the chip's fully-rounded background ballooned into an oval that the text escaped, with the remove button floating over the text. Wrapped chips now render as a contained rounded rectangle; single-line chips keep their pill shape.
Minor Changes
Blank lines you type in the visual editor now stay where you put them. Previously the editor showed the space while you were typing and then quietly took it back — switching to markdown source and returning, reloading the page, or a teammate opening the same document all collapsed a run of blank lines down to one.
Before After Press Enter three times, reload — one blank line Press Enter three times, reload — three blank lines The file on disk kept your blank lines, the editor did not show them The editor shows what the file actually contains This covers blank lines between top-level blocks, which is where they come from when you press Enter. Two places still collapse them, both deliberately: runs at the very top or very bottom of a document, and runs inside a list, a quote, or a table cell — where CommonMark treats the blank line as meaningful structure rather than space. Nothing new is written into your files: no
, no<br />, just the newlines that were already there.Worth knowing: blank lines still render as nothing when a document is published to GitHub or any other CommonMark renderer. This makes the editor honest about your file, it does not add vertical space to published output.
Keep your place when switching between the visual and Markdown editors. Toggling modes now keeps the block you were looking at in view in both directions instead of dropping you at an unrelated scroll position, and a plain toggle no longer disturbs either editor's own selection.
Add a View in source action that jumps from the visual editor to the exact Markdown behind what you're looking at — centered, briefly highlighted, with the caret placed ready to edit. Reach it from the selection bubble menu, the desktop editor right-click menu, or the keyboard:
⌥⌘Mtoggles between visual and source, and⌥⌘Eopens the source for the block at the caret. The highlight respects your reduced-motion setting.Landing is honest about uncertainty rather than confidently wrong. If the document changes underneath a pending jump, or a block cannot be matched exactly, it settles on the nearest enclosing block and drops the highlight instead of guessing. Long documents keep working: the landing waits for the incoming editor to actually render, re-aims itself if content shifts while it settles, and gives up cleanly rather than fighting the page.
Explicit navigation always wins over a mode switch that is still settling. Clicking a Problems-panel row, an outline entry, or a find/replace match takes you there even immediately after a flip, and the composer's scroll-to-bottom no longer pulls you off a landing.
Underline is now a real, savable format. The bubble-menu Underline button and Cmd+U have always been there, but nothing wrote the result to disk: the underline vanished the moment the document was saved, reloaded, or opened by a teammate. Underlined text pasted in from Word arrived as italic instead.
Before After Cmd+U, reload the page — underline gone Cmd+U, reload the page — still underlined Paste underlined text from Word — comes in italic Comes in underlined Paste underlined text from Joplin or Logseq — formatting dropped Comes in underlined Underline is written as
<u>text</u>, the same thing Typora, MarkText and Obsidian write, so files stay readable and portable. Documents that already spell it<ins>text</ins>— what Joplin and Logseq produce — are read as underline too and keep that spelling when saved, so importing a vault does not rewrite it under you.Worth knowing: markdown has no underline of its own, so this is HTML inside your file. It renders as underline in OpenKnowledge, in a published site, and when pasted into Word, Docs or Gmail. GitHub is the exception — it strips
<u>, so the text shows up unformatted there. Files that use<ins>do keep their underline on GitHub.Underline can be combined with bold, italic, strikethrough and inline code on the same text.
Patch Changes
Screenshots attached to a bug report now show up in the report itself, instead of only inside the downloadable diagnostic bundle. Anyone triaging a report sees the picture immediately rather than having to download a zip and unpack it to find out what the reporter was looking at.
Before After Report says a screenshot was included; nothing visible The screenshot is displayed in the report Triager downloads the bundle and unzips it to see anything Nothing to download for a look at the screen This was supposed to work already, and never did once. The intake tried to pull the screenshot out of the bundle after the upload, but the storage the bundle lands in refuses that read, so the step failed silently on every single report and the screenshot was quietly left out. The app now uploads the screenshot alongside the bundle rather than expecting the server to dig it back out, which removes the failing step entirely.
Two things worth knowing. This takes effect for reports sent from this version onward, because the app is what supplies the screenshot now — reports already filed are unaffected. And re-sending a report from the report list in a later session sends it without the inline screenshot, since the capture is no longer in memory by then; the screenshot is still inside the bundle in that case.
Unchanged: the screenshot is still opt-in via the checkbox in the report dialog, still previewed before you send, and unchecking it still means no screenshot leaves your machine.
Version history that a consolidation had folded away could be destroyed by routine checkpoint cleanup. Consolidation picked the single newest checkpoint to chain onto, without regard to what kind it was. The silent rescue checkpoints written when the editor bridge detects content loss are parentless, so whenever one of them happened to be newest, the new consolidation chained onto a dead end and the previous consolidation was left dangling. The next cleanup pass then deleted that dangling checkpoint's ref, and with nothing else pointing at it, its full content snapshot and every edit it had folded in became unreachable and were lost at the next git garbage collection. A same-second pair of consolidations could fork the chain the same way with no rescue checkpoint involved at all.
Consolidation now adopts every dangling anchor rather than the single newest one, and only checkpoints whose retention cannot be emptied are eligible to anchor. Cleanup is non-destructive again. Two side effects of the old behavior are also fixed: a rescue checkpoint is no longer kept alive forever by being chained onto, so the retention budget that expires it now genuinely does, and the per-document activity history no longer goes silently empty after a rescue checkpoint lands following a consolidation.
Show comment annotations dimmed in the editor instead of hiding them.
Text that looks like comment syntax (
<!-- note -->,%%note%%) is claimed by the comment promoter the next time a document is re-derived. The resulting comment mark and comment block rendereddisplay: none, so prose a user typed as ordinary text disappeared from the editor while the bytes on disk stayed intact, with no way to recover it from the WYSIWYG.Comments now render dimmed in the editing surface, with the delimiter form shown alongside the body and a dashed rail plus gutter marker on the block form so it reads as annotation rather than as a blockquote. The app's read-only views that mount the editor schema (skill viewer, rendered diff) show comment bodies dimmed too; only fully rendered markdown output still hides them, as literal HTML comments. Comments are still dropped from cross-app clipboard payloads, and bytes are untouched in both directions.
To turn an annotation back into ordinary text, edit the delimiters in source mode (
Cmd+/) — backslash-escaping either delimiter (\%\%) keeps the run as prose. There is no WYSIWYG control for this yet.Keep backlinks, graph data, and tags consistent across every document lifecycle path. Direct creates and duplicates, disk reconciliation, deletes, folder renames, project-skill moves, ignore-file changes, startup, and branch switches now pass through one ordered derived-index coordinator with shared admission, readiness, persistence, and invalidation rules.
Derived-view queries wait for startup and branch replacement to settle instead of observing a partially rebuilt index. Rapid consecutive branch switches are serialized so one transition cannot release another transition's barrier.
Durable creates, duplicates, renames, deletes, and persistence stores now return according to authoritative disk state. Derived projection and cache persistence remain best-effort, so coordinator shutdown or a delayed cache save cannot turn an already successful write into a failure.
Fix multiple self-closing MDX JSX tags on the same paragraph collapsing to text. The R23 autolink/void-HTML guard classified any
<X…as self-closing when the paragraph contained any/>, soA <Foo />, B <Bar />, C <Baz />.promoted every<up to the last one and lost the intermediate tags. It now decides self-closing per-tag by scanning that tag's own contents (quote- and brace-aware), skipping<characters that fall inside another uppercase tag's attribute region. Orphan uppercase openers stay guarded; quoted>//>and<X>inside JSX expression braces round-trip cleanly.Fix the outline not following your scroll position in Markdown source mode
The outline pane now highlights the heading you are reading while you scroll the Markdown source view, the same way it already did in the rich-text view. The position marker, the highlighted row, and the row's accessibility state all keep up as you scroll, and clicking an outline row leaves that heading highlighted once the view settles.
Headings written with Windows line endings are also recognized now. Previously a document saved with CRLF line endings produced an empty outline.
Two more places where the editor could replace what you had open with the copy on disk now leave a restorable version behind first.
The first is a conflict between an agent and something else writing the same file. When an agent saves a document that another program changed underneath it, the copy on disk wins — that part is correct and unchanged, and the agent is still told its write did not land. But the open document was replaced outright, and anything you had typed into it that had not saved yet went with it, silently and with no way to undo. The second is the same situation for a skill or template edited from two places at once.
Both now write a recovery checkpoint of the open document before replacing it, so what you had is restorable from version history, and both record the event so it shows up in a diagnostics bundle rather than disappearing without trace.
The Problems panel now offers a Fix all with AI button beside the deterministic fixer, in both the document and project scopes. It hands the whole scope to your preferred AI agent as one instruction, pointing it at the
audittool for the current list of lint violations and broken links and atlintwithfix: truefor the mechanically-fixable subset, so the agent reads live truth rather than a snapshot taken when you clicked.The deterministic button is now labelled Auto-fix rather than "Fix all", and both buttons explain themselves on hover. Previously a document whose problems had no automatic fix showed a single disabled "Fix all" with nothing to say for itself, which read as a bug rather than as "these need judgement"; now the AI action sits next to it, enabled, and the disabled Auto-fix says why it is off.
The Problems panel now names the validator behind each finding instead of tagging every row
LINT— a frontmatter-schema failure readsFRONTMATTERand a markdownlint rule readsMARKDOWNLINT, so the two are distinguishable at a glance (the chip carries the producer, so the line below it now shows the bare rule code). Repeated findings collapse into one row with an instance count ("Frontmatter property is missing · 10 instances") that expands to the individual lines, each keeping its own Fix and Ask AI actions, instead of burying the rest of the plane under identical rows. The project-scope refresh icon finally explains itself on hover.Clicking Edit next to a frontmatter schema in Settings now reliably opens the Fields editor. Previously it could drop you on the read-only Source view instead, which was especially confusing when the schema was already the file open behind the Settings dialog, and the ignored request could then send you to Fields the next time you opened that file when you had asked for Source. Edit now takes effect whether or not the schema is already open.
Stop two typed editor states from losing content on the next re-derivation.
A hard break typed at the end of a block (Shift+Enter) was dropped at serialize time, so the break vanished with no trace in the bytes and none in the view. It is now re-spelled to
<br />, the same lossless spelling the serializer already uses inside table cells and headings.An empty task item lost its checkbox and came back as a plain list item whose text was the literal
[ ], at every position in the list. GFM cannot spell a task item with no content, so an empty one now serializes with a the parse side takes back out, and the checkbox round-trips.Sharing a folder with no documents in it now says so plainly instead of promising the link will work after the next sync. Git has no way to represent an empty folder, so the minted link 404s for the recipient and no push would ever fix it — but the share popover offered "Enable auto-sync", "How to push manually" and "Sync now", and running a sync turned the warning into "Synced. The link is up to date." over a link that still went nowhere. The popover now shows a warning naming the only real remedy, adding a document, and offers no sync buttons at all. The
share_linkMCP tool relays the same sentence to agents rather than the push advice, and the freshness signal onPOST /api/share/construct-urlgained anemptyverdict so the two cases are also tellable apart in the logs. Folders that genuinely just need a push are unaffected, and a doc reports exactly as before at any size, including zero bytes.On Windows, a project inside a git repository now correctly places
.ok/at the repository root when the path contains a shortened folder name. Windows keeps a legacy short alias for most long folder names —C:\Users\alexandrais also reachable asC:\Users\ALEXAN~1— and the two spellings point at the same folder but read as different paths. OpenKnowledge compared the shortened form against the long form that git reports, concluded the repository was outside your home directory, and quietly skipped the promotion, leaving.ok/config.ymlin the sub-folder you picked instead of at the repository root. Both spellings are now resolved to the same canonical path before the comparison, so the repository root is used as the project root as intended. Unaffected on macOS and Linux, which have no such aliases.
Minor Changes
- Serve your knowledge base's MCP endpoint to your other devices through any HTTPS tunnel — ngrok, Cloudflare Tunnel, Tailscale, or a reverse proxy: run the tunnel against the stable remote port (default 24550,
remote.portto change) and start the server withok start --remote <url>(or setremote.urlin.ok/config.ymland pass a bare--remote). The model is trust-the-tunnel: OpenKnowledge does not authenticate remote callers — anyone who can reach the tunnel URL has full read-write access, and restricting reach is the tunnel's job (private tailnet, ngrok OAuth, Cloudflare Access). The trade is always explicit: remote access is armed only by the--remoteflag (never by config alone), every remote start prints a warning banner, and a server started without the flag refuses tunneled requests with a fix-it hint instead of serving them. The web UI is served from the same tunnel URL (/= editor,/mcp= MCP — one port, one tunnel; browse and edit from your phone). Its collaborative surfaces work over the tunnel too: live editing and the Ask AI panel, which runs an agent on the machine hosting the server (using that host's own agent and credentials). Wrong-Host requests (DNS rebinding) are refused everywhere. Local loopback behavior is unchanged.
Patch Changes
Pasting a copy of an entire document no longer makes the paste disappear. Selecting all, copying, and pasting produces a document that is an exact doubling of what was just saved, which is the same shape a stale browser cache produces when it merges an old copy back in. The persistence guard that watches for that corruption could not tell the two apart, so about two seconds after the paste it refused to save and rolled the open document back to the copy on disk. The pasted content vanished from under the cursor, and undo could not bring it back because the rollback arrives as a remote change that neither the server nor the editor tracks as undoable.
The guard now distinguishes the two cases by when the doubling appears. A stale-cache merge shows up as soon as the document syncs, before the session has saved anything; a paste is an edit on a document that has already saved cleanly. Pastes are left alone and save normally. When the guard does still act on a genuine corruption, it now writes a recovery checkpoint of the pre-rollback document first, so that version stays restorable from version history instead of being lost.
Minor Changes
Edits typed while something else is rewriting the same document no longer disappear. The bridge between the visual editor and the raw markdown behind it gained four safety nets, all default-on: a defer guard that holds back a rebuild which would overwrite a keystroke that has not propagated yet, a pre-drain flush that pushes a pending non-overlapping keystroke into the document before an agent write or undo rebuilds it, a loss detector that spots content dropped at the reconciliation boundary, and a fixed-point backstop that freezes a rebuild loop which oscillates instead of settling. The practical effect: typing through an agent write, a
git pullthat lands on the open document, or a same-second edit from a second window is far less likely to cost you a line.- Anything a safety net cannot save in place is written to a recovery checkpoint, and those checkpoints now appear in the Timeline as ordinary "Recovered content" versions you can preview and restore like any older version. Before this release the same checkpoints were written but reachable through no API and invisible in the UI, so rescued content was effectively lost anyway.
- Undo in source mode is now a single origin-aware system. Cmd/Ctrl-Z undoes only what you typed, never a collaborator's or an agent's write. Previously CodeMirror's own history could revert content you never wrote.
- Unsynced work survives more of the browser lifecycle: a tab flushes to the server when you switch away and resyncs when you come back, edits captured during a server-instance recycle land in a durable IndexedDB outbox so a tab crash mid-recycle no longer drops them, and a worker-homed presence heartbeat keeps a backgrounded tab from being pruned as offline by its peers. In the desktop app, a window holding unsynced work opts out of Chromium's background throttling until it is clean.
- New project config leaves, each a kill-switch defaulting to on:
bridge.deferGuard.enabled,bridge.preDrain.enabled,bridge.lossDetector.enabled,bridge.fixedPoint.enabled,bridge.flushOnHide.enabled,bridge.backgroundThrottle.enabled. Turn one off only to isolate a suspected regression. - New
lossCapture.enabledandlossCapture.maxBytesconfig leaves plus a local event ring at.ok/local/loss-capture/(roughly 12 MB before it rotates). It records which safety net fired, on which document, and when, and it is content-free by schema: a byte length and a digest, never document text. Nothing leaves your machine until you runok diagnose bundle; setlossCapture.enabled: falsefor a sensitive workspace.
Stop recovery-checkpoint cleanup from discarding the newest rescue point. Checkpoints written in the same second could not be ordered by their timestamps, so the sweep picked which one to drop essentially at random and could keep an older copy while permanently deleting the most recent one. Because loss-hardening writes one checkpoint per open document in a burst, the discarded copy could be a document's only recovery point. Cleanup now keeps every checkpoint it cannot confidently order, so it may hold slightly more than the retention budget rather than destroy the wrong one.
Correct the Source editor entry in the keyboard shortcut help. It advertised undo-selection shortcuts that source mode does not have; those shortcuts still work in the code block editor and in code-shaped property fields, and are now listed there instead. Redo shortcuts are unchanged and continue to work on every platform.
ok diagnose bundlenow redacts by default, and the y/N prompt states plainly what leaves your machine before it writes anything.Credentials are scrubbed on every bundle. GitHub PATs, AWS access keys, Anthropic and OpenAI keys, bearer tokens, JWTs, and URL-embedded credentials are replaced with
[REDACTED-*]placeholders, and macOS/Linux home-directory paths are anonymized to~/(the path hierarchy below the home directory is preserved).ok diagnose bundlenever ran this scrub before, so bundles produced by earlier versions could carry a live token;ok bug-reportand the in-app bug report already did.The content directory path is masked as
<CONTENT_DIR>, so a shared bundle does not leak your home-directory layout.The flag is now
--no-redact, an explicit opt-out that writes a raw bundle for you to inspect locally: no credential scrubbing and no path masking.--redactis still accepted and is now simply the default, so existing scripts and muscle memory keep working.Document names ship in cleartext. The old opt-in
--redactpass hashed them todoc:<8hex>and wrote a<bundle>.docnames.jsoninverse-map sidecar next to the zip; both are gone. Legible document names are what make a bundle diagnosable, so the tradeoff is now an informed one rather than a silent one: the consent summary says "in cleartext" next to the count, and the manifest'sredactionblock no longer carriesdocNameMapSidecarordocNameCollisions.The consent summary is now itemized by what actually leaves the machine: document names, whether the content directory path is masked, and whether credentials were scrubbed (with the line count). Under
--no-redactit says so in as many words.Bundles carry the content-loss ring as
state/loss-current.jsonlandstate/loss-prev.jsonlwhen it exists, so a "my edit vanished" report arrives with the evidence attached. The ring is content-free by schema.The desktop app now scrubs captured renderer console output before writing it to
~/.ok/logs. Console capture is on by default, and a credential printed to the console previously landed verbatim in that log file for its 7-day retention — the keyed-field redaction in front of the log never inspected the message text. The same scrub also masks/Users/<name>/paths to~/there, matching what the web build already did on its way to the server log.
The in-app bug report and
ok bug-reportalways redact.ok diagnose bundleis the only surface with an opt-out.GitHub-style
<div align="center">wrappers now render the way GitHub shows them: the tag lines disappear and everything between them — headings, text, badge images — actually centers (left/right/justify work too). The content inside stays fully editable, and the<div align>markdown is preserved byte-for-byte on disk. Divs with other attributes keep their literal rendering.Documents no longer get permanently stuck offline after the server restarts. Previously, if the restart took more than a couple of seconds, an open document could land in a state where it reconnected forever without ever syncing: the banner said "Connection lost, keep this tab open, your edits will sync when reconnected", but nothing it promised was going to happen. Switching to another document and back did not help, and anything typed while stuck was lost on reload. The client now treats a restarted server the same way whichever way it finds out about it, so the affected documents recover on their own and the edits typed during the outage are replayed instead of dropped.
The slash menu now has an Emoji entry — type
/emoji(or just/:) and an emoji picker opens right at your cursor, with search. Picking one inserts it as plain text, so it round-trips through markdown untouched.Enter inside a table now works the way it does in Notion and Obsidian: it moves the cursor to the same column of the row below, and from the last row it creates a new row — so you can fill a table top to bottom without touching the mouse. Enter no longer splits a cell into multiple paragraphs (which markdown tables can't represent and used to resolve unpredictably); Shift+Enter is the way to get a line break inside a cell.
Patch Changes
Bug reports sent from the desktop app now carry the evidence needed to diagnose an editor crash. Two pieces were missing. First, the detailed-diagnostics report is meant to be a superset of the basic one, but it was leaving out the app's own log file — the one the desktop app writes the editor's console messages into. Basic reports included it, detailed ones did not, so the most thorough report was the one that dropped the crash trail. Second, when the editor hit an error, the report recorded the error message but not React's component stack, which is the only part of a crash that still names real parts of the app in a released build; without it, a crash report read as a string of meaningless letters and numbers. Reports now include both, so a crash can be traced without asking you to copy anything out of a developer console by hand. Secrets in the newly included log are redacted the same way as everywhere else, and the component stack is trimmed to its most relevant frames.
The Default tile in Settings → Plugins → Themes now previews its own colors instead of borrowing the palette you currently have selected. Picking Dracula used to repaint the Default preview in Dracula's colors, so the grid gave you no way to see what switching back would actually look like. The Default tile now shows the app's own light or dark palette, following your light/dark preference, whichever theme is active.
Fix the editor's main content being squeezed into the top of the pane with a large empty band below it. The bottom sessions dock could be left holding space while hidden and showing nothing, and its drag handle is disabled while hidden, so there was no way to reclaim the space. The dock now collapses whenever it reports a size while hidden, so that state can no longer persist, and it records a diagnostic when it has to repair itself so a recurrence leaves a trace. Separately, the dock's height is re-clamped to at most half the window whenever the window is resized — previously a height chosen on one display kept its old ceiling after a move to a smaller one.
Frontmatter schema settings are now just the on/off toggle.
The Modified badge and the Reset-to-default button are gone. Both described the config file rather than the schema: "Modified" meant only "config.yml maps this file", on or off, and Reset removed that mapping. Neither is something you need a model of to decide whether a schema should validate your docs, and in practice the words did the opposite of explaining themselves. Reset in particular read as "refresh", so pressing it to reload the panel silently discarded every glob you had written. The Only modified filter went with them, since the concept it filtered on no longer appears anywhere in the surface. Search still narrows the list.
Your globs are always kept. Turning a schema off leaves its
appliesTopatterns in place, so turning it back on restores exactly what you had. The toggle never discards a mapping. If you want a schema out ofconfig.ymlentirely, edit the file — or delete the schema, which removes the file and its mapping together.Shorter delete confirmation. Deleting a schema no longer adds "Docs it validated keep their frontmatter; they just stop being checked." Nobody expected deleting a schema to rewrite their documents, so the reassurance mostly introduced the doubt it was trying to settle.
Keyboard shortcuts no longer fire underneath an open dialog, command palette, or menu. Pressing ⌘T with the ⌘K palette up opened a new editor tab behind it, and the same leak affected ⇧⌘T, ⌃Tab, ⌘1–⌘9, ⌘L, ⇧⌘J, ⌘F, ⌘G, ⌘Z and ⌘⇧I — these are registered on the window, most of them in capture phase, so an overlay had no way to stop them from underneath. Every app-global shortcut now declines while a layer that owns the keyboard is open. Escape likewise dismisses only the topmost layer instead of also collapsing the graph panel or closing a diff pane behind it, and ⌘K no longer stacks the palette on top of another dialog while still dismissing the palette when it is the top layer. Editing keys the app never claimed — copy, paste, cut, select-all, undo, and arrow/Enter navigation inside the overlay — are untouched, and a hover-opened link panel no longer counts as owning the keyboard, so shortcuts keep working while the pointer rests on a link.
On the desktop app, ⌘N, ⌘,, ⌥⌘S, ⌥⌘B and ⌘J arrive as native menu accelerators rather than as renderer key presses, so they can still fire while an overlay is open; those five are fixed in the browser only.
Fix bold, italic, and other formatting being dropped from wiki links, images, inline math, tags, and line breaks.
Formatting applied to an inline element was lost as soon as the document round-tripped through the collaboration layer. Writing
**[[Page]]**in source mode, in the editor, or through the agent API produced[[Page]]on disk, and the emphasis could not be re-applied because it was discarded again on the next sync. A soft line break inside bold text split it into two separate bold runs.Before After **[[Page]]**→[[Page]]**[[Page]]**→**[[Page]]****→**→****a<br>b**→**a**<br>**b****a<br>b**→**a<br>b**This affects
[[wiki links]], images, image references, footnote references, inline math, tags, inline JSX, and hard line breaks.Documents already saved without their formatting are not repaired automatically — the formatting has to be applied again. If you run multiple clients against one server, update them together: an older client editing a document alongside an updated one will strip the restored formatting.
Patch Changes
- Fixed a crash that could take the whole editor down to the "Something went wrong" screen during navigation — most visibly right after creating a new file, and also when a document was renamed, deleted, or moved out from under an open tab. The editor tracked its open-document pool with a value that was rebuilt from scratch on every pool change, including changes that altered nothing you could see, such as re-opening a document that was already open. Each rebuild re-rendered the app shell and handed the navigation layer a fresh set of callbacks, which prompted it to re-open the document again. When a document's identity changed underneath that cycle, it fed itself until React aborted the render with "Maximum update depth exceeded". The pool value is now reused when nothing observable has changed, so the cycle can no longer close.