Commit Graph

4412 Commits

Author SHA1 Message Date
hermes-seaeye[bot] cfdbbb6e35 fmt(js): npm run fix on merge (#107102)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-10 04:49:08 +00:00
hermes-seaeye[bot] 425b00174d fmt(js): npm run fix on merge (#107101)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-10 04:43:55 +00:00
hermes-seaeye[bot] 7cdc6a7338 fmt(js): npm run fix on merge (#107097)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-10 04:38:38 +00:00
brooklyn! 78052fe437 fix(desktop): keep notification IPC types independent of the renderer
The Electron composite project imported src/global.d.ts and followed its renderer-only dependencies. Share the notification payload from a dependency-free Electron module instead; all desktop typechecks and lint now pass.
2026-09-09 23:37:48 -05:00
brooklyn! 85571c16b6 fix(desktop): reveal the notification session without replacing its tab
Resolve missing runtime mappings through the shared mirror, dismiss covering routes, and preserve Bot tab ownership. Includes the mapping fix from https://github.com/NousResearch/hermes-agent/pull/100405 (b9e66c4a485dc353238fab8c7a07aa16107d11b7).

Co-authored-by: Nikita Simakov <everm1nd.mail@gmail.com>
2026-09-09 23:37:48 -05:00
brooklyn! 7107458d96 fix(desktop): route native notifications back to their source window
Capture durable focus identity before runtime bindings disappear, retain native handlers, and keep runtime approval actions and plugin callbacks on their source renderer.
2026-09-09 23:37:48 -05:00
Brian Sweatt f557829bff fix(desktop): retain notification handlers across banner dismissal
Adapted from https://github.com/NousResearch/hermes-agent/pull/65897 (a6fb6fe063e235c33b41212bd9cdaea4f9dcb1dc). Keep handlers across close, release on consumption or failure, and dismiss expired notifications before releasing them.

Co-authored-by: brooklyn! <brooklyn.bb.nicholson@gmail.com>
2026-09-09 23:37:48 -05:00
outdog-hwh a245d9aaa4 fix(desktop): resolve native notification action indexes
Adapted from https://github.com/NousResearch/hermes-agent/pull/51893 (a70b0991b2d72796479d63532ccf0fac15b17ba0).

Co-authored-by: brooklyn! <brooklyn.bb.nicholson@gmail.com>
2026-09-09 23:37:48 -05:00
brooklyn! c7676fc6b7 docs(desktop): explain radio artist lookups and privacy 2026-09-09 23:32:55 -05:00
brooklyn! 983f0c2870 feat(desktop): link radio credits directly to artist profiles 2026-09-09 23:32:55 -05:00
brooklyn! a6102b8d80 docs(desktop): document Radio controls and privacy 2026-09-09 22:57:27 -05:00
brooklyn! 3f53719c1d feat(desktop): ship Radio as an opt-in SDK plugin 2026-09-09 22:57:27 -05:00
hermes-seaeye[bot] 1675f1f2c2 fmt(js): npm run fix on merge (#107057)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-10 03:02:40 +00:00
brooklyn! b5c0a7ebe2 test(desktop): cover clarify submit shortcuts and input guards 2026-09-09 21:57:01 -05:00
brooklyn! ff7a1e8953 fix(desktop): restore Cmd/Ctrl+Enter in clarify forms 2026-09-09 21:57:01 -05:00
Austin Pickett b8d09d785d fix(desktop): exclude expired Windows trust roots with real-certificate coverage (#107014)
* fix(desktop): filter expired Windows CAs and deduplicate trust roots

* test(desktop): verify CA expiry and deduplication with real certificates

* test(desktop): cover Linux alongside macOS CA no-op policy

Co-authored-by: Gabriel Stoltemberg <215755014+Stoltemberg@users.noreply.github.com>

* chore: map CA trust contributors for release attribution

* style(desktop): match CA filtering to lint rules

---------

Co-authored-by: wliu-dev <249166551+wliu-dev@users.noreply.github.com>
Co-authored-by: Gabriel Stoltemberg <215755014+Stoltemberg@users.noreply.github.com>
2026-09-09 21:34:06 -04:00
hermes-seaeye[bot] 09183ae7bb fmt(js): npm run fix on merge (#107022)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-10 01:27:32 +00:00
brooklyn! a6474766c8 feat(desktop): show expanded recent sessions in project grouping 2026-09-09 20:22:22 -05:00
brooklyn! 32f3c908ff feat(desktop): limit recency previews at complete group boundaries 2026-09-09 20:22:22 -05:00
brooklyn! cdf4c76521 docs(desktop): specify artifact links as the last status rows 2026-09-09 19:57:56 -05:00
brooklyn! 8f319bf1a4 fix(desktop): keep artifact links below the composer queue 2026-09-09 19:57:56 -05:00
brooklyn! 643ead6f53 fix(desktop): keep settings search opaque under Glass
Use the existing opaque-surface contract on the search pill, which sits outside the raised card. Preserve the current theme tokens and palette handoff.

Co-authored-by: Moisés Valero <moisesvs84@gmail.com>
2026-09-09 19:50:47 -05:00
Austin Pickett fad7cfca42 fix(desktop): wait for session discovery without discarding queued prompts (#106986)
* fix(desktop): wait for session list before auto-draining queued prompts

Restored composer-queue entries were retrying as soon as the gateway
socket opened, burning the drain budget against reaped runtimes and
toasting "Queued message not sent" on every launch.

* test(desktop): cover composer-queue drain while sessions are still loading

* docs(desktop): clarify queue-drain discovery gates

* test(desktop): preserve hidden-session queues after failed drains

---------

Co-authored-by: xxxigm <tuancanhnguyen706@gmail.com>
2026-09-09 20:42:49 -04:00
brooklyn! 145c713f16 docs(desktop): define composer status disclosure and banner spacing 2026-09-09 19:12:10 -05:00
brooklyn! 5b181e511a fix(desktop): keep composer status groups collapsed except todos 2026-09-09 19:12:10 -05:00
hermes-seaeye[bot] 8e85b276f1 fmt(js): npm run fix on merge (#106967)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-09 23:36:18 +00:00
brooklyn! d47f40bb30 docs(desktop): define sidebar focus and chat dimming behavior 2026-09-09 18:34:57 -05:00
brooklyn! 2bc099dfea fix(desktop): keep chat focus when using the sessions sidebar 2026-09-09 18:34:57 -05:00
Teknium 97ca90f184 fix(desktop): expired OAuth grant shows a one-click 'Sign in again' instead of a retryable Provider error
A rejected OAuth token (HTTP 401 'User not found' from Nous Portal, Codex,
xAI…) reached the desktop error card as 'Provider error' with Retry as the
first action, which just replays the same dead credential.

Backend: nonretryable_client_error_result dropped failure_reason /
failure_retryable, so error_surface classified every non-retryable 4xx as a
retryable provider failure. It now stamps the classifier verdict like the
max-retries path, and auth-layer descriptors carry auth_kind
(oauth|api_key, derived from the provider catalog tab) + provider_label.

Desktop: an auth/oauth surface renders 'Authentication error', explains that
the <provider> sign-in expired/was revoked, and offers 'Sign in to <provider>
again' which launches that provider's existing onboarding OAuth flow scoped
to the failed session's gateway profile. Retry stays as the follow-up click.
Re-login to the provider already in use keeps the current model instead of
swapping in the recommended default.
2026-09-09 16:30:24 -07:00
hermes-seaeye[bot] 47104b9f17 fmt(js): npm run fix on merge (#106881)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-09 20:59:22 +00:00
Teknium 474143da81 fix(desktop): trim the auto Show-earlier gate and prove it end to end
Drop the injectable `topEdgePx` knob (one caller, one constant) so the
threshold has a single home, and fold the two-branch tail of
shouldAutoShowEarlier into one predicate. Comments keep the WHY: wheel is
the only signal at a clamped scrollTop 0, and the gates name the states
where scrollTop is near 0 without meaning "read earlier".

Tests: collapse the 11-case matrix into one invariant test on the pure
gate, and add a rendered-list test that mounts the real Thread against a
transcript heavier than RENDER_BUDGET, escapes the bottom lock, wheels up
at the clamped top and asserts more turn groups mount — while an upward
wheel mid-transcript mounts none. Red on origin/main (28 groups stay 28),
green with the fix.
2026-09-09 12:23:20 -07:00
KoNit-K 3ec8042483 fix(desktop): page earlier transcript on top-edge scroll
Long conversations and branched sessions hide older turns behind the
render budget, but only the Show earlier button loaded them. Auto-page
through the same showEarlier path when the reader is at the viewport top.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 12:23:20 -07:00
hermes-seaeye[bot] 15f3037647 fmt(js): npm run fix on merge (#106818)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-09 19:21:46 +00:00
Teknium 544169a41a perf(desktop): stop per-event full-timeline allocations on streamed tool events
Every tool.start / tool.progress / tool.complete ran three whole-timeline
passes that each allocated per part: `completeOpenStreamParts` (`map` with a
spread per row), the pending-row scan (`map` → `filter` → `map`, one wrapper
object per part), and `generatedImageEchoSources` (`flatMap` + a throwaway
array per non-image row) — then `dedupeGeneratedImageEchoesInParts` copied
the array again even when it stripped nothing, handing React a fresh
identity per event. Replace them with single in-place loops, and return the
input array from the dedupe when there are no echoes so a no-op stays a
no-op for the store.

Measured on the real `useMessageStream` hook (vitest/jsdom, 2,000 starts +
2,000 completions interleaved with text deltas): 618 ms → 138 ms; the
tool-only 2,000×2 case 252 ms → 67 ms. The remaining O(n) is one
`findIndex` per event over a ~4k array (~8 µs), well under the cost of the
copy React needs anyway, so no per-stream id index is introduced.

Behaviour is unchanged: same first-match `toolCallId` resolution, same
sparse/no-id contextual correlation, same ordering of parts.

Co-authored-by: Xipong <217837358+Xipong@users.noreply.github.com>
2026-09-09 12:13:14 -07:00
Xipong 7b639164cf fix(desktop): parallelize roster source requests 2026-09-09 12:13:14 -07:00
teknium1 a02a3b9808 test(desktop): trim MCP polling tests to two invariants per fix
Health checker: keep the 12-reconnect coalesce test and replace the
stop/disconnect drop rows with the invariant #106135 names — one fresh
follow-up still runs when the active sweep fails through the handled
config-error path (13 config reads on main, 2 after the fix). Log poller:
keep single-flight + resumed cadence, and shrink the replacement-lifecycle
test to "stop suppresses a late publish and leaves no timer". Both files
were run against origin/main source and failed (11 / 13 / 13 requests).
2026-09-09 12:11:25 -07:00
teknium1 8ac2c147a5 refactor(desktop): drop the serial-task layer from the MCP log poller
startCompletionPoll already keeps the pane single-flight and its stop
function already suppresses a late publish, so createSerialTask (a second
queue that never forwarded its AbortSignal to getLogs) and the AbortError
re-throw inside the poll callback were defense-in-depth on top of the fix.
A profile switch now tears the old lifecycle down and starts the new one
immediately; at most one stale read can still be in flight, and its result
is discarded. Replace the AbortController with a plain stopped flag.
2026-09-09 12:11:25 -07:00
Xipong d93988233c fix: serialize MCP log polling 2026-09-09 12:11:25 -07:00
Xipong 3fbc408ff6 fix: preserve event delivery across scheduler lifecycle boundaries 2026-09-09 12:11:25 -07:00
Xipong 42f2067092 fix(desktop): coalesce MCP reconnect sweeps 2026-09-09 12:11:25 -07:00
Teknium d31318745c test(desktop): trim salvaged fixtures to the invariant tests
Drop the two companion tests from #106124 and #106099 that already pass
on main without their fixes (a pinned active transcript is never measured
because prune() skips it before weighing; a rerendered effect already
discards the previous closure). Keep the one red-on-main invariant per
fix: no re-serialisation of unchanged warm transcripts, and change ticks
coalescing behind an in-flight session.active_list request.
2026-09-09 12:09:00 -07:00
Xipong 3d4c537584 fix(desktop): coalesce pending live status refreshes 2026-09-09 12:09:00 -07:00
Xipong 3bfd7b653b fix: cache warm transcript weights 2026-09-09 12:09:00 -07:00
Xipong 5a2ccffd41 fix(desktop): skip distro probe for WSL drive paths 2026-09-09 12:09:00 -07:00
Xipong ef677265d2 fix(desktop): reuse detected SSH platform 2026-09-09 12:09:00 -07:00
teknium1 fc36288832 fix(desktop): say "Auto-discovered" in the project row's accessible name
The salvaged glyph swap and hover tooltip are visual-only: the codicon is
aria-hidden and a Radix tooltip only speaks on hover/focus, so a screen
reader still announced an auto-discovered repo exactly like an explicit
project ("Open my-repo"). #106162 asks that the distinction reach
assistive tech and not rely on the glyph/color alone — the link's own
aria-label now appends the same "Auto-discovered" string for isAuto rows.

Also trims the salvaged tests from three to two invariants (explicit vs
auto), each asserting glyph, tooltip and accessible name together; the
"isAuto absent" case was a duplicate of the explicit case.

Refs #106162
Refs #73888
2026-09-09 10:58:43 -07:00
David Metcalfe 03b5460ad9 fix(desktop): distinguish auto-discovered repos from explicit projects in the sidebar overview
Auto-discovered repo lanes (Tier 2/3 in project_tree.py, tagged isAuto on
the tree node) rendered identically to explicit projects — both used the
folder-library lead glyph under the same PROJECTS header, so a user with
one real project could not tell it apart from repos Desktop found by
scanning disk.

The backend already emits isAuto; the frontend used it for sorting,
dismissal, and context-menu behavior but never surfaced it visually.
Auto nodes now render the repo glyph instead of folder-library, and the
row label carries an "Auto-discovered" tooltip. Explicit projects and
Home are unchanged. Adoption still works: theming an auto repo creates a
real projects.db row, the next tree refresh promotes it to an explicit
project (id p_<hex>, isAuto unset), and the auto cue disappears.

Closes #80383. Also addresses #73888 (same request, filed earlier).
2026-09-09 10:58:43 -07:00
Teknium c80003ff57 refactor(desktop): key the live-owner escape off the 4090 reason code, drop the prose sniffer
The gateway already ships the refusal as machine data: prompt.submit returns
JSON-RPC 4090 with error.data.reason = SESSION_NOT_OWNED. Matching the English
sentence in a new lib helper duplicated that contract and would silently miss a
reworded or localized message. submit.ts now classifies the rejection once
(isSessionNotOwnedError beside the sibling isSessionBusyError, reason code
first, prose only for pre-contract backends) and stamps the inline error bubble
with errorSurface {layer: gateway, code: SESSION_NOT_OWNED, retryable: false},
the same descriptor shape the gateway uses for turn failures. The error card
reads surface.code instead of re-sniffing text; retryable=false already hides
Retry through the existing gate.

Dropped from #106248: the stranded-resume overlay button. That overlay fires
when session.resume itself fails through every retry; reading is never fenced
by the lease, so the live-owner refusal never reaches it (the issue's screenshot
is the inline turn-error card). Also drops the resumeStartNewSession locale
keys and the separate detector module + its test; two invariant tests remain
(submit stamps the descriptor; the card offers Start new session / hides Retry).
2026-09-09 10:57:47 -07:00
KoNit-K 6efe3a45c1 fix(desktop): offer Start new session on live-owner exclusivity refusals
Retry alone is a dead end when another surface holds the session lease
(JSON-RPC 4090 / SESSION_NOT_OWNED). Surface an explicit new-session
escape on the turn-error card and the stranded-resume overlay without
weakening the lease guard.
2026-09-09 10:57:47 -07:00
teknium1 8d93081971 fix(desktop): stop flagging local/LAN auxiliary pins as stale
An aux task pinned to a private endpoint via `base_url` (a home Ollama
box at `byron.local`, a LAN IP, localhost) is the intended per-task
endpoint feature and can never bill a provider. The Settings → Model
banner still counted it as "still run on openai" forever and offered
"Reset all to main", which would wipe the working local setup; the
post-switch `stale_aux` report had the same blind spot; and the aux row
never showed the `base_url` the backend already sends, so the pin was
indistinguishable from a paid-provider pin.

- `GET /api/model/auxiliary` now stamps each task with `local_endpoint`,
  the verdict of the one canonical classifier
  (`agent/model_metadata.py::is_local_endpoint`) — no TS mirror of the
  private-range rules, so frontend and runtime cannot drift.
- Desktop: the persistent banner filter is the pure
  `staleAuxAssignments()` and skips `local_endpoint` pins; the pinned row
  appends ` · <base_url>` when one is set.
- `_stale_aux_pins` (post-switch report) skips local pins the same way.
- `is_local_endpoint`: `*.local` (RFC 6762 mDNS) now counts as local, and
  IPv6 literals no longer ride the "no dots ⇒ unqualified host" rule, so
  a global-scope address (`2607:f8b0::1`) is not local while `::1`,
  ULA and link-local still are via the `ipaddress` scope checks.

Slim redo of #106236 (@webtecnica) and #106234 (@huklaa), which fixed the
same symptom with a client-side classifier copy; the bug class, row
display and mDNS/IPv6 classifier corrections are theirs.

Refs #106228

Co-authored-by: Hukla <129692708+huklaa@users.noreply.github.com>
Co-authored-by: webtecnica <webtecnica@gmail.com>
2026-09-09 10:33:00 -07:00