Skip to content

🚀 Release BOSS 9.5.8

Latest

Choose a tag to compare

@github-actions github-actions released this 04 Sep 00:19
· 1 commit to main since this release

BOSS v9.5.8

Release Date: 2026-09-04

Four changes. The hard freeze on 9.5.7 is fixed - a browser tab whose renderer stopped answering took the EDT and the macOS menu bar with it, and force quit was the only way out. Alongside it, the double-shift search gains four kinds of thing that were each reachable from only one place, the new-tab URL field gains the ghost-text completion the address bar has always had, and the standard browser shortcuts arrive with three latent bugs that stopped a chord firing at all.

Highlights

  • A wedged renderer no longer freezes the application. Three code paths made a blocking JxBrowser round trip from Dispatchers.Main, and a renderer parked on a modal window.prompt cannot run script until that dialog is answered. There is no suspension point in the call, so a withTimeoutOrNull around it was not a bound: the EDT parked forever, AppKit's main thread parked behind it, and the window and the menu bar went with them. Captured with jstack against two separate freezes. A wedged tab now costs one parked daemon thread and later calls on that tab answer null on schedule (#321)
  • Tools, settings, MCP tools and recent pages are in the double-shift search. All four were already indexed somewhere - tools in the launcher dialog, settings in the Settings window's own box, MCP tools in Toolbox, recent pages on the dashboard - and reachable only from there. Tools lead the results now, because a tool is a thing you open and use where a file match is a thing you then have to search inside (#266)
  • The new-tab URL field completes inline, and matches like an address bar. Gray ghost text, accepted with Tab or Right at the end of the input, drawn with a VisualTransformation so it is never part of the field's value. Matching went from contains anywhere in the domain, URL or title to per-term matching where word starts outrank mid-word hits, so "boss pulls" finds github.com/risa-labs-inc/BossConsole/pulls and "loc" stops ranking a PR titled "…block…" first (#323)
  • The standard browser shortcuts, in one shared table. Reopen closed tab (Cmd+Shift+T), tab select by position (Cmd+1..8, Cmd+9 for the last tab), positional stepping (Cmd+Opt+Arrow, Cmd+Shift+Bracket), history (Cmd+[ / Cmd+]) and DevTools (Cmd+Opt+I). KeymapPresets.standardBrowserBindings() is merged into all four presets rather than copied into four hand-written lists, and a candidate is dropped where a preset already claims the chord - so IntelliJ keeps Cmd+1 on the Project tool window and simply gets no Cmd+1 tab select (#322)

Features

  • search: tools, settings, MCP tools and recent pages are new sources. Two of the four cannot be reached from the search service at all - the settings index is desktopMain while the service is commonMain, and a window's tools belong to its own component while the service is one object shared by every window - so both arrive through SearchSources as suppliers rather than snapshots, since a list captured once would go stale into a search index, which is the one place staleness is invisible (#266)
  • search: selecting a settings row navigates and highlights. SettingsWindowState.reveal carries the row through to the window with its own nonce, so picking the same row twice works twice. Settings match on label, breadcrumb and keywords, with keywords scored below a label hit, so "passkey" reaches "Platform Authenticator" without outranking a row called that (#266)
  • search: tools are indexed unfiltered by the sidebar's hidden set, for the reason the tools launcher already gives - a tool someone hid is the one they will come here to find. allSidebarTools() is one definition shared by both surfaces (#266)
  • browser: inline URL completion in the new-tab dialog. A host completes before a path, so three characters do not fill in a deep PR URL; a deletion does not re-complete, an accepted completion is not extended, and Escape cancels the proposal as well as the list. One derived urlToOpen is read by Enter, the Done action and the confirm button, ranked highlighted row > ghost completion > typed text, so no two commit paths can disagree about where the field goes (#323)
  • browser: rankMatches works per term - every whitespace-separated term must match, in the address or the title, with word-start matches in a tier above mid-word ones. Word starts alone lost "tube" for youtube.com, hence the second tier. A pasted URL is canonicalised before matching, or the whole paste was one term longer than any address. This is the only query matcher in the tree and the browser's own address bar goes through it too, so both suggestion lists improve (#323)
  • keymap: Cmd+Opt+Arrow is its own action rather than a second binding on tab.next. Ctrl+Tab is MRU-driven and commits when its held modifier is released, so a discrete chord routed through that path would start cycles nothing ever commits (#322)
  • keymap: reopen is type-agnostic by construction - the entry is a TabInfo and addTab rebuilds it through the same tab-type factory that opened it. Runner terminals are excluded because their id is the handle RunnerTerminalService tracks and closing tells it to forget them; download-redirect auto-closes are excluded because the user never closed them; a still-live id is focused rather than rebuilt, since addTab has no duplicate guard and would orphan the running component (#322)

Bug Fixes

  • browser: BrowserHandleImpl.executeJavaScript (the plugin-facing suspend API), injectPageEventScript (via putProperty to newJsProxyObject) and CoBrowseRtcPeerImpl all blocked the EDT on a renderer reply. The blocking call goes on a dedicated single daemon thread and, where a result is needed, the wait is bounded from a different one - both on one thread and the timeout cannot fire. Browser-process calls are deliberately left where they are: loadUrl, browser.url() and dispatch are answered by a process page JS cannot block (#321)
  • browser: CoBrowseRtcPeerImpl injected straight from its LoadFinished callback, which is delivered on the RPC thread - the same hazard #268 fixed, at a call site it missed. It is posted onto the peer's own thread now, with the injected flag claimed atomically so two LoadFinished events cannot queue a second injection (#321)
  • browser: requestPictureInPicture was a fourth EDT site the source guard cannot see - a non-suspend override on the public BrowserHandle interface that blocked on the renderer inline, so the EDT arrived from a plugin's caller one module away. It goes through BoundedBrowserCall.post, the fire-and-forget half the class was missing (#321)
  • browser: sendDom gated on BoundedBrowserCall.backlog alone, which a peer page that has not answered never reaches - runJs appends to pending and returns while ready is false, so the cap was bypassed in exactly the state its KDoc describes. The cap moved into runJs and counts DOM frames separately, since pending also holds the offer and every ICE candidate (#321)
  • keymap: alternateKeystrokes was modelled on KeyBinding, with helpers and serialization, and consulted by neither matcher. It is what makes Cmd+Plus reach zoom in alongside Cmd+Equals (#322)
  • keymap: AWTKeyboardInterceptor emitted "Left" where the presets store "DirectionLeft", so no arrow binding ever matched there - Cmd+Arrow panel navigation worked only through the native menu accelerator and went dead whenever a terminal or browser held focus. Panel navigation is now also gated on panelCount > 1, mirroring the menu item's enabled flag, so a single-panel window still leaves Cmd+Left to mean caret-to-line-start (#322)
  • keymap: a host binding that matched but had no dispatch case fell through to the plugin-default pass, inverting the documented "host bindings always win" rule. EDITOR_GO_TO_LINE (Cmd+L) is exactly that shape, so a plugin registering Cmd+L as a GLOBAL default shadowed it and consumed the event - a plugin's onAction returns Unit and dispatch reports success regardless (#322)
  • keymap: KeyStroke.signature() sorted modifiers and uppercased the key but folded neither, while sameChordAs folded both, so migration's chord check and its rebind check disagreed. A keymap spelling panel navigation ["Meta","Option"] + "Right" looked free to migration, a second action was added onto the same chord, and since findMatchingBinding folds when it matches the winner was map iteration order - with no conflict badge either, because KeymapValidator compares the same signatures. The fold now lives in the model beside canonicalModifiers and repairs claimsChord, the validator and sameChordAs together (#322)
  • keymap: Cmd+[ and Cmd+] matched nothing on Linux and Windows. Compose renders the bracket keys "Left Bracket"/"Right Bracket" on some platforms and "Open Bracket"/"Close Bracket" on others, and unifying three key-name tables into one dropped the pair macOS does not use. CanonicalKeyNameTest now enumerates every spelling the three old tables accepted, so a dropped alias fails on any platform rather than only the one that uses it (#322)
  • keymap: ClosedTabHistory could outlive a plugin. Entries closed before an unload stay on the stack and a third-party TabInfo is one of the plugin's own classes, so up to 25 of them pinned its classloader - and after an update the new factory would receive an instance of the old class, with a config as MyTabInfo throw escaping the reopen collector. dropEntriesFor runs in the teardown path and rebuildClosedTab discards an entry whose factory throws (#322)
  • keymap: the Shortcuts row filter hid any plugin-prefixed entry whose plugin was not loaded, including a user's own rebind of another plugin's shortcut, which they could then neither see nor reset. It is scoped to the ids the host authors on a plugin's behalf (#322)
  • browser: https://s completed nothing. The canonical part of the typed text was located with indexOf, which finds the first occurrence, and a short canonical form occurs inside the very prefix that was stripped to produce it - canonicalUrlKey("https://s") is s, and "https://s".indexOf("s") is the s of https. The offset is computed from what canonicalisation removes and then validated against the typed text rather than trusted (#323)
  • browser: a completion may only add a path under a host the typed text already names, never change it. History is attacker-influenceable, and one drive-by visit to paypal.com-login.evil.example would otherwise turn a typed paypal.com into someone else's domain, which Enter would take. Candidates carrying a query string or invisible characters are refused, as are addresses too long to read (#323)
  • browser: UrlCompletion carries the displayed text and the navigation target separately, because they are not the same string - display is canonical so it lines up with what is typed, target is what history recorded. Collapsing them sent a stored http://192.168.4.20:8123/… through processUrlInput as https:// and dropped the www. off a host whose certificate needs it (#323)
  • browser: paths match case-sensitively. Splicing the typed casing onto a stored tail produced …/bossConsole/pulls, an address in neither place (#323)
  • search: rendering and selection disagreed about what "first" means. SearchResultsList draws the All view grouped, walking SearchCategory in declaration order, while the keyboard indexed getFilteredResults() sorted by score - so the top row and the row Enter fired could differ. One sort by category ordinal then score descending now serves the drawn order, the arrow keys and Enter (#266)
  • search: the empty state listed five tiles for ten sources - the panel a user reads before typing described the search as it was before this release. The tiles derive from SearchCategory.entries now, chunked into rows of five so a narrow window wraps instead of clipping (#266)
  • search: four section headers drew the generic grid icon. SectionHeader carried a second icon table naming only the five original categories and falling through to Apps, so Tools, Settings, MCP Tools and Recent Pages each drew a grid icon while the identical chip a few pixels above drew the right one. Icons and accents are one definition each now, with no else branch to inherit silently (#266)
  • search: a search that opened a tool could close it instead, and the highlight effect could not see a null to null transition, so picking the same settings row twice worked once (#266)

Improvements

  • browser: the 1000-entry history cap applied only when writing to disk, so the in-memory map grew for the life of the process while the per-keystroke cost went up roughly tenfold. It is pruned in memory too, titles are capped at 256 characters (a page controls its own title), the lookup runs off the composition thread, and the per-term address scan is computed once instead of twice - about a third of the matching pass (#323)
  • browser: the search row now uses the same encoder as the confirm path, which the row and Enter previously disagreed about (#323)
  • search: nine copies of "log the pick, call the handler, close if there is none" became one dispatchResult, and the four new row kinds share one renderer and one table rather than four call sites that drift apart (#266)
  • search: MCP tools are indexed but deliberately have no action - they take arguments a search row cannot collect, so selecting one closes the dialog. The question this answers is "is there a tool for this, and what is it called", so the row shows the name clients actually use and whether it is switched off. It reads allTools, not the exposed set: a disabled tool is precisely the one someone is looking for (#266)
  • threading: the EDT guard is a source check, for the reason InjectJsCallbackOwnershipTest gives - the failure mode is a new call site, and reproducing it needs a real Chromium and a page that genuinely stops answering. It fails against all three sites as they stood before the fix, verified by reverting both files and watching the tests go red. Its lexer no longer desyncs on a nested-quote string template, which had made blockAt either truncate a block or run it to EOF. docs/THREADING.md gains the post and upstream-queue rules (#321)
  • tests: GlobalSearchOrderingTest pins the "atlas" case directly, that categories come out contiguous rather than interleaved, and that TOOLS is the entry after ALL, so a future reshuffle of the enum fails the build rather than quietly reordering the UI (#266); CanonicalKeyNameTest is mutation-checked against removing either bracket spelling (#322); the completion lookup dispatcher is an overridable seam, so the tests drive it from the composition's own dispatcher rather than racing advanceTimeBy against a pool the test clock does not touch (#323)

Known Limitations

  • The search row can sit below the dropdown's fold with a busy history. A real query is long enough or contains a space, both of which suppress completion, and the list scrolls (#323)
  • IDN and homoglyph lookalikes are not addressed. The invisible-character filter is not a confusables policy (#323)
  • Whether a completion is offered still says nothing about visit count. A visitCount > 1 floor was considered and not taken: visitCount counts calls to addUrl, whose only caller is the browser plugin's title listener, so a visited page raises its own count with two assignments to document.title. It would exclude the accidental single visit and not the adversarial one the rule is for (#323)
  • The new-tab dropdown rows still differ from the address bar's in icon and delete-button behaviour (#323)

📦 Downloads

Platform Architecture Package
macOS Universal (Apple Silicon + Intel) BOSS-9.5.8-Universal.dmg
Windows x64 BOSS-9.5.8.msi
Windows ARM64 BOSS-9.5.8-arm64.msi
Linux DEB AMD64 (x86_64) BOSS-9.5.8-amd64.deb
Linux DEB ARM64 (aarch64) BOSS-9.5.8-arm64.deb
Linux RPM AMD64 (x86_64) BOSS-9.5.8-amd64.rpm
Linux RPM ARM64 (aarch64) BOSS-9.5.8-arm64.rpm
Linux JAR AMD64 (x86_64) BOSS-9.5.8-amd64.jar
Linux JAR ARM64 (aarch64) BOSS-9.5.8-arm64.jar

Always latest - newest stable release, resolved server-side, so these stay
correct in a bookmark and need no API key:

macOS DMG · Windows x64 · Windows ARM64 · Linux DEB amd64 · Linux DEB arm64 · Linux RPM amd64 · Linux RPM arm64 · Linux JAR amd64 · Linux JAR arm64

Release metadata - version, every asset, sha256 checksums - is at ?app=boss.


Full Changelog: v9.5.7...v9.5.8