Commit Graph

43645 Commits

Author SHA1 Message Date
brooklyn!
8b17394cc6 fix: abort reasoning-field length stops and surface close taskkill failures
finish_reason=length with empty visible content and a non-empty reasoning
or reasoning_content field uses the existing thinking-budget abort. No
model id is consulted. Empty content with no side channel still continues.

Desktop close/stop no longer discards Windows taskkill failures. After the
same tree-kill, owned PIDs are inventoried and only unheld gateway locks
are cleared.
2026-09-25 01:08:23 -05:00
Hermes Agent
fa58e4ab75 fix(desktop): let the boot-failure overlay be dismissed
The recovery modal rendered with no close control and ignored Escape, so a
latched boot error trapped the user. Add a Close button to both the recovery
card and the embedded Gateway settings view, and route Escape through the
same onOpenChange path. Dismissal hides the modal only: the boot error stays
latched, and the overlay comes back when the error changes, clears and
recurs, or a retry starts and fails again.

Co-authored-by: giggling-ginger <110955495+giggling-ginger@users.noreply.github.com>
2026-09-25 01:07:48 -05:00
brooklyn!
e726b79803 fix(desktop): stop macOS launch from showing two Dock icons
The setup launcher had no LSUIElement, and its already-installed hand-off
ran after Tauri/AppKit, whose default activation policy is Regular. That
registered the setup bundle as a second Dock app beside the real desktop.
Hand off before constructing Tauri, and restore Regular activation only
when the installer UI is actually shown.

On macOS the Electron single-instance lock also ran before deep-link
registration. setAsDefaultProtocolClient relaunches the app through Launch
Services, so the loser already had a Dock icon by the time the lock failed
and app.exit(0) ran. Register the protocol first. The lock-losing instance
still hard-exits before ready.

