v0.11.0
Part of the OpenKnowledge changelog.
Minor Changes
Lifecycle commands (
ok start,stop,status,clean,ui,mcp,preview, and bareok) now work from any subdirectory of an initialized project. The CLI walks up to the nearest enclosing.ok/config.yml— the same resolution the MCP server uses, so both always agree on which project (and whichserver.lock) a directory belongs to — and boots with that root's config instead of failing with "This directory isn't set up yet." When the resolved root differs from your current directory, the CLI prints[ok] Using Open Knowledge project at <root>so the project scope is always visible. Nested projects compose: the walk stops at the closest ancestor, so a subtree you've separately runok initin keeps its own root. Directories with no enclosing project still get the "runok initfirst" message, and scaffolding commands (ok init,seed,clone) keep their existing current-directory semantics.ok startnow has a clean, legible terminal. DiagnosticINFOlogs are routed to the on-disk log file (still captured for bug reports) instead of flooding stdout, so the startup banner — Editor + API URLs plus a "Next steps" hint — is front and center. The skill/MCP/launch-json reclaim sweeps no longer print routine JSON to the terminal (only genuine failures surface), and invalid frontmattertagsentries (e.g. comma-joined Obsidian-style values) are dropped silently instead of warning once per file. To restore the full log stream, setLOG_LEVEL=infoor pass--log-level <level>(which now takes effect — previously it was parsed but ignored).Two reliability fixes ride along: the HEAD watcher falls back to chokidar when
@parcel/watcheris unavailable (packaged builds ship without the native addon), so git branch-switch detection keeps working instead of silently degrading; and pressing Ctrl+C now prints an immediate "stopping…" notice explaining that pending changes are being saved and the server lock released.Add semantic ("by meaning") search to the Cmd+K omnibar.
When semantic search is set up for a project (enabled plus an embeddings API key), the omnibar shows a "By meaning" filter pill beside "By tag". Clicking it opens an exclusive mode where typing stays instant and local; pressing Enter fires one vector search through the existing
/api/searchsemantic fusion, the same engine the MCPsearchtool uses. Results are sticky: editing the query after a search keeps the prior results (dimmed) and offers a one-key re-fire, so a stray keystroke never costs the expensive result set. Escape exits the mode before closing the palette. When semantic search is not set up the pill is hidden and the omnibar is unchanged.Omnibar-initiated semantic searches now carry a
sourcetag so their cost can be counted apart from the MCP tool's.
Patch Changes
ok startstays responsive while indexing large content directories. The boot-time index scans (backlink graph, tags, asset basenames, and the file-watcher's seed walk) previously used synchronous recursive directory reads, so on a vault with thousands of files the server's event loop was blocked for several seconds — Ctrl+C produced no feedback and editor/API connections couldn't be served until indexing finished. The walks now use async filesystem calls that yield to the event loop, so shutdown signals and incoming connections are handled while indexing proceeds. The same fix shortens the window where the desktop app's editor is slow to connect on large projects, since the desktop spawns the same server boot path.Fix stale-editor content resurrection (PRD-6955b). An editor whose Y→PM apply path had stopped (a "wedged" collaboration binding) could silently re-publish its frozen, minutes-old copy of a document wholesale over newer collaborative state on the next click or keystroke — erasing remote fixes. A new client-side binding staleness guard now detects the wedge the moment an external update goes unapplied, refuses to publish the stale replica (both the transaction channel and the binding's write-back seam are gated), and transparently recycles the editor so it remounts from current document state. Healthy editors are unaffected; recovery is rate-capped per document to prevent recycle loops under a persistent fault.
ok auth status --jsonnow includes abackendfield naming where credentials are stored —keyring(the OS keychain) orfile(~/.ok/auth.yml). The field appears in every JSON status shape, including when you are not logged in, so you can confirm which storage backend is active without a stored token. Human-readable (non---json) output is unchanged.Fast-exiting CLI commands no longer dump a wall of minified bundle source to the terminal. Commands that exit within the same tick as startup — most visibly
ok startin a directory that hasn't beenok init'd — raced the asynchronous open of the CLI's diagnostics log file: pino's exit-time flush hook then threwsonic boom is not ready yeton the never-opened file descriptor, burying the real one-line message under the error and a giant minified code frame. The log-file destination now opens synchronously, so the exit-time flush always finds a ready file descriptor and the clean message (e.g. "Runok initfirst") is all you see.Fixed a document-corruption bug where reopening a doc the server had unloaded and re-read from disk (an external delete + recreate — e.g. a
git pullor checkout rewriting files — a rename, or any other server-side unload) could merge the browser's cached copy into the fresh document as a second materialization: every block appeared twice, and content deleted on disk resurrected. The server now stamps each loaded document with a lineage epoch; browsers record the epoch they synced and present it when rejoining, and a stale rejoin is rejected up front — the client clears its cached copy and reloads the document cleanly instead of corrupting it. The same fence covers the deferred cache-attach window during boot, so a cache learned to be stale after sync is discarded rather than merged. Also fixes a separate, pre-existing cleanup gap the fence depends on: clearing the browser cache of a renamed or deleted doc was silently skipped in tabs that had not yet observed a branch, leaving stale rows that could re-merge on a later open.Show a redacted tail of the stored embeddings key in Settings
The "API key set" panel in Settings now shows the last four characters of the stored key (the rest masked) so you can tell at a glance which key is configured. The key is still never returned in full: the status endpoint exposes only the redacted tail, and only when the key is long enough that four characters are a negligible fraction.
The git credential helper (
ok auth git-credential get, invoked by git on every sync) now records a diagnostic line to~/.ok/logs/cli.*.logon every credential lookup: the host, the active storage backend, and the outcome —found,absent(no credential stored), orread-error(the keychain read failed, e.g. locked keychain or access denied). Hits log atdebug(silent at the default level); misses log atwarn. Previously these only went to stderr, which git swallows, so a vanished credential left no trace. The log is flushed before the helper exits so the record reliably lands. Token values are never logged.Omnibar "By meaning" UX: carry typed text into the mode, and show indexing progress
- Switching to "By meaning" (clicking the pill) now keeps whatever you already typed as the semantic query instead of clearing it — only a
tag:filter prefix is dropped. The typed text becomes the pending search you can fire with Enter. - The "By meaning" view now shows an indexing banner with live coverage ("Indexing your pages — N of M ready. Results may be incomplete.") while the corpus is still embedding. The first by-meaning search lazily kicks off that background index, and the banner ticks up as it progresses, so it is clear when results may not yet be complete.
- Switching to "By meaning" (clicking the pill) now keeps whatever you already typed as the semantic query instead of clearing it — only a
Fix silent loss of collaborative edits that land while a document editor is still opening. The editor's pre-warm mount path captures document state at construct time, and a remote or agent edit arriving in the brief window before the editor view binds was never reconciled — the editor showed stale content, and the first click or keystroke could republish that stale copy over the shared document, erasing the missed edit for every peer and on disk. A walk-currency check now rides every pre-warm mount: it watches the document fragment from construction, and if anything changed before the view bound, it invalidates the pre-warm and re-derives the editor content from current state. Quiet mounts keep the full pre-warm fast path.
The document Properties panel now reflows to a vertical layout when the editor pane is narrow. Each property's value drops below its name and spans the full width of the pane, instead of being squeezed into a thin column beside a fixed-width label.
Previously the panel always used a two-column layout with a fixed label column, so on a narrow pane (side panels open, split view, or a small window) long values like a title or description wrapped into a tall sliver with most of the row left empty. The panel now switches to the stacked layout based on its own width, so it adapts whether the whole window is small or just the editor column is narrow. Wide panes keep the original side-by-side layout.
Tighten loose permissions on the embeddings secrets file when reading it
The embeddings API key in
~/.ok/secrets.ymlwas re-secured to 0600 only on write. A file left group/other-readable (an older build before chmod-on-write, an external tool, or a hand-edit) could stay world-readable indefinitely, since the key is read on every search but rewritten rarely or never. Reads now self-heal: a secrets file with a mode looser than owner-only is tightened to 0600 the moment it is read, with a one-time warning. Best-effort and never throws, so it stays safe on the search read path.The store also reads a key stored under the previous field name as a fallback, so a key written by an earlier build keeps working after the field was renamed to
OPENAI_API_KEY; the next time the key is set, it is rewritten under the current name (one-shot and self-clearing).Make semantic search retrieval rank-based so synonym queries return matches
Semantic ("by meaning") search returned nothing for synonym queries: "cybersecurity" missed a page about hacking, "kid" missed a page about a child. The cause was an absolute cosine floor of 0.35 applied before ranking. text-embedding-3-small produces a compressed similarity band for short keyword queries against whole-document embeddings (correct hits commonly score 0.13 to 0.29), so the floor discarded every candidate and search fell back to pure keyword matching, which a synonym cannot satisfy.
An absolute threshold is the wrong mechanism here: the right value is model-specific, and on a compressed scale a weak-but-real hit and weak noise overlap (a correct hit scored 0.147 while an unrelated query scored 0.151), so no threshold separates them. Retrieval is now rank-based: the closest pages by cosine are returned and ranked, with no absolute cutoff by default. The omnibar bounds the "By meaning" list by a result count (the same cap as keyword search) rather than a score, since nearest-neighbor search always has a closest match. An optional per-project hard cutoff remains via search.semantic.similarityFloor for anyone who knows their provider's cosine scale and wants to suppress weak matches.
This affects both the MCP search tool and the omnibar "By meaning" mode.
Fix the sync status dialog and recovery when a GitHub credential is missing. Previously a credential-less fetch/push was misclassified as a retryable network error, so the popover showed "Offline" with a raw
could not read Username … Device not configuredstderr blob and "Sync now" / "Retry" buttons that could never succeed — and the one useful action, reconnecting GitHub, was never offered. Now this is classified as an auth error (auth-no-credential): the dialog reads "Reconnect required" with a clear one-line message ("GitHub sign-in is missing or expired. Reconnect to resume syncing.") and a single "Connect GitHub" button. Reconnecting resumes sync without an app restart, and the auth-error state no longer persists across restarts. The server also setsGIT_TERMINAL_PROMPT=0so git fails fast instead of attempting a no-TTY prompt.