Commit Graph

6944 Commits

Author SHA1 Message Date
teknium1
520f0e9ed5 feat(desktop): first-run shared-metrics consent and Settings › Privacy toggles 2026-09-28 12:43:03 -07:00
teknium1
b685eb90c0 feat: shared_metrics.status/set RPCs for Desktop consent
Desktop had no way to read or change the shared-metrics opt-ins. The new
profile-scoped RPCs read/write telemetry.shared_metrics.{enabled,send} in the
focused profile's config.yaml through the one config writer, keep the setup
wizard's invariant (send is forced off unless collection is on) and reconcile
the consent windows on every change via the wizard's own helper. first_run
answers also record the desktop setup-completed metric (lazy import until the
events module lands).
2026-09-28 12:43:03 -07:00
Gille
8127b377a2 fix(desktop): satisfy lint for refresh regression 2026-09-28 14:42:44 -05:00
Gille
37942a69ce fix(desktop): retain completed tool cards across transcript refresh 2026-09-28 14:42:44 -05:00
Brooklyn Nicholson
2de175e8a7 feat(desktop): put model-picker pricing behind a setting, off by default
Prices on every row are noise until you're comparing cost. Settings →
Appearance → General → Model Pricing turns them on; the choice persists.
2026-09-28 14:27:59 -05:00
Brooklyn Nicholson
a9d14d0ac1 feat(desktop): surface cached-read rates in the model catalog menu
Issue #63125 asks for the cached vs uncached read rate at a glance in
the model picker. The backend already ships it: _apply_pricing fills
ModelPricing.cache from input_cache_read, and the CLI picker renders it
as a third column — the catalog menu was the only surface dropping it.

- ModelPrice appends the cached rate (dim, `·$0.16`) next to the
  uncached input/output pair; its hover title names it, and the row's
  tooltip gains the cached-read line. Uncached read stays the inline
  `input` rate, so the cached-vs-uncached comparison reads at a glance.
- The hardcoded "free" label and English-only tooltip move to
  shell.modelMenu copy (free / cacheRead / priceTitle) so the row
  localizes; fr/de/es are completeness-checked, so all locales that
  carry the modelMenu block get the new keys.
