v0.71.1
Patch Changes
-
The editor header no longer lets its trailing actions cover the file-navigator toggle at narrow widths.
The header lays out a leading rail (the Files toggle) and a trailing rail (Share, sync status, presence, Settings, Resources) as two overlays that measured each other but never negotiated for width. At a phone-width window with the file navigator open the header is about 97 px wide, the trailing rail kept its full width, and it spilled left over the toggle: tapping the toggle activated Settings instead, and two trailing buttons landed over the navigator and could not be tapped at all.
The header now compares the trailing rail's own width against the space the leading rail leaves it. When they do not both fit, the trailing actions collapse into a single More actions button that opens them in a popover; every action keeps its label and behaviour. Where both rails already fit, which is every desktop width, nothing changes.
Sync connection toasts no longer depend on the header's layout. They were hosted inside the presence avatars, so they are now raised into a dedicated host that stays mounted whatever the header does.
The More actions button also carries the sync state it hides. When git sync is in conflict, offline, needs reauthentication, or is paused, the button shows a coloured dot and names that state in its accessible label, so a collapsed header never hides a sync problem.
When the header gets narrower still and cannot seat the leading rail, the trailing button and the document tab strip at the same time, the tab strip now hides instead of drawing a clipped sliver underneath the buttons.
-
Configure semantic-search text chunks per indexing request, character budget, and request timeout from
Settings → Search, so slower local embedding servers can be tuned without patching the installed OpenKnowledge bundle.Clarify that semantic search indexes pages when you search, and show indexed coverage without an idle loading spinner.
-
A terminal tab now shows its shell prompt as soon as it opens. Before this, output the shell printed before the tab was listening could be dropped, leaving the tab on
Starting terminal…until you pressed a key. A tab whose shell never starts at all now saysThe terminal couldn't start.and offersTry again, instead of reporting that the shell stopped unexpectedly. -
Keep dialogs and menus open while following an agent's file edits.
Follow-the-agent no longer navigates away from a settings dialog or menu that you opened. Following resumes on a later agent update after you dismiss the overlay.
-
New tabs keep their file-creation actions while the Agents panel or terminal is open.
Previously, opening either panel hid the template list and the "or create a new file" link. Both actions now remain available in empty and existing projects. Loaded templates stay visible across panel toggles, and wheel or trackpad scrolling over the end of a template list continues through a short pane to reach the file action. The compact view remains centered beside a right-docked terminal and bottom-aligned above a bottom-docked terminal. The duplicate AI composer remains suppressed.
-
The Ask AI composer no longer covers the last line of a document as it grows.
Typing a long prompt at the end of any file the editor opens in a text view (
.ts,.py,.json,.yaml,.txtand the rest), or of a.mmdor.mermaiddiagram in source mode, used to let the growing composer cover the line the caret was on. The composer reserved room at the bottom of those editors but the scroll position did not follow it, so the last line slid underneath. It now stays above the composer. -
Fixes a bug where an agent write that Open Knowledge had already confirmed and saved could be silently undone by another program a moment later.
An editor or script holding the older version of a file in its buffer, and saving it after the agent write landed, put the exact bytes that preceded the write back on disk. Open Knowledge saw a file whose content did not match what it last wrote, found the open document unchanged since that save, and concluded the file had been edited outside the app. It applied those bytes as an ordinary external edit, so the acknowledged write disappeared from the open document with nothing to indicate anything had gone wrong.
The server now remembers, for a bounded window, which versions an acknowledged agent save displaced. It stores a local durability snapshot for only those recently displaced documents, including the acknowledged and rejected content needed to recover safely after an app restart. When a file on disk turns out to be one of those displaced versions while a newer save is the current one, the file is no longer applied. The document keeps the acknowledged content and is frozen with a
stale-external-writeconflict reason, so later agent writes fail explicitly instead of building on an unnoticed rollback. The existing conflict view and conflict tools expose both versions and can resolve the document by keeping either one or combining them. Until you choose, the file on disk is left exactly as the other program wrote it: Open Knowledge does not write over it, so there is no save fight between the two.External edits that do not exactly restore a version Open Knowledge itself displaced still reconcile the way they always have. An intentional exact restore of a recently displaced version is indistinguishable from a stale editor save, so it is surfaced for confirmation instead of being applied silently. The record of displaced versions is per branch and is dropped when a document is deleted. Its protection window expires after 30 minutes; expired text is removed from the recovery file only on a later state-persistence event, not by a timer or restart. Unresolved stale-write conflicts remain until they are resolved.
Conflict resolutions can explicitly write an empty string to keep an empty file; only the separate
deletestrategy removes the file. The diff view preserves the selected bytes even when the current Git version in the editor differs from its committed stage.Recovery snapshots contain document text and are written with owner-only permissions. If a snapshot is corrupt, unreadable, or from an incompatible version, the server preserves it and stops opening the project with an actionable recovery message rather than discarding potentially unique edits. A write that discovers a stale file during its final flush returns a distinct 409: its collaborative edit and recovery snapshot are retained, but its disk write is skipped. Further external saves cannot discard that pending edit; explicit resolution is required. Resolve and re-read before retrying.
-
The Timeline no longer reports "History unavailable", and a folder's timeline card no longer disappears, on branches whose names contain characters git accepts but the history endpoint used to reject.
A branch named
worktree-design+atc-release-packageused to get a 400 from/api/history, which the document Timeline rendered as "History unavailable" and a folder's timeline card rendered as nothing at all — the card was simply absent from the folder overview, with no message.feature-caféand fully non-Latin branch names were rejected the same way. Twenty-one printable-ASCII characters real branch names may contain —+,%,#,=,&,!,$,(,),,,;,'and others — plus every non-ASCII character and any leading underscore are now served.Both surfaces also now log the server's problem type and title to the browser console when a history request fails, so a Timeline that will not load says why.
One class is still rejected: the shared rules refuse any whitespace, including non-ASCII whitespace such as a non-breaking space, which git itself does allow. A branch named that way still gets a 400 and a dead Timeline.
-
Crash reports now include evidence of prolonged main-thread freezes.
Until now the "previous session ended without a clean quit" report carried one liveness fact, the last time the main thread's own 60-second heartbeat ran. A main thread that hangs stops that heartbeat exactly the way a crash does, so "the app died at 11:29" and "the app froze at 11:29 and you force-quit it four hours later" produced identical reports and pointed at different bugs.
The desktop now runs a watchdog on a worker thread that records main-thread pings every five seconds. At the next launch, the desktop log reports whether the last successful sample observed a block of at least 15 seconds, together with the measured duration, threshold, sample time and evidence state. A
diedverdict means that sample was below the threshold; it does not rule out a shorter or recovered block, or a block after the watchdog stopped writing. Missing or unusable evidence yields no verdict. The worker re-baselines after a whole-process pause so sleep does not count as a hang. These diagnostics do not change whether the app offers a crash report. -
The folder-config, template, and folder Activity (
/api/history) endpoints now refuse to read or write through symbolic links planted in a folder's.ok/directory. Previously, a maliciously committed symlink at<folder>/.ok/frontmatter.yml(arriving viagit clone/pull of an untrusted branch) letGET /api/folder-configdisclose any YAML file the server process could read (for example~/.config/gh/hosts.ymlor~/.kube/config) as folder frontmatter, and onePUT /api/folder-configfolded the link target's keys into the merge and replaced the link with a real, git-trackable file containing that foreign content. A symlink at<folder>/.ok/templates/<name>.md— or a symlinked.ok/.ok/templatesdirectory reached by requesting a descendant folder — similarly letGET /api/templatereturn the link target's raw bytes verbatim, and a symlinked<folder>/.okdirectory aliased both endpoints into another directory. The seed/ok initpath could also create a file outside the project by writing through a committed dangling symlink at a scaffolded leaf.The server now:
- rejects a symlinked
<folder>/.okon the requested folder on every folder-config, template, and folder-history arm, and — on the template arms, reads AND writes — a symlinked<folder>/.ok/templatesdirectory, a symlinked template leaf (GETrefuses to read through it;PUT/import refuse to write through it), and a symlinked ancestor.ok/.ok/templateson the fetch-by-name walk, all withurn:ok:error:symlink-refused. The folder-config arms degrade instead of rejecting for the template menu: a symlinked.ok/templateson the requested folder (wherever it points, dangling included), or a symlinked.ok/.ok/templateson any ancestor it inherits from, is skipped by the menu and reported byGET /api/folder-configviawarnings+warningCodes(templates-symlink-refused, ortemplates-unverifiablewhen itslstatfails with anything butENOTDIR; a non-directory.okor.ok/templates, on this folder or an ancestor, provably holds no templates and produces notemplates-*code); - reports a symlinked, malformed, or uninspectable
frontmatter.ymlinGET /api/folder-configviawarnings+warningCodes(symlink-refused/malformed-yaml/unverifiable, withfrontmatter_local: null) while the rest of the folder payload keeps working, and treats such a leaf — or a symlinked containing.ok— as absent in folder-metadata enrichment and template-menu enumeration; - writes scaffolded files with
O_EXCL, so a committed dangling symlink at a seeded leaf fails rather than writing through; - degrades instead of erroring on the folder-config and folder Activity arms for three shapes the path gate used to reject: a folder's own
.ok/templateslink that resolves outside the content root (was 400path-escape), a regular file named.ok, and a.okdirectory the server cannot search (both were 500). All three now answer 200 onGET /api/folder-configand onGET /api/history?folder=.GET /api/folder-confignames what it dropped inwarnings+warningCodes—templates-symlink-refusedfor the first,unverifiablefor the second,templates-unverifiable+unverifiablefor the third;GET /api/history?folder=has nowarningsfield, so it simply answers.PUT /api/folder-configstill fails a folder whose.okis a regular file or is unsearchable, now from the write itself rather than path validation; it no longer rejects a folder whose own.ok/templatesresolves outside the content root, since the write never reads or writes that path. Those arms no longer run the.ok/templatescontainment check: the unconditional check on<folder>/.okalready covers every escape through an ancestor or.okitself, and a symlinked.ok/templatesis refused by identity incollectFromFolderbefore anything under it is enumerated. The template arms keep both checks; - returns 500 (carrying the errno) from
POST /api/template(move) when an ancestor.ok/.ok/templateson the source folder's walk cannot be inspected, matchingGET /api/template, where it previously fell through to a misleading 404template-not-found.
This is a blanket refusal of symlinks at
.okartifact paths on the HTTP plane, broader than the earlier escaping-symlink fixes: an in-root.oksymlink is refused too, because the folder-config write merges and rewrites the leaf in place. If a folder's properties, its template picker, or its Activity feed stop resolving after upgrading, replace the symlink under that folder's.ok/(thefrontmatter.ymlleaf, the.okortemplatesdirectory, or a template file) with a regular file. - rejects a symlinked
-
Broken links inside a
log.mdno longer show up as work to do.A project log is an append-only history. Its entries name pages that have since moved, and pages nobody has written yet, so unresolved links are what a log is supposed to accumulate. Until now every one of them was reported as a broken link to repair: in the Problems panel, as a sidebar tint, as an editor squiggle, in
ok audit, and in the advisory an agent gets back from every write or edit. That last one is what made this more than noise, because agents are told to read that advisory and fix what it names, and the only way to fix a stale entry in a log is to edit history that was meant to be permanent.A new project setting, Ignore broken links in log.md under Settings ▸ This project ▸ Preferences ▸ Content rules, leaves those links out of all of it. It is on by default and shared with the project through
config.ymlasvalidation.suppressLogLinkAdvisories. Turn it off and every finding comes straight back, everywhere, with no restart.If you gate CI on
ok audit --errors-onlywithvalidation.links: error, reserved-log findings no longer contribute toerrorCountwhile this default-on setting is enabled. Setvalidation.suppressLogLinkAdvisories: falseto keep the previous gate.The match is deliberately narrow: the stem must be exactly lowercase
log, with a supported Markdown extension, at any depth. The extension's case is not what decides, solog.MDis a reserved log too;LOG.mdandcatalog.mdare ordinary documents and keep their findings. What decides is the file a link is written in, not the file it points at, so a broken link to a log from anywhere else still reports normally.Nothing is hidden from the views you use to inspect links on purpose. The Links panel, the editor's unresolved-link styling,
links({ kind: "dead" }), andGET /api/dead-linksall show a log's unresolved links in both states.For agents, filtered results are now explicitly conditional rather than quietly redefined. When the policy withholds findings, audit, write, and edit responses carry
brokenLinkSuppression, naming the reasonreserved-log-policyand how many final findings were withheld. On write and edit it sits besidebrokenLinks. WhenbrokenLinkSuppressionis absent,brokenLinks: []still means what it always did: every outbound link resolves. When the marker is present, treat the empty list as filtered, not confirmed. No href or path comes back, because none of them is repair work, and the bundled project skill and MCP tool descriptions now say exactly that. -
Missing external apps now appear behind “Show N more” in Agent connections settings, including enabled apps and apps with setup to remove. Detected and unknown apps stay visible; all apps appear when none would remain visible or while searching. All three sections announce their expanded state and retain keyboard focus within the group when a focused disclosure disappears.
-
Settings → AI tools no longer makes a connected agent look like something is still wrong.
A connected project MCP entry carried an amber follow-up line, announced as a live status update, directly under the checkmark and "Installed". It read like a security step OpenKnowledge was waiting on, and it never went away, because OpenKnowledge cannot tell whether the agent has accepted the entry. Claude Code asks for approval on its own the first time you run it in a project. Codex and GitHub Copilot load a project entry once the folder is trusted, which the CLI asks about on first run in a new folder; their setup dialogs now say so, and Codex's names the remedy. Those rows now show no follow-up line at all.
Cursor keeps one, for the case where you really do have to act: a project-only registration. Cursor lists a new
.cursor/mcp.jsonserver as disabled until you switch it on under Customize → MCPs, and OpenKnowledge cannot see that setting, so when the project entry is the only thing connecting Cursor the row says exactly that and names where to go. A user-level registration, whichok initwrites by default, starts on its own and its row shows no line. It now reads as ordinary muted text instead of an amber status. -
ok deinitandok uninstallnow allow up to 15 seconds for the Windows identity probe to start PowerShell on busy systems instead of 5 seconds; macOS and Linux keep the existing 5-second timeout. -
Theme changes now fade semantic app colors, terminal colors, and live HTML previews while preserving reduced-motion preferences and native window chrome settlement.
-
Make the bug report dialog more compact with an enlarged screenshot preview, drag-and-drop image attachments, and expandable diagnostics details. Keep detailed diagnostics opt-in and clarify the privacy disclosures.
Image selections that exceed the type, count, or 3 MB total limits are now rejected with a visible message instead of being silently trimmed or blocked later. Existing attachments are preserved.
- @inkeep/open-knowledge-core@0.70.5-beta.0
- @inkeep/open-knowledge-server@0.70.5-beta.0
Downloads
| Platform | Architecture | Download |
|---|---|---|
| macOS | Apple Silicon | OpenKnowledge-arm64.dmg |
| Windows | x64 | OpenKnowledge-Setup-x64.exe |
| Windows | arm64 | OpenKnowledge-Setup-arm64.exe |
| Debian / Ubuntu | x64 | OpenKnowledge-amd64.deb |
| Debian / Ubuntu | arm64 | OpenKnowledge-arm64.deb |
| Fedora / RHEL | x64 | OpenKnowledge-x86_64.rpm |
| Fedora / RHEL | arm64 | OpenKnowledge-aarch64.rpm |