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 modalwindow.promptcannot run script until that dialog is answered. There is no suspension point in the call, so awithTimeoutOrNullaround 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
VisualTransformationso it is never part of the field's value. Matching went fromcontainsanywhere in the domain, URL or title to per-term matching where word starts outrank mid-word hits, so "boss pulls" findsgithub.com/risa-labs-inc/BossConsole/pullsand "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
SearchSourcesas 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.revealcarries 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
urlToOpenis 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:
rankMatchesworks 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
TabInfoandaddTabrebuilds it through the same tab-type factory that opened it. Runner terminals are excluded because their id is the handleRunnerTerminalServicetracks 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, sinceaddTabhas no duplicate guard and would orphan the running component (#322)
Bug Fixes
- browser:
BrowserHandleImpl.executeJavaScript(the plugin-facing suspend API),injectPageEventScript(viaputPropertytonewJsProxyObject) andCoBrowseRtcPeerImplall 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()anddispatchare answered by a process page JS cannot block (#321) - browser:
CoBrowseRtcPeerImplinjected straight from itsLoadFinishedcallback, 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 twoLoadFinishedevents cannot queue a second injection (#321) - browser:
requestPictureInPicturewas a fourth EDT site the source guard cannot see - a non-suspend override on the publicBrowserHandleinterface that blocked on the renderer inline, so the EDT arrived from a plugin's caller one module away. It goes throughBoundedBrowserCall.post, the fire-and-forget half the class was missing (#321) - browser:
sendDomgated onBoundedBrowserCall.backlogalone, which a peer page that has not answered never reaches -runJsappends topendingand returns whilereadyis false, so the cap was bypassed in exactly the state its KDoc describes. The cap moved intorunJsand counts DOM frames separately, sincependingalso holds the offer and every ICE candidate (#321) - keymap:
alternateKeystrokeswas modelled onKeyBinding, with helpers and serialization, and consulted by neither matcher. It is what makes Cmd+Plus reach zoom in alongside Cmd+Equals (#322) - keymap:
AWTKeyboardInterceptoremitted "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 onpanelCount > 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'sonActionreturns Unit and dispatch reports success regardless (#322) - keymap:
KeyStroke.signature()sorted modifiers and uppercased the key but folded neither, whilesameChordAsfolded 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 sincefindMatchingBindingfolds when it matches the winner was map iteration order - with no conflict badge either, becauseKeymapValidatorcompares the same signatures. The fold now lives in the model besidecanonicalModifiersand repairsclaimsChord, the validator andsameChordAstogether (#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.
CanonicalKeyNameTestnow 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:
ClosedTabHistorycould outlive a plugin. Entries closed before an unload stay on the stack and a third-partyTabInfois 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 aconfig as MyTabInfothrow escaping the reopen collector.dropEntriesForruns in the teardown path andrebuildClosedTabdiscards 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://scompleted nothing. The canonical part of the typed text was located withindexOf, which finds the first occurrence, and a short canonical form occurs inside the very prefix that was stripped to produce it -canonicalUrlKey("https://s")iss, and"https://s".indexOf("s")is thesofhttps. 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.examplewould otherwise turn a typedpaypal.cominto 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:
UrlCompletioncarries 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 storedhttp://192.168.4.20:8123/…throughprocessUrlInputashttps://and dropped thewww.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.
SearchResultsListdraws the All view grouped, walkingSearchCategoryin declaration order, while the keyboard indexedgetFilteredResults()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.entriesnow, chunked into rows of five so a narrow window wraps instead of clipping (#266) - search: four section headers drew the generic grid icon.
SectionHeadercarried a second icon table naming only the five original categories and falling through toApps, 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 noelsebranch to inherit silently (#266) - search: a search that opened a tool could close it instead, and the highlight effect could not see a
nulltonulltransition, 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
InjectJsCallbackOwnershipTestgives - 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 madeblockAteither truncate a block or run it to EOF.docs/THREADING.mdgains the post and upstream-queue rules (#321) - tests:
GlobalSearchOrderingTestpins 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);CanonicalKeyNameTestis 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 racingadvanceTimeByagainst 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 > 1floor was considered and not taken:visitCountcounts calls toaddUrl, whose only caller is the browser plugin's title listener, so a visited page raises its own count with two assignments todocument.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