Commit Graph

4421 Commits

Author SHA1 Message Date
hermes-seaeye[bot] 16a408534c fmt(js): npm run fix on merge (#107255)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-10 09:55:33 +00:00
Teknium d5495e762c fix(desktop): messaging restart banner lives in the detail column; drop duplicate toast CTA
Live pass showed the banner spanning list + detail and the save toast carrying a
second 'Restart gateway' button beside the banner's 'Restart now'. Render the
banner inside DetailColumn (content width, above the platform header) and let the
toast stay a plain confirmation. Also hide the redundant 'Messaging gateway
stopped' setup pill when the state pill already says exactly that.
2026-09-10 02:48:21 -07:00
Teknium 52dea2c7d9 feat(desktop): Telegram QR quick setup + persistent restart banner on Messaging
Port the dashboard Channels flow into the Desktop Messaging page:

- Telegram "Quick setup": the Nous pairing service mints a bot; the page shows
  the QR / deep link, polls until Telegram confirms, pre-fills the detected
  owner id into the allowlist, and applies. The backend writes the token +
  allowlist into the scoped profile and restarts the gateway; the page watches
  the restart child's exit so a failed restart surfaces instead of a silent
  "gateway stopped".
- Restart-needed banner: every credential save / clear / enable toggle keeps a
  banner with "Restart now" up until a restart actually completes. Toasts
  vanish; the credential still only loads on the next gateway start.
- runGatewayRestart now resolves a boolean (never rejected before, so the
  Webhooks page's catch path was dead and its banner cleared on failure).
- New API helpers carry the page's profile scope on every pairing call so
  start/status/apply hit the same backend that holds the pairing in memory.
- i18n keys in all six locales; `qrcode` added to the desktop deps (already
  pinned identically in web/).
2026-09-10 02:48:21 -07:00
hermes-seaeye[bot] 5172f22df2 fmt(js): npm run fix on merge (#107244)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-10 09:37:09 +00:00
Teknium 61afcde8f9 refactor(desktop): one Plugins surface — Capabilities → Plugins owns agent + desktop plugins, install, and the catalog
Plugins were split across two pages that each showed half the picture:
Settings → Plugins listed desktop plugins plus "Install from Git" and a
pointer saying agent plugins live elsewhere; Capabilities → Plugins listed
agent plugins plus the catalog picker but knew nothing about desktop
plugins. A user asking "what extends my Hermes and where do I add more?"
had to visit both and still could not see the whole set in one place.

Capabilities → Plugins is now THE plugins page:

- Agent plugins section (scoped to the profile selector) with the
  "Install from Git" button in its header — installs target the scoped
  profile, not whichever one is active.
- Desktop plugins section beneath it (same for every profile), with the
  folder/rescan controls and the "agent half missing here" drift chip,
  whose repair also lands in the SCOPED profile.
- The catalog picker underneath, unchanged.

Settings → Plugins is removed. `/settings?tab=plugins[&plugin=…]` and the
existing `?tab=mcp` redirect share one table (`settings/moved-tabs.ts`) so
old bookmarks and palette links land on the same row on the new page.
Command palette: plugins moved from the Settings group to the Capabilities
group; installed-plugin rows deep-link to `/skills?tab=plugins&plugin=…`.
Dead `settings.plugins.agent.*` and `settings.nav.plugins` i18n keys dropped;
docs and in-code pointers say Capabilities → Plugins.
2026-09-10 02:30:50 -07:00
Teknium 2554a16a1d fix(desktop/settings): let searchable-select popovers size to content, not the shrink-wrapped trigger
The Timezone field (and the voice/model ComboboxInput) pinned the popover to
--radix-popover-trigger-width. In the settings grid the trigger shrink-wraps to
its current value (~90-120px), so every IANA row was clipped to "Africa/A…" and
the picker was unusable by eye even though selection itself worked. Use min-w so
the list keeps its own width and only grows to cover a wider trigger.

Live-verified on a sandboxed Electron instance over CDP: popover 123px -> 288px,
rows no longer overflow; click, type+Enter and autosave to config.yaml all land.
2026-09-10 01:04:59 -07:00
brooklyn! 6e07eb4838 docs(desktop): define panel titlebar and sticky backdrop behavior 2026-09-10 01:14:30 -05:00
brooklyn! e7c819a7e1 fix(desktop): mask text behind sticky user messages 2026-09-10 01:14:30 -05:00
brooklyn! bbe212de9b feat(desktop): keep panel tabs in the titlebar 2026-09-10 01:14:30 -05:00
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