Fixes #73151
2026-09-25 01:01:42 -05:00
brooklyn!
08887d9f86 fix(desktop): sort the primary-boot route import
registryPrimaryBootRoute comes before resolveDesktopRemoteRoute.
2026-09-25 01:01:04 -05:00
brooklyn!
1beed32449 fix(desktop): keep primary-boot fixture overrides on the startup options type
The new select and attach mocks were spread in as unknown, so the
assertions that read them failed typecheck.
2026-09-25 01:01:04 -05:00
brooklyn!
9167ab8457 fix(desktop): type the primary-boot tile fixtures
The identity-guard test used a partial owner route and a partial connection, so the desktop typecheck rejected the file.
2026-09-25 01:01:04 -05:00
brooklyn!
cd2915ac6c fix(desktop): boot the registry primary after an update (#99395)
A Desktop whose registered primary is SSH could finish an update and spawn
a local backend. Remote session tiles then resumed against that local
backend and showed the durable-session retry copy.

After update clearance, select launchMode=primary before any local attach
or spawn. If the active backend identity is not the tile owner, unbind the
persisted tile with a wrong-backend error instead of that retry copy.
2026-09-25 01:01:04 -05:00
Brooklyn Nicholson
45c61101fa fix(desktop/settings): keep the searchable select trigger from shrink-wrapping
The settings action cell shrink-wraps to content, so with a blank value
the timezone SearchableSelect trigger collapsed to the 'Search…'
placeholder + chevron (~70px), and the popover — whose width floors at
the trigger's --radix-popover-trigger-width — inherited the squish
(#99751). Give the shared trigger a min-w-44 floor, the same
min-width-floor convention the model-settings selects already use; the
popover's IANA-truncation half was fixed on main earlier.

No live candidate PR (both were deleted); implements the issue's own
proposed floor, with a width-contract test (jsdom cannot compute
Tailwind layout, so the rendered class floor is the assertable unit).
2026-09-25 00:58:00 -05:00
Brooklyn Nicholson
13b9feea33 fix(desktop): correct Quick Entry shadow artifacts on macOS
The transparent, frameless Quick Entry window was created with
hasShadow: true. macOS derives a transparent window's native shadow
from its alpha content, but the boot HTML paints an opaque background
before the renderer forces transparency, so the OS caches a full-frame
shadow that renders as a stray grey rounded outline behind the card
(#99172). The other transparent overlays (pet, HUD) already run with
hasShadow: false; other platforms keep the native shadow.

Also fit the card's own CSS box-shadow inside the 12px transparent
padding budget (0 18px 48px clipped to a hard edge at the fixed window
bounds; 2+8 = 10px stays inside and fades cleanly).

Mirrors #83704.

Co-authored-by: Guangtong li <64474753+Marcus112-CS@users.noreply.github.com>
2026-09-25 00:58:00 -05:00
Brooklyn Nicholson
50e72e318e fix(desktop): restore archived sidebar rows from their action menu
The sidebar's Archived view reuses the shared row action menu, whose
archive item was built unconditionally (label Archive, icon archive,
always the archive path), so an already-archived row offered a no-op
re-Archive instead of a restore (#98813).

Pass the row's archived state through to the menu, flip the verb to
Unarchive (with the ArchiveOff glyph the Settings restore button
already uses), and add unarchiveSession to useSessionActions — the
restore path the Settings → Archived Chats flow already has: flip
setSessionArchived false with the row's owning profile, drop the
archived-view row, lift the eviction tombstone, and re-list through
restoreListedSession so messaging/cron rows land back in their own
slice. The wiring's onArchiveSession dispatches by state: an id in
$archivedSessions (or listed archived) restores, everything else
archives as before.

New copy: sidebar.row.unarchive + desktop.restored/unarchiveFailed in
all locales (en/ar/de/es/fr/ja/ru/zh/zh-hant).

Mirrors and extends #98823 (locales the PR missed, slice routing,
restore icon).

Co-authored-by: liuhao1024 <11816344+liuhao1024@users.noreply.github.com>
2026-09-25 00:58:00 -05:00
Brooklyn Nicholson
dffe2c346e fix(desktop): apply app color scheme to inline previews
The ::preview iframe's theme prelude carried tokens, font, and a
transparent background but no color-scheme, so the frame document fell
back to the UA default color-scheme:light — form controls, scrollbars,
the canvas beneath the transparent body, and prefers-color-scheme
inside the frame ignored the app's dark theme (#95814). A transparent
background alone does not fix this.

Inject :root{color-scheme:<app scheme>} as part of the prelude (first,
so a preview page's own color-scheme declaration still wins) and key
the framedDoc memo on the scheme so a theme switch rebuilds the srcdoc.

Mirrors #97463.

Co-authored-by: 墨綠BG <103036558+BlackishGreen33@users.noreply.github.com>
2026-09-25 00:58:00 -05:00
Hermes Agent
a78aa6927b test(desktop): give the relay test's plugin-sdk mock an atom
The cold-open mark lives in ./shared, which relay.ts imports since main's
relay drain change, so the mock must export atom like its neighbours.
2026-09-25 00:57:00 -05:00
Hermes Agent
761ad9639f fix(desktop): spin the bot row while a cold chat opens
Clicking a bot whose chat is not already a tab waits on source prep and
the registry open before anything changes, so on a cold backend the
click looked dead. openRosterBot now publishes $pendingBotOpen after the
fronted-tab miss, and the row shows a GlyphSpinner with aria-busy and a
translated "Opening chat…" label. The highlight still follows the chat
on screen. Every exit settles the mark behind a generation guard, and
bumpBotOpenGeneration clears it, so a group open, a return to Sessions,
or the next click drops it.

Closes #120277

Co-authored-by: finn763 <165816600+finn763@users.noreply.github.com>
Co-authored-by: Vaibhav Arora <varora1406@gmail.com>
2026-09-25 00:57:00 -05:00
Hermes Agent
8a0371f0f6 fix(desktop): portal dropdown submenus into the parent menu's container
A submenu always portaled to document.body at z-50, even when its parent
menu lived inside a dialog. In the Kanban task dialog the model catalog's
thinking-effort submenu landed under the modal overlay: blurred and not
clickable.

SubContent now reads the parent Content's resolved portal container from
context (dialog content, or an explicit portalContainer). When it has one
it steps up to z-(--z-modal-popover) and restores pointer-events, which
the modal parent menu sets to none for a submenu that registers first.
Outside a dialog nothing changes.

Co-authored-by: Chen Jin <Enough1122@users.noreply.github.com>
2026-09-25 00:55:55 -05:00
Hermes Agent
4050c91580 fix(desktop): track the pointer when dragging a nested section's sash
The workspace | right-rail seam resizes the rail's inner review zone,
but the drag preview wrote that zone's px as the flex-basis of the whole
section wrapper, so the rail snapped narrower mid-drag and jumped on
release. The preview now grows each fixed track's wrapper by the zone's
delta from its own pointerdown width, sizes the inner zone item to the
planned px, and restores both on release. This applies per track, so a
cascade that reaches a nested section past the seam partner previews
correctly too.

Co-authored-by: David Metcalfe <80915+DavidMetcalfe@users.noreply.github.com>
2026-09-25 00:55:08 -05:00
Hermes Agent
fe3c6837be fix(desktop): gate the session.info branch write on the selected session
0401e08884 made the session.info cwd write require the event to describe
the selected stored session, but the branch write a few lines below
stayed unconditional. A background Kanban worker's runtime update that
reached the pane's active-runtime path could still flip the composer's
coding-rail branch to the worker's PR worktree while the default chat
stayed selected. cwd and branch now share one identity check.

Co-authored-by: ClintonEmok <54935030+ClintonEmok@users.noreply.github.com>
2026-09-25 00:54:59 -05:00
Hermes Agent
0e31fd3b45 test(desktop): type the session-timer element props instead of casting unknown 2026-09-25 00:54:21 -05:00
Hermes Agent
490037b87d fix(desktop): statusbar timer shows focus-since on tiles and says so 2026-09-25 00:54:21 -05:00
ethernet
716cfe9d09 Merge pull request #122322 from NousResearch/fix/venv-ownership
fix(pm): a dev checkout's update no longer rewrites the Desktop install's venv
2026-09-25 01:54:13 -04:00
Hermes Agent
b200d3a690 fix(desktop): gate the HUD resize frame's hit test on engagement (#108793)
The resize frame was unconditionally pointer-events: auto, so a
collapsed HUD kept an invisible ring of window — nothing painted,
every click and drag aimed at the app underneath eaten. The frame now
rides the band's engagement gates: the caret in the composer
(data-hud-typing, stamped live by useHudGlass), a held band, or a
solid-input host re-arm it; an in-flight drag keeps its own handle
live via data-hud-grabbing (pointer capture routes the moves).

Direction from #108796 (liuhao1024); behavioral test instead of that
PR's source-reading one.

Co-authored-by: liuhao1024 <noreply@github.com>
2026-09-25 00:53:16 -05:00
Hermes Agent
df293160ce fix(desktop): preserve tile widths across reload via hydration suspend + partner-validated recall (#108679)
Boot-time pruning recorded share transitions while panes were still
hydrating, and recalled seam weights didn't validate the partner pane,
so a reload could rewrite tile widths. beginLayoutHydration gates
contributed-pane pruning until hydration ends; paneSharePartners.v1
records the seam partner and recalledEdgeWeights falls back to even on
partner mismatch; parseTileList drops self-anchored entries.
2026-09-25 00:53:16 -05:00
Hermes Agent
b401fb2e49 fix(desktop): clear PrimaryProfilePin on current-owner exit/error and make one launch-profile decision per start (#108417)
Salvages the pin-clear half of PR #109059 (salch-cred) and goes further:

- The current child's 'error' and 'exit' handlers now clear the pin
  after the clearForCurrentProcess guard, so an unexpected exit cannot
  leave routing pinned to a dead backend while the respawn reads the
  new --profile. A stale exit (older child) still returns before the
  clear and never touches a newer primary's pin.
- The connection IIFE's catch clears the pin behind the attempt guard:
  a failed startup releases its routing identity; a superseded
  attempt's failure never clears the newer attempt's pin.
- resolveLaunchProfile(readPreference) makes the launch decision ONE
  read per attempt: startHermes() now derives routingProfile (pin,
  setActiveGatewayProfile, remote resolve, child env identity) and
  argvProfile (the --profile flag) from the same decision, instead of
  pinning primaryProfileKey() up top and re-reading
  readActiveDesktopProfile() deep inside the IIFE — the split that let
  a mid-startup hermes:profile:remember produce 'routing alpha,
  --profile beta'. Unset preference keeps the legacy flag-less launch.
- #108118's invariant is preserved: a live primary still answers
  primaryProfileKey() from the pin while a preference change lands, so
  no duplicate backend spawns mid-life.
2026-09-25 00:53:16 -05:00
Hermes Agent
cc6bfebfab fix(desktop): tear down a HUD whose renderer dies before first paint (#108230)
A window born show:false is revealed only by success-shaped events
(ready-to-show, did-finish-load). When the HUD's main frame fails to
load or its render process dies before first paint, neither fires, the
4s fallback is never even scheduled, and the transparent window stays
hidden forever while broadcastHudState(true) keeps every toggle reading
open. wireWindowReveal now grows a failure branch (onRevealFailed) that
fires exactly once for a main-frame did-fail-load or a pre-reveal
render-process-gone, cancels a pending fallback, and disarms the reveal.
The HUD passes a handler that tears the window down through the bounded
requestHudClose, so the existing 'closed' handler owns the one teardown
path (snap shortcut, main-window restore, broadcastHudState(false)) and
the toggles converge to closed. The log-only post-reveal lifecycle
(#81290) is untouched: a crash after a successful reveal is still
diagnosable, not resurrected.

wireWindowReveal moves from main.ts into window-reveal.ts (same
signature, event wiring now unit-testable against fake emitters).
2026-09-25 00:53:16 -05:00
Yida Weng
e8c1ce8df2 fix(desktop): deliver file-watch events to owning window (#108189)
Preview/plugin fs.watch change events were always sent to mainWindow, so
secondary windows never live-reloaded. Capture event.sender, store the owner
WebContents on each watch, and deliver hermes:preview-file-changed there
(tearing the watch down if the owner is gone).
2026-09-25 00:53:16 -05:00
brooklyn!
60329f766f fix(desktop): auto-reload a live window whose renderer is killed (#122255)
* fix(desktop): reload live windows when renderer is killed

External SIGTERM/OOM watchdogs report reason=killed with isDestroyed=false,
leaving a white window because only crashed/oom were recoverable. Treat
live-window killed as recoverable; user close still exits early via
isDestroyed expected-teardown.

Closes #85048

* fix(desktop): end a killed-renderer reload loop on the recovery page, skip it on quit

Live-window 'killed' now reloads under the shared crash-loop budget. Once
that budget is spent the window hands off to onRendererTerminated so it
lands on the #116472 recovery page instead of a dead window, and a quit or
update handoff (which can kill renderers before their windows report
destroyed) is treated as expected teardown for every reloading window.

---------

Co-authored-by: doresa0 <doresa0@users.noreply.github.com>
Co-authored-by: Hermes Agent <agent@hermes.local>
2026-09-25 00:51:27 -05:00
brooklyn!
d45d6f0eb9 fix(desktop): confirm before switching to This device (#122046)
* fix(desktop): confirm before switching to This device

The at-rest This device pill used the same home glyph as a gateway's
default profile, and clicking it went straight into selectConnection,
which can start a local install or silently replace the center with a
fresh session.

- Give the local default a device-desktop glyph and a label that says
  what the click does, on the rail pill and both condensed dropdowns.
- Add a probe-only hermes:local-backend:probe IPC (resolveHermesBackend,
  no ensureRuntime) and a useLocalDeviceSwitch dialog: Install locally /
  Connect to existing instead / Cancel when bootstrap is needed, or a
  Switch / Cancel fresh-session cue when Hermes is already installed.
- Wire it into ProfileRail, ProfileDropdown, the statusbar
  ProfileSwitcher and ConnectionSwitcher.

Fixes #102826

Co-authored-by: chelsealong <chelsealong@126.com>

* fix(desktop): retain profile rail status indicators

* fix(desktop): preserve local device confirmation in rail

---------

Co-authored-by: Hermes Agent <agent@hermes.local>
Co-authored-by: chelsealong <chelsealong@126.com>
2026-09-25 00:49:20 -05:00
brooklyn!
31427d7f4b fix(desktop): preflight provider before new bot intro (#122066)
* fix(desktop): preflight provider before new bot intro

* test(desktop): fix remote provider mock signature

---------

Co-authored-by: Hermes Agent <agent@hermes.local>
2026-09-25 00:48:53 -05:00
brooklyn!
e417a096fd fix(desktop): keep continuous voice working after a mic meter AudioContext error (#122278)
* fix(desktop): wait for the last mic meter to close and report a dead one

The voice recorder's level meter fired AudioContext.close() without awaiting
it and opened a new context on every take, so back-to-back takes (and the
barge-in monitor) could hold two capture contexts at once. That is what makes
Chromium raise "The AudioContext encountered an error from the audio device",
and the error was never handled: the analyser went flat and the take read as
silence.

Meter closes now go through mic-meter-context, and both the recorder and the
barge monitor wait for pending closes (capped at 1s) before opening a new
context. The recorder watches its context for 'error' and an unexpected
'closed', treats a context it can't build as failed, calls
onMeterFailure, and marks the take meterFailed.

* fix(desktop): transcribe voice turns whose level meter died

Continuous voice dropped any take whose meter-only heardSpeech stayed false,
so once the AudioContext errored every later utterance was discarded before
STT with no error shown. A meter failure now ends the turn, and the take goes
to STT unless it is under 750ms (STT decides whether there was speech).
Back-to-back meter failures show "Microphone failed" and end the conversation
instead of re-arming forever.

Fixes #75329

---------

Co-authored-by: Hermes Agent <agent@hermes.local>
2026-09-25 00:48:22 -05:00
brooklyn!
c1ab579cca fix(desktop): stop clipping attachment chips under the sticky user bubble (#122034)
The attachment row under a sent user message had -mt-3, which pulled it
up into the sticky prompt's box. The sticky-prompt clip then cut off the
top of folder/file chips and image thumbnails, even with the thread at
rest. Drop the negative margin so the row starts below the bubble.

Fixes #78847

Co-authored-by: Hermes Agent <agent@hermes.local>
Co-authored-by: Sami Rusani <samgithub@pm.me>
2026-09-25 00:45:18 -05:00
Hermes Agent
4869d3812c fix(desktop): leave the entered project when switching profile or backend
A project id names a row in one backend's projects.db, but $projectScope
survived profile and connection switches. The fresh draft resolved its cwd
from the stale scope against the still-loaded old project tree, so a new
chat in profile B started in profile A's project folder.

Fixes #54990

Co-authored-by: Robin Rademacher <robinradx@gmail.com>
2026-09-25 00:38:15 -05:00
Hermes Agent
953ca9e094 refactor(desktop): move project scope into a dependency-light store module
Profile and gateway-switch stores need to reset the scope synchronously;
importing store/projects from them would close an import cycle.
2026-09-25 00:38:15 -05:00
ethernet
c16e8037d2 Merge pull request #122312 from NousResearch/ethie/no-system-python-fallback
fix(desktop): never resolve a source root's interpreter off PATH
2026-09-25 01:34:25 -04:00
ethernet
268820136f fix(update): repair an install whose venv runs another checkout
An install's in-tree venv whose editable record names another checkout
(what project_venv_dir used to cause) turns <install>/venv/bin/hermes
into that checkout's CLI. Every update Desktop hands to the launcher
then pulls the other tree: the install never moves, the hand-off
reports success, and posix.sh swaps the install's stale release/ build
back over the app. Pressing Update can never get out of that loop.

`hermes update` now notices it is running on another checkout's in-tree
venv and re-runs itself with that install's own code (PYTHONPATH=<install>,
cwd=<install>). The install's updater pulls the install and reinstalls it
into its venv, which points the editable record home again, so the next
relaunch runs a fresh build. A wrapper that put the running checkout on
PYTHONPATH on purpose chose that tree and is left alone.
2026-09-25 01:21:28 -04:00
ethernet
f9f235ed1a fix(pm): never adopt another install's venv for a checkout
project_venv_dir() fell back to the running interpreter's venv whenever
hermes_constants was loaded from the checkout. Where the code was loaded
from says nothing about who owns the interpreter: with
`PYTHONPATH=<dev checkout> <app install>/venv/bin/python -m hermes_cli.main`
(a shell wrapper around a dev tree), PROJECT_ROOT is the dev checkout but
the venv is the Desktop install's. base_venv() then returned the app's
venv, `hermes update` synced the dev tree into it, and the Desktop
install's venv became an editable install of the dev checkout. From then
on the Desktop shell tracked ~/.hermes/hermes-agent while its backend and
its handed-off `hermes update` ran and pulled the dev tree, so every
in-app update "succeeded" without moving the install. The same misread
let running_from_selected_environment() accept lazy extras into that
venv.

Only fall back to the running venv when its own hermes-agent install
records this checkout in direct_url.json, which every install of a
checkout into a venv writes (installers, uv sync). Otherwise the
checkout gets its own environment, as it did before 4f6c04cd07. The
#116148 out-of-tree layout (a venv installed from the checkout) still
resolves to the running interpreter.
2026-09-25 01:17:12 -04:00
Hermes Agent
69948c0057 fix(desktop): log what ssh did when an SSH connect fails
SshConnection.open() handed the classified error to its caller and
logged nothing about the failure. desktop.log showed only "connecting"
and then "connection closed", and the renderer shows only friendly copy
for the error kind. A connect that dies right after TCP setup (#80836)
therefore left no exit code, close signal or stderr anywhere. Log them,
redacted, for both the mux and no-mux connect paths.
2026-09-25 00:02:48 -05:00
ethernet
268e1055e8 fix(desktop): never resolve a source root's interpreter off PATH
findPythonForRoot ended in findSystemPython(), so a checkout with no
in-tree venv/.venv produced a PATH Python. Two designed refusals were
therefore unreachable: readSourceUpdate's `!managed && !probe.python`
guard (checkout-source.ts) and StateDbPreflight's `string | null` python
plus its "Python not found" throw (state-db-preflight.ts). Both were
written expecting null and could never see it.

A PATH Python can import a checkout while lacking its selected
dependencies, which is the failure the checkout-source comment already
names, so a failed read became a wrong answer instead of a refusal:
the update probe, the state.db pre-flight and the source backend all
ran under an interpreter nothing selected. PM deletes the in-tree
venv/.venv once a generation is committed, making null the ordinary
answer for a managed install — its callers resolve the installation
launcher instead, and the backend ladder falls through to its next rung.

resolveSourcePython owns the decision (override, then the checkout's own
venv, else null) and findPythonForRoot keeps its signature, so the four
call sites are unchanged. findSystemPython stays for the uninstaller,
whose interpreter choice is a separate, deliberate one for a locked venv.
2026-09-25 01:01:45 -04:00
Hermes Agent
99a161531c fix(desktop): decide image bytes vs path from the session owner's own backend
isSessionRemote only trusted route.mode. A connection-tagged session row
(the unified Sessions list) and a bare-profile owner carry no mode, so the
check fell back to the window's ambient connection. A local-ambient window
then sent a client path via image.attach to a remote backend that can't
read it ("image not found: /Users/...").

Resolve the owner's mode in this order: route.mode, the registry
connection's kind, then the mode of the socket already dialed for that
owner (the primary for its profile, or the owner's own secondary). Fall
back to ambient only when none of those is known. registryConnectionKind
moves into connection-registry-state so profile.ts and session-states.ts
share one lookup.
2026-09-24 23:54:30 -05:00
Hermes Agent
c19a35d8c9 fix(desktop): keep pip downloads and cache files out of Artifacts
021950ac81 stopped indexing arbitrary dotted paths, but terminal output
is still scanned for bare URLs and looksLikeArtifact accepts any
http(s) value. Installing from a PyPI mirror logs every download with
its full URL (the .whl, its .whl.metadata, sdists under /packages/),
so each one landed in the Artifacts menu; sdists pip reports under its
cache dir (AppData\Local\pip\Cache, ~/.cache/pip, Library/Caches/pip)
were indexed too. Heuristic candidates that are wheels, package-index
dist downloads, or files under pip's cache are now dropped; explicit
MEDIA deliveries are unaffected.

Co-authored-by: yungchentang <46495124+yungchentang@users.noreply.github.com>
2026-09-24 23:51:53 -05:00
Hermes Agent
f216e44643 fix(desktop): list a compressed project chat once (#77591)
Compression rotates a chat's live id while the project snapshot still holds
the older segment. The live overlay matched rows by id only, so the tip was
added next to the snapshot row (a ghost duplicate until the next tree
refresh), and the owner map missed the tip, letting an umbrella project claim
it by cwd.

Match rows and owners by every id the conversation has had (id, lineage root,
lineage chain) in the lane overlay, the overview previews and the drill-in
preview backfill.
2026-09-24 23:49:09 -05:00
Hermes Agent
f9f1413f4e fix(desktop): stop an entered project refetching on every tree refresh (#77591)
Every sessions.changed and window focus refreshes projects.tree, and each
refresh handed the sidebar a new array. The entered project keyed its
projects.project_sessions refetch on that array, so every refresh paid for a
full hydrated tree build on the backend (seconds over a remote gateway) and
rebuilt the lanes.

The tree now keeps unchanged nodes by reference, the drill-in refetches only
when its own overview node changes, an unchanged answer keeps its reference,
and a background refetch no longer reports loading.
2026-09-24 23:49:09 -05:00
ethernet
138e33d51f Merge pull request #122244 from NousResearch/fix/restore-pm-merge-drops-current
Restore behavior lost during PM integration
2026-09-25 00:36:50 -04:00
ethernet
067934a106 Merge pull request #122093 from benbarclay/fix/pm-agent-browser-exec-bit
fix(pm): make the staged agent-browser binary executable
2026-09-25 00:35:56 -04:00
ethernet
502dd8d54c Merge pull request #122288 from NousResearch/fix/gateway-start-no-prompt-on-captured-stdout
fix(gateway): don't ask the Windows login question when stdout is captured
2026-09-25 00:34:57 -04:00
ethernet
af3299a22c fix(gateway): don't ask the Windows login question when stdout is captured
#122234 gave Desktop update steps NUL stdin, but the hand-off script
that runs is the one from the checkout being updated FROM. Every update
that starts on an older commit still runs the old script, which gives
steps the hand-off console as stdin and captures their stdout until
they exit. Only the steps after `hermes update` run new code.

So `gateway start --all` from the new checkout still saw an interactive
console, asked "Install it now so the gateway starts on login?" into
the captured stdout, and waited forever. The update never relaunched.

start() now asks only when stdout is a terminal too. Nobody can answer
a question they cannot see.
2026-09-25 00:33:42 -04:00
Hermes Agent
da931c9ea8 fix(desktop): ignore auto-repeat and post-focus Ctrl+W/Ctrl+R
Holding W through a browser's own Ctrl+W keeps sending auto-repeat keyDowns
once Hermes inherits focus, and each one closed a tab; a keydown synthesized
on window activation could do the same. Close Tab and Reload now swallow
auto-repeats and anything within 200ms of the window gaining focus. They are
still claimed so the renderer's mod+w keybind can't act on them. Zoom keeps
repeating.

Fixes #105498

Co-authored-by: kokhlo <konstantin.khlopkov93@gmail.com>
2026-09-24 23:19:36 -05:00
ethernet
2ef41d2b58 Merge pull request #122103 from NousResearch/ethie/pm-evict-incompatible-plugins
fix(pm): hermes update disables plugins that no longer fit instead of failing
2026-09-25 00:08:10 -04:00
ethernet
e71f4dd4ac Merge pull request #122221 from NousResearch/fix/anthropic-lazy-install
Opt-in provider SDKs (anthropic, bedrock) install and swap into the running process on first use
2026-09-25 00:05:51 -04:00
ethernet
a75d8b420e Merge pull request #122234 from NousResearch/fix/handoff-noninteractive-steps
fix(desktop-update): Windows update steps get NUL stdin; installer asks gateway questions once
2026-09-25 00:05:41 -04:00
ethernet
6834b4c634 Merge pull request #122208 from NousResearch/fix/source-version-release-tags
fix(update): source installs report the current release after updating
2026-09-25 00:04:38 -04:00
ethernet
4c89dbf880 docs(termux): mark the Termux package as currently broken
The APT package does not work at the moment and a fix is in progress.
Warn at the top of the install guide so users do not burn time on steps
that will fail.
2026-09-25 00:04:26 -04:00