- A collapsed `-fast` family now falls back to the fast sibling's price
  when the base id is unpriced (the sharing the comment already
  claimed — review point on #84831).

Refs #63125
2026-09-28 14:27:59 -05:00
Brooklyn Nicholson
81e28566c5 fix(desktop): harden catalog ModelPrice against partial pricing payloads
Review follow-ups on #84831:

- Treat null input/output as unknown: render nothing rather than
  "null/null" when a provider reports a non-free model without rates
  (_apply_pricing ships "" for unknown, but the wire type is
  string | null, so guard both).
- Only show the sale tag for a positive discount_percent, so a negative
  value can't render as a double-minus.
- Fallback title uses "—" for a missing half instead of "undefined".
- New test: no price span at all when the provider carries no pricing
  for the model.
2026-09-28 14:27:59 -05:00
Thiago Chu
67bc567657 feat(desktop): render per-model pricing in the model catalog menu
The model.options payload has carried per-model ModelPricing for providers
that support live pricing (Nous Portal included) since the picker requested
it, but the catalog menu never displayed it. Add a compact price label to
each model row: input/output $/Mtok, a green "free" label for free-tier
models, and a sale tag when the portal reports a discounted list price.

Verified against a live payload probe: the nous provider ships 35 pricing
entries (e.g. anthropic/claude-sonnet-5 -> $1.60/$8.00, 20% discount).
2026-09-28 14:27:59 -05:00
Brooklyn Nicholson
8423c4775c fix(desktop): make the full-changelog copy a small icon on the What's new row 2026-09-28 14:11:59 -05:00
Brooklyn Nicholson
6f2a01fce7 fix(desktop): complete locales and reuse CopyButton for full changelog copy
fr/de/es overlays are completeness-checked against the en key set, so the
new updates.copyFullLog key must ship there or catalog-completeness fails;
ar/ru carry the neighbouring moreChanges key, so they translate too.
Swap the hand-rolled copied-state button for the shared CopyButton
primitive (haptics, aria-label, error path, unmount cleanup included).
2026-09-28 14:11:59 -05:00
Weidows
542fd83d7b fix(desktop): address review feedback for full changelog copy
- Restore bounded -n 40 git log in readCommitLog (shallow-safe)
- Extract formatFullChangelogText to commit-changelog.ts for testability
- Fix breaking commit format: type(scope)!: not type!(scope):
- Add copyFullLog to ja.ts and zh-hant.ts
- Add unit tests for formatter (conventional, scoped, breaking, fallback)
2026-09-28 14:11:59 -05:00
Weidows
c395693c85 feat(desktop): add 'Copy full changelog' button to updates overlay
The update overlay previously showed at most 6 commit subjects from the
changelog, with no way to access the full list or copy it for translation.

Changes:
- Increase git log limit from 40 to all commits (remove -n 40)
- Add 'Copy full changelog' button that copies all commits as formatted
  text (type(scope): subject - author) to clipboard
- Use parseCommitHeader to reconstruct full convention commit format
  including scope and author
- Add i18n keys: en 'Copy full changelog', zh '复制完整更新日志'

Closes #48654
2026-09-28 14:11:59 -05:00
Brooklyn Nicholson
cb07391753 fix(desktop): drop SSH host refocus effect, assert custom input is typeable
The first-pick bug was the Select's controlled value already being the
Custom sentinel, which the value swap fixes on its own; the Input's
autoFocus handles focus once it mounts, so the rAF refocus effect is not
needed. Extend the regression test to type into the field and to check
that clearing it and blurring returns to the dropdown.
2026-09-28 14:00:18 -05:00
webtecnica
49c444eccd fix(desktop): focus SSH custom host input on first selection (#76825) 2026-09-28 14:00:18 -05:00
Brooklyn Nicholson
6d42313dee fix(desktop): a superseded preview action only releases its own controller (#125408)
The original handler's finally deleted the same-id replay's controller, so a
later cancel found nothing and the replay kept typing.
2026-09-28 12:53:29 -05:00
Brooklyn Nicholson
0e78b0144f fix(desktop): keep client-local system notices across refreshes (#126422)
The fallback-switch notice is never stored, and every refresh rebuilt from
stored rows, so it vanished on the next refresh.
2026-09-28 12:53:29 -05:00
Brooklyn Nicholson
aa51ba88a4 fix(desktop): stale-send tip reads every row a folded bubble owns (#125975)
Live settle addresses a folded tool turn by its final row, hydration by its
first, so after a tool-using turn the tips never matched and the first send
bounced with "Chat out of date" in a single window.
2026-09-28 12:53:29 -05:00
funky-xamarin
7f674c3e76 fix(desktop): retain workspace-only sidebar refreshes 2026-09-28 12:53:29 -05:00
Teknium
a5bd246865 Old pre-decomposition import paths are gone: plugin compat layer removed on schedule (#126164)
* refactor(plugins): remove the Sep 2026 decomposition compat layer on schedule

The PLUGIN-COMPAT layer (2776813df3 + d63e380324 + 0a5164cebe) kept pre-#102117 import paths
alive for external plugins until 2026-09-14. That window closed two weeks ago; since then the loader
has already been skipping plugins that use the old paths. This removes the layer itself:

- 328 appended `PLUGIN-COMPAT` blocks (lazy `__getattr__` pointer tables, re-exported third-party
  names, restored dead definitions) and the three re-export stub modules
  (gateway/startup_watchdog, hermes_cli/observability/relay_runtime, tools/environments/modal_utils)
- COMPAT_MANIFEST.md, compat_manifest.json, scripts/check_compat_pointers.py and its lint step
- the reporting surfaces: CLI banner notice, `hermes plugins compat`, the `hermes doctor` section,
  the post-update notice, the Desktop one-time dialog, the loader's pre-import skip and the
  `plugins.allow_deprecated_imports` escape hatch

An external plugin that still imports an old path now fails to load with its ImportError as the
reason in `hermes plugins list`, the same path as any broken plugin.

hermes_cli/plugin_compat.py stays as three inert stubs (compat_report, removal_in_effect,
summary_lines): an already-running pre-removal `hermes update` lazy-imports them after the checkout
swap (tests/compat/old_updater_surface.json).

In-tree fallout, both already dead: hermes_cli/setup.py::_check_espeak_ng (no callers; its
`shutil` came from a compat block) and gateway/config.py::SessionResetPolicy ("retained solely for
the scheduled plugin-compat window"). Two test_run_agent patches targeted the removed
`run_agent.handle_function_call` pointer; they now patch `model_tools.handle_function_call`, the
seam production reads, like every sibling test in that file.

* chore: retrigger CI (zero-job startup_failure phantom)

* test: drop resolution allowlist rows for the two deleted which() sites

hermes_cli/setup.py::_check_espeak_ng (dead) and tools/skillevaluator_scan.py::scanner_available
(a restored definition inside a PLUGIN-COMPAT block) no longer exist; the stale-row gate requires
their allowlist entries go with them.
2026-09-28 10:21:41 -07:00
Brooklyn Nicholson
fe18e71863 feat(desktop): localize the stale-aux dismiss action
staleAuxDismiss in all nine locales (fr/de/es are completeness
checked).

Co-authored-by: Konstantin Khlopkov <47825603+kokhlo@users.noreply.github.com>
2026-09-28 12:18:21 -05:00
Brooklyn Nicholson
ca3d6209d4 fix(desktop): readable stale-aux warning with a Don't-show-again escape
The banner was text-xs amber-200 — easy to miss in dark mode and near
illegible in light mode. It now uses the app's warn-badge contrast
pair (text-amber-600 dark:text-amber-300) at text-sm with a size-4
icon, stronger border/background, and medium weight, so it reads at
a glance in both themes. The persistent variant gains a dismiss button
that records the fingerprint acknowledgement; the post-switch notice
stays undismissable because it announces a change that just happened.

Co-authored-by: kyssta-exe <25470058+kyssta-exe@users.noreply.github.com>
Co-authored-by: Konstantin Khlopkov <47825603+kokhlo@users.noreply.github.com>
2026-09-28 12:18:21 -05:00
Brooklyn Nicholson
03bb1137f8 feat(desktop): fingerprint-based acknowledgement for the stale-aux banner
A dismissal is bound to the exact pin configuration it acknowledged —
main provider plus every slot's task/provider/model/base_url — stored
per profile. Any slot edit, main switch, or endpoint repoint changes
the fingerprint and re-arms the banner, so the silent-credit-burn
protection is never lost. base_url is normalized (trimmed, trailing
slashes stripped) so equivalent endpoints share a fingerprint, and
StaleAuxAssignment grows an optional base_url the persistent banner's
mapping now carries (switch echoes still omit it).

Co-authored-by: Konstantin Khlopkov <47825603+kokhlo@users.noreply.github.com>
2026-09-28 12:18:21 -05:00
Brooklyn Nicholson
e865b1f341 feat(desktop): let queued prompts expand to their full text
The queue panel showed each queued prompt as a single truncated line;
reading a long entry required loading it back into the composer. Previews
now wrap across two lines, and long or multiline entries get an in-place
expand/collapse toggle (aria-expanded chevron) that reveals a scrollable
full-text view. Edit/send/delete actions are unchanged.

Adds a QueuePanel test covering toggle presence (long/multiline only) and
the expand/collapse round-trip. Copy added in every locale that carries
the neighbouring queue keys.

Original work by YonganZhang in
https://github.com/NousResearch/hermes-agent/pull/46007, rebased onto the
current TypeScript sources (Codicon + iconSize tokens, steer/resume
actions preserved).

Co-authored-by: Yongan Zhang <374456248@qq.com>

Fixes https://github.com/NousResearch/hermes-agent/issues/45664
2026-09-28 12:17:34 -05:00
Brooklyn Nicholson
687c666ada fix(desktop): stop capping the composer model pill width
The composer model pill capped its width at max-w-40 (160px), so long
model names with session-state suffixes ("DeepSeek V4 Flash · Med")
truncated with an ellipsis even when the row had room. Drop the cap: the
pill sizes to its label, and the existing shrink + min-w-0 + truncate
squeeze path still yields width between collapse stages, so long names
only clip when the row is genuinely out of space.

Extends the model-pill test to lock the unbounded-width behaviour.

Original idea by @Aio777 in
https://github.com/NousResearch/hermes-agent/pull/49341 (raised the cap;
this removes it as requested by the issue).

Co-authored-by: aryan <alokesh1@sheffield.ac.uk>

Fixes https://github.com/NousResearch/hermes-agent/issues/49340
2026-09-28 12:16:41 -05:00
Brooklyn Nicholson
1b91b8eaa5 feat(desktop): search inside log tails with next/prev and severity marks
LogTail gains in-place search: matches are highlighted, the active one is
scrolled into view, and a "3/6" counter with up/down (Enter, Shift+Enter,
Cmd+G) steps through them, reusing the find bar's key handling and labels.
The Command Center system log and the MCP log pane share it, so there is one
log component instead of a Command Center fork.

Clearing a search re-engages follow-the-tail instead of leaving the pane
parked where the last match was.

WARNING/ERROR/CRITICAL lines get a thin gutter mark, read from the level
column of the log format, so an INFO line mentioning ERROR is not flagged.

Command Center sections use the same breadcrumb header as Settings.

Co-authored-by: Sonnenwerk <a.majuskel@gmail.com>
2026-09-28 11:53:06 -05:00
Brooklyn Nicholson
792e0e639b test(desktop): no-native-title covers RowButton
RowButton forwards every prop to a bare <button>, so title= on it is a
native tooltip too.
2026-09-28 11:50:16 -05:00
Brooklyn Nicholson
e96cbb0d39 fix(desktop): bots plugin uses Tip and Codicon over native title and glyphs
Avatar and chip rows move their hints into Tip; rows whose text or
aria-expanded already says it drop the title. The installed check is a
Codicon.
2026-09-28 11:50:16 -05:00
Brooklyn Nicholson
f573fe6f35 fix(desktop): billing credits use formatMoney 2026-09-28 11:50:16 -05:00
Brooklyn Nicholson
959a841fe2 fix(desktop): remote picker folder error uses FieldHint 2026-09-28 11:50:16 -05:00
Brooklyn Nicholson
c756986db2 fix(desktop): catalog counts use compactNumber and a Codicon star
Also drops cursor-pointer RowButton already sets, and Install from Git uses
textStrong instead of text plus underline.
2026-09-28 11:50:16 -05:00
Brooklyn Nicholson
770735b8a3 fix(desktop): keybind clear and reset are ghost icon Buttons
They were raw buttons with transition-all.
2026-09-28 11:50:16 -05:00
Brooklyn Nicholson
579ed78442 fix(desktop): layout picker uses selectableCardClass and control sizes
Cards share the settings card emphasis, the name field is Input size=sm,
Save current as is a text Button, and New grid layout keeps Button's height.
2026-09-28 11:50:16 -05:00
Brooklyn Nicholson
352faef967 fix(desktop): model menus use Badge, RowButton and Codicon spinning
The reasoning chips were a raw span re-rolling Badge, the add-custom row a
raw button, the refresh icon an animate-spin class, and Reset to defaults
re-set its own color.
2026-09-28 11:50:16 -05:00
Brooklyn Nicholson
36dcbd4650 fix(desktop): gateway and credential settings stop hand-sizing spinners
The list load uses Loader; Loader2 inside a Button keeps the Button's own
icon size.
2026-09-28 11:50:16 -05:00
Brooklyn Nicholson
f265273347 fix(desktop): skills and clarify loading render the Loader primitive
DESIGN.md rules out literal "Loading…" text and bare spinners for section
loading.
2026-09-28 11:50:16 -05:00
Brooklyn Nicholson
10dc619adc refactor(desktop): profile switchers share one gateway section and the session dot colors
Both switchers rendered the same gateway rows; they now use
FleetGatewayMenuGroup. Profile dots paint sessionDotClassName instead of a
copied palette.
2026-09-28 11:50:16 -05:00
Brooklyn Nicholson
6007b3ce4f refactor(desktop): export DialogCloseButton for the boot-failure overlay
The overlay carried a byte-for-byte copy of DialogContent's close button.
2026-09-28 11:50:16 -05:00
Brooklyn Nicholson
1b8569f41f feat(desktop): filter Messaging platforms by status
A status filter sits beside the Messaging search, with a tab only for tones
some platform is in (errors, needs attention, connected, inactive). Picking a
tab moves the detail pane into the filter; a platform that leaves the filter
because you just toggled it stays open.

Messaging and the gateway menu now read platform state through one
classifier, so the same bot can no longer show two different tones.

Co-authored-by: Sonnenwerk <a.majuskel@gmail.com>
2026-09-28 11:49:02 -05:00
finn763
1bb55780ab fix(desktop): collapse a redelivered interim into its bubble (#120104, #93926)
A message.interim redelivered after the first copy sealed the stream took the
standalone-append path and painted the same commentary twice. Refresh the
newest same-text assistant row of the current turn in place instead,
comparing whitespace-normalized text.

Supersedes #120145 and #93962.

Co-authored-by: Brammiebram <64777864+Brammiebram@users.noreply.github.com>
2026-09-28 11:31:46 -05:00
Abarajithan
675f29da03 fix(desktop): settle high-overlap sealed replies 2026-09-28 11:31:46 -05:00
Brooklyn Nicholson
34e09bff78 fix(desktop): namespace the project-row drop-hover attribute
Some checks failed
Deploy Site / deploy-vercel (push) Has been cancelled
Deploy Site / deploy-docs (push) Has been cancelled
auto-fix lint issues & formatting / Generate eslint --fix patch (push) Has been cancelled
auto-fix lint issues & formatting / Apply patch (push) Has been cancelled
Skills Index Freshness Check / check-freshness (push) Has been cancelled
Build Skills Index / build-index (push) Has been cancelled
Build Skills Index / trigger-deploy (push) Has been cancelled
'data-drop-hover' is a generic token any component could style; rename to
'data-session-drop-hover' to match the session-drag module that owns it
(same attr constant in session-drag.ts) so no foreign CSS can hit-test it.
2026-09-28 10:56:57 -05:00
Hermes Agent
fbfe3e29f7 test(desktop): cover the project-row drag-drop target
Moves to the dropped project row, and stays a no-op over Home and the
session's own current project — the same exclusions the row's existing
"Move to project" menu applies.
2026-09-28 10:56:57 -05:00
Hermes Agent
c4d14871e1 feat(desktop): drag a sidebar session onto a project row to move it
Extends the existing pointer-based session drag (session-drag.ts) with a
new resolvable target: a sidebar project row (tagged data-sessions-project
by ProjectOverviewRow). Dropping a session there calls the same
moveSessionToProject RPC the row's own "Move to project" context-menu item
already uses, so this is a faster path onto an existing capability rather
than a new one. Home and the session's current project are excluded as
targets, matching the context-menu's own filtering. The hovered project
row is highlighted imperatively (a data-drop-hover attribute + an
outline-sidebar-ring style) to keep the sidebar out of React re-render
churn during pointer travel.
2026-09-28 10:56:57 -05:00
Siddharth Balyan
e408d36339 fix(desktop): break the combo.ts <-> actions.ts import cycle (#126361)
2ffa4977ba made combo.ts import keybindActionAllowedInEditableTarget
from actions.ts, while actions.ts already imported IS_MAC from
combo.ts. When actions.ts is evaluated first, its KEYBIND_ACTIONS
table reads IS_MAC before combo.ts has initialized it. The dev
renderer throws "Cannot access 'IS_MAC' before initialization" and
#root stays empty.

actions.ts now reads the platform from the leaf module
@/lib/platform (isMacPlatform), which chords.ts and other keybind
code already use. actions.ts no longer imports combo.ts, so the
cycle is gone. The three defaults resolve at module load exactly as
before.
2026-09-28 15:00:41 +00:00
Siddharth Balyan
8f21606116 Hiding reasoning no longer hides tool rows, and tool_progress: off no longer hides reasoning (salvage #125948) (#126479) 2026-09-28 14:18:37 +00:00
Brooklyn Nicholson
7dbbb0f4a6 fix(desktop): report the current commit in updater status 2026-09-28 08:58:55 -05:00
alt-glitch
fc4dbe32df fix(logs): hermes logs --since/--level handle unstamped lines and MCP output
`hermes logs --since` and `--level` passed every line that had no leading
timestamp. A traceback's frames are written without one, so an old error
printed its frames without the header that was filtered out, and
`--component` dropped the frames of a matching record. `hermes logs` now
reads each unstamped line as part of the record above it: the line gets
that record's verdict for every filter, in the tail read and in `-f`.
Lines before the first stamp in the read window have an unknown time and
level, so they are dropped when `--since` or `--level` is set.

mcp-stderr.log had no parseable stamp at all: the banner started with
`=====` and server output was copied raw, so `hermes logs mcp --since`
printed the whole file. The stderr tee already reads each server's
stderr in a thread, so it now writes one line at a time, each prefixed
with the asctime-shaped local stamp the Python logs use. The banner
starts with the same stamp. The stamp comes from new public
`timestamp()`/`stamp_line()` in hermes_cli/stderr_timestamp.py, which
stays stdlib-only. The desktop MCP log view accepts both banner shapes.

A test now requires a real writer's sample line for every LOG_FILES
entry to parse with `_parse_line_timestamp`. The docs no longer say the
`timezone` key changes log timestamps; log lines use the machine's
local time.
2026-09-28 19:02:20 +05:30
alt-glitch
2d9d4abbef fix(desktop): desktop.log stamps lines in local time, like the Python logs
desktop.log stamped each line with an ISO-8601 UTC time in brackets
(`[2026-09-28T07:48:19.466Z]`), while agent.log, gui.log and errors.log use
Python's local-time asctime (`2026-09-28 13:18:46,062`). Reading one
incident across logs/ meant converting clocks by hand, and
`hermes logs desktop --since 1h` returned every desktop line, because its
timestamp regex expects the Python shape at the start of the line and
lines it cannot parse pass the filter.

formatLogStamp() now emits the Python shape in local time, and
formatDesktopLogLine() puts it first: `2026-09-28 13:18:46,062 [hermes] ...`.
The #84405 comment already said the stamp matched the Python convention;
now it does.
2026-09-28 19:02:20 +05:30
Brooklyn Nicholson
71cb7d60f8 fix(desktop): add model visibility recovery action
Edit Models gains a "Reset to defaults" action: it clears the persisted
visible/known model snapshots so models frozen hidden by the old
allowlist adoption come back, while custom models stay stored and shown.
Copy added in all supported locales.

Original work by @atomic-pancake-49 in
https://github.com/NousResearch/hermes-agent/pull/122055.

Fixes https://github.com/NousResearch/hermes-agent/issues/122053
2026-09-28 08:23:14 -05:00
teknium1
3df15fd727 fix(desktop): trim connection-scoped tile salvage to the tab-persistence invariants (#120106, salvage #121983)
Keep the (connectionId, profile) tile keying and its two invariant tests;
drop the post-switch remembered-route re-navigation (separate scope, hook
latch juggling) and the two extra delete-path tests.
2026-09-28 06:06:17 -07:00