v0.22.0
Released Jun 30, 2026. Part of the OpenKnowledge changelog.
Minor Changes
- Add an in-app product-updates subscription. The Resources menu is regrouped into Resources, Community, and Product updates, and a new Subscribe action opens a reusable email form (shadcn + react-hook-form) that registers the address through the
/api/subscribeendpoint. The GitHub row now shows the repo's live star count.
Patch Changes
- Fix image and file paste/drop in the packaged desktop app, which failed with an "internal server error" toast (the upload endpoint returned HTTP 500).
- Coalesce the redundant
GET /api/documentsrequests fired on load. Several consumers independently fetch the same full listing — the page-list context (asset/folder/file paths), the empty-editor state (entry count for the onboarding branch), and the wiki-link suggestion source (referenced assets) — so on boot and on every file-change push the server ran the sameready-gated disk walk two or three times concurrently. These now share a single in-flight request and one JSON parse. It is single-flight, not a result cache: the slot is released as soon as the request settles, so consumers that refetch on a file-change push still get current data. The sidebar file tree keeps its owndepth=1lazy fetch (a different URL) unchanged. - Paint the sidebar file tree incrementally as the directory listing streams in, instead of withholding the whole level until the walk finishes. The
GET /api/documentsNDJSON walk is now applied to the tree per network chunk (additively, so folders not yet streamed are never pruned mid-stream), and the loading skeleton clears on the first batch. The authoritative prune + optimistic-merge reconcile still runs once as a single splice when the stream completes, so the final tree is unchanged. On a knowledge base with a large top-level directory the first rows appear sooner rather than after the entire level is enumerated. - Fix the Properties panel rejecting Obsidian-style empty
tags:/aliases:frontmatter. Files that use Obsidian's default "no tags" shape (an emptytags:list whose only item is a blank-, or a baretags:with no value) parse to YAMLnull, which the read schema previously rejected for the whole frontmatter block — so the panel showed nothing (or an error banner) and refused every edit, even to the file's other valid properties. The read path now coerces these empty shapes to empty values (tags:\n-reads as an empty list, a baretags:as empty text), so the panel reads and edits these files normally. Files are not rewritten on disk until you actually edit a property. - Speed up project load for knowledge bases with many files.
GET /api/pagespreviously re-read and re-parsed every markdown file from disk on every request — including the redundant full-directory pass that ran concurrently with the watcher's seed walk on cold load, and a fresh full re-read on every window focus / file-change refresh. Page titles and icons are now cached on the in-memory file index (derived from the content the watcher already reads for its change-detection hash, so no extra disk reads), and/api/pagesserves them straight from memory. Titles/icons stay current through create, edit, and rename events. Behavior is unchanged; only the cost of listing pages drops from O(files) disk reads per request to an in-memory scan. - Desktop: launching an agent CLI from the docked terminal ("Open in Claude/Codex/Cursor/OpenCode") no longer pollutes your shell history. The launch command used to be typed into an interactive shell, so every launch — prompt and all — was recorded in
~/.zsh_history, cluttering↑/Ctrl-Rand writing document content in plaintext outside.ok/. The launch is now baked into the tab's shell spawn ($SHELL -l -i -c '<cmd>; exec $SHELL -l -i'): the command runs without going through the line editor (so it is never recorded), PATH is unchanged (the same login-interactive shell is used), and the tab drops into a fresh interactive shell when the agent exits — so your own later commands still record normally. Applies to all four CLIs.