Commit graph

1868 commits

Author SHA1 Message Date
hermes-seaeye[bot]
66e20786d0
fmt(js): npm run fix on merge (#73666)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-28 21:55:38 +00:00
Teknium
03af12c5c9 style(desktop): blank line between node: and vitest import groups
perfectionist/sort-imports requires spacing between builtin and
external import groups.
2026-07-28 14:47:36 -07:00
Teknium
20e0b26c4e test(desktop): run venv-blocker-scan tests under vitest
CI's electron vitest project collects electron/**/*.test.ts; the
node:test-based suite reported 'No test suite found'. Import
describe/it from vitest like sibling electron tests.
2026-07-28 14:47:36 -07:00
Teknium
1dd1c449bb fix(desktop): run venv-blocker scan async off the main-process event loop
The preflight's execFileSync froze the Electron UI event loop for up to
15s while psutil scanned the full process table. Convert
scanVenvBlockers to async execFile and await it in applyUpdates; tests
updated to async DI stubs. Also adds trailing newlines.
2026-07-28 14:47:36 -07:00
atakan g
d210500b54 fix(desktop): surface external venv update blockers 2026-07-28 14:47:36 -07:00
Siddharth Balyan
3cedac00b7
fix(credits): reintroduce grant_spent, gated on an in-session crossing (#73634)
* Revert "fix(credits): remove the 'Grant spent · $X top-up left' notice"

This reverts commit 5dc6a14c14.

* fix(credits): reintroduce grant_spent behind an in-session crossing gate

The removed notice nagged because its condition is a steady state for
accounts living on top-up, the latch is per-session, and the cold-start seed
runs the policy at every session open — every session re-announced
'Grant spent · $X top-up left'. Reintroduce it gated so only a session that
WATCHES the grant run out announces:

- seen_grant_unspent crossing gate, mirroring seen_below_90: opens when the
  session observes the grant meaningfully unspent (>= GRANT_UNSPENT_MIN_MICROS,
  1 cent — portal-seeded states derive micros from float dollars and can carry
  sub-cent residue where headers report exactly spent). Seeds never prime it.
- The gate guards only the show branch and is consumed by the announcement —
  one announcement per crossing. Header flicker (used_fraction None and back)
  clears the sticky line but cannot re-announce; a renewal re-opens the gate.
- new_credits_latch() centralizes the latch shape (agent build, lazy re-init,
  and the policy test helper all build through it).
- Tests: steady-state-open stays silent (seed + policy seams), live crossing
  announces once, flicker/renewal/residual cases locked; the rendering test
  primes the gate and asserts its leg count so a gate regression cannot
  silently shrink coverage.
2026-07-28 21:36:23 +00:00
atakan g
fa9217542d fix(desktop): preserve local model provider endpoint 2026-07-28 14:17:27 -07:00
brooklyn!
c8cdeb435f
fix(desktop): allow renderer camera capture (#73558)
* fix(desktop): allow camera capture through the permission handlers

The session permission hooks were written for the voice composer and denied
video outright, so any renderer getUserMedia({video}) failed with
NotAllowedError before the OS was ever consulted.

Rename isAudioCapturePermission to isMediaCapturePermission and accept video
alongside audio in both the request and check handlers. The OS capture
permission still applies, so the user keeps a real allow/deny.

* build(desktop): declare camera usage for signed macOS builds

A hardened-runtime build needs the camera entitlement and an
NSCameraUsageDescription string, or the packaged app is killed on first
camera access instead of prompting.
2026-07-28 14:50:52 -05:00
atakan g
89490ae373 fix(desktop): honor gitignore across Windows path casing 2026-07-28 12:36:35 -07:00
Atakan
15317e1fd7 fix(desktop): upload Windows attachments to Linux backends 2026-07-28 12:36:01 -07:00
atakan g
d4221b2736 fix(desktop): validate WebSocket token before backend ready 2026-07-28 12:35:57 -07:00
Teknium
43e1b7f474 style: eslint padding fix in profile-scope test (salvage follow-up) 2026-07-28 11:58:03 -07:00
Teknium
e27997d63f fix(desktop): route streaming TTS through the active profile backend
The /api/audio/speak-stream WebSocket URL was always minted for the primary
backend with no profile param, so streaming read-aloud used the default
profile's TTS provider even when another profile was active. Mint the
ticket for the active profile's connection and carry ?profile= (same seam
as /api/pty), via a read-only getApiRequestProfile() accessor.
2026-07-28 11:58:03 -07:00
Tom Brautlacht
c95f04501d fix(desktop): skip markdown tables in speech output 2026-07-28 11:58:03 -07:00
HarpStar
f02d41cb21 fix(desktop): summarize code blocks for read aloud 2026-07-28 11:58:03 -07:00
webtecnica
7c398a5c8e fix(desktop): prevent voice playback from auto-starting next sentence after stop (#70955) 2026-07-28 11:58:03 -07:00
Max Hsu
181511473e fix(desktop): render video/audio image-markdown as media, not broken <img>
Generated media often arrives as image markdown (![clip](clip.mp4)). The chat
markdown renderer maps the img element to MarkdownImage, which renders a raw
<img>; for a video/audio source the browser cannot paint it and shows a
broken-image icon even though the file is valid and plays fine. Images worked
only because <img src=...png> is valid markup.

Route video/audio sources in MarkdownImage to the existing MediaAttachment,
which already picks the correct <video>/<audio> element (streaming protocol +
open-externally fallback) by media kind. The intended MEDIA:-tag -> #media:
link path is unaffected; this only fixes the bare image-markdown case.

Since #57944 the image path is built on hooks (async src resolution, failure
and loading states), so the routing check cannot be a plain early return
inside it -- it would have to sit after every hook call and would still fire
an image resolve for media never rendered as an image. MarkdownImage is
therefore a thin hookless router in front of MarkdownImageContent, which is
the previous body unchanged.

The regression tests join the existing markdown-text.media.test.tsx added by
#57944 rather than replacing it.

Fixes #40896

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018vU45SEvcxLkVeyyEE9gJ2
2026-07-28 11:58:03 -07:00
Ares4Tech
3e47efeb25 fix(desktop): stream remote media through gateway 2026-07-28 11:58:03 -07:00
Bryan Roller
3a1efa5e30 [verified] fix(desktop): scope TTS requests to active profile 2026-07-28 11:58:03 -07:00
Bartok9
a2b0c314ae fix(desktop): scope audio endpoints to the active profile
transcribeAudio/speakText/getElevenLabsVoices called the backend without
profileScoped(), so for a non-default profile they hit the default backend
config instead of the active profile - playback used the wrong TTS/voice
even though settings were saved to the active profile config.

Fixes #53441
2026-07-28 11:58:03 -07:00
Brooklyn Nicholson
eaf30a4de7 build(desktop): declare camera usage for signed macOS builds
A hardened-runtime build needs the camera entitlement and an
NSCameraUsageDescription string, or the packaged app is killed on first
camera access instead of prompting.
2026-07-28 13:40:44 -05:00
Brooklyn Nicholson
47cb4ea1fe fix(desktop): allow camera capture through the permission handlers
The session permission hooks were written for the voice composer and denied
video outright, so any renderer getUserMedia({video}) failed with
NotAllowedError before the OS was ever consulted.

Rename isAudioCapturePermission to isMediaCapturePermission and accept video
alongside audio in both the request and check handlers. The OS capture
permission still applies, so the user keeps a real allow/deny.
2026-07-28 13:40:44 -05:00
hermes-seaeye[bot]
9cf1227d4e
fmt(js): npm run fix on merge (#73555)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-28 18:38:33 +00:00
hermes-seaeye[bot]
2faac36866
fmt(js): npm run fix on merge (#73552)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-28 18:30:19 +00:00
Teknium
5dc6a14c14 fix(credits): remove the 'Grant spent · $X top-up left' notice
The grant_spent notice fired for every subscription user with top-up
funds the moment their cap was reached and camped in the CLI/TUI status
bar and desktop toasts with no action to take — the account keeps
working off top-up. Remove it everywhere:

- agent/credits_tracker.py: drop grant_cond + the emit/clear block;
  the dev fixture state now (correctly) produces no notice
- TUI: keep the turn-start clear of credits.grant_spent as back-compat
  for older backends that still emit the key
- Desktop: drop the demo step and stale comment references
- Docs/config comments: remove grant-spent from credits_notices text
- Tests updated: policy/cold-start now assert the key never fires

Usage bands, depleted, and restored notices are unchanged; /usage still
reports the full balance breakdown.
2026-07-28 11:21:44 -07:00
Teknium
071e2adede test+i18n: follow-up for salvaged PR #68969
- Extract the cmdk filter into exported rankSearchOption and cover it,
  SearchableSelect selection/clear/placeholder, and ConfigField
  searchable-schema routing with 12 vitest cases (sabotage-verified:
  the backend schema test fails on unfixed main).
- Add searchPlaceholder/noResults/systemDefault strings to ja/ar/zh-hant
  (zh + en came with the salvaged commits; defineLocale would have
  fallen back to English otherwise).
- Add a backend invariant test: timezone ships as a searchable,
  clearable select of sorted IANA ids with a UTC fallback.
2026-07-28 10:49:14 -07:00
David Metcalfe
c8a4b18d34 fix: address community review — system default, clearable schema flag, UTC fallback
- Add 'System default' clear option to SearchableSelect via clearLabel prop
- Add clearable flag to ConfigFieldSchema (schema-driven, not hardcoded)
- Add clearable: true to timezone schema override in web_server.py
- Fix CommandItem value for clear item: use clearLabel instead of '' so
  cmdk can match it during search
- Fix backend: or ['UTC'] fallback for hosts without tzdata where
  available_timezones() returns an empty set (not an exception)
- Add systemDefault i18n key (en, types, zh)
2026-07-28 10:49:14 -07:00
David Metcalfe
b5b3ed6563 fix: address cross-vendor review feedback
- Fix focus: use autoFocus on CommandInput instead of e.preventDefault()
- Fix filter: prioritize city segment match (return 2 for slash match)
- Fix handleSelect: always select, don't toggle-deselect
- Add aria-haspopup to trigger button
- Use defensive placeholder logic
2026-07-28 10:49:14 -07:00
David Metcalfe
5cb0a6aec1 feat(desktop): searchable timezone dropdown in Settings → Chat
The timezone field was a free-text input with no guidance on format.
Users had to know the exact IANA identifier (e.g. America/New_York)
to configure it. Replace it with a searchable combobox built on
Popover + cmdk Command — the same stack as Shadcn's Combobox.

Backend:
- Add `_timezone_options()` to web_server.py (cached at import time,
  returns sorted zoneinfo.available_timezones() — ~598 identifiers)
- Add `"timezone"` to `_SCHEMA_OVERRIDES` with `type: "select"`,
  `options`, and `searchable: true`

Frontend:
- New `SearchableSelect` component (Popover + cmdk Command)
  — closed-world filterable dropdown for large option lists
- `ConfigField` routes to `SearchableSelect` when
  `schema.searchable === true` (explicit opt-in, no threshold)
- Add `searchable?: boolean` to `ConfigFieldSchema` type
- Add i18n keys: `searchPlaceholder`, `noResults`

The `searchable` flag is deterministic — no existing field is
affected unless explicitly opted in. Future large-list fields
can adopt the same pattern by adding `searchable: true` to their
schema override.
2026-07-28 10:49:14 -07:00
Adolanium
b5dc471152
feat(desktop): make attachment data-URL size limit configurable (#73221)
Hard 16 MB cap on readFileDataUrl blocked larger local attaches with no way to raise it. Settings -> Chat now has a free-form MB field. Main process owns the persisted value and clamps only absurd inputs.
2026-07-28 09:10:21 -04:00
brooklyn!
30526baab5
Merge pull request #73262 from NousResearch/bb/dedupe-main-tile
fix(desktop): drop a session's tile when it loads into the main tab
2026-07-28 04:23:02 -05:00
brooklyn!
87a37b9492
Merge pull request #73247 from NousResearch/bb/skill-chip-only
Render a /skill turn as its invocation, never the skill body
2026-07-28 04:22:07 -05:00
Brooklyn Nicholson
ab741ed331 fix(desktop): drop a session's tile when it loads into the main tab
A session is meant to be either the main thread or a tile, never both.
openSessionTile enforced this from the tile side (refusing to tile the
selected session), but the main side never dropped an existing tile — so
any path that routes main to a session that's already tiled (cold-start
remembered-session restore, a pasted/Cmd-K route, a notification jump)
painted the same transcript twice: the workspace pane from the route and
the tile pane in parallel, both fighting one runtime.

resumeSession is the single chokepoint every load-into-main path funnels
through, so close the redundant tile there once the session becomes the
selection. The warm cache/runtime binding survives for main to reuse.
2026-07-28 04:16:28 -05:00
brooklyn!
dcc543af9a
Merge pull request #73172 from NousResearch/bb/featured-models
feat(picker): curated model defaults + collapsible providers + select-all in Edit Models
2026-07-28 04:09:46 -05:00
brooklyn!
7ddf3f1500
Merge pull request #73161 from NousResearch/bb/default-zoom-out
feat(desktop): default UI zoom to Appearance 90% preset
2026-07-28 04:08:15 -05:00
Brooklyn Nicholson
b3ba5570c9 Merge origin/main into bb/skill-chip-only
Keep skill-invocation projection helpers alongside the auto-continue
legacy display typing from #73250.
2026-07-28 04:06:32 -05:00
Brooklyn Nicholson
42fc6227f6 fix(ci): sort desktop imports and stub skill display in TUI test
Eslint wants @hermes/shared before react, and the slash handler only
passes a display override when command.dispatch includes one.
2026-07-28 04:03:48 -05:00
Brooklyn Nicholson
4a9754b3c3 feat(desktop): default UI zoom to Appearance 90% preset
Land on the exact 90% scale so the settings control shows a selection
on fresh installs, instead of five Ctrl+- steps (~91%) between presets.
2026-07-28 04:00:55 -05:00
Brooklyn Nicholson
9ffb35c3c7 feat(desktop): collapsible providers + select-all + search in Edit Models
Mirror the picker dropdown's provider collapse into the Edit Models dialog so
curating is one click per provider instead of scrolling through 30 models.
Each provider header is a full-width clickable label (same style as the
composer context-menu labels) with a DisclosureCaret next to the text and a
select-all Checkbox (indeterminate when partial). Model rows stay Switches.
The dropdown's collapse is fixed too: the current provider is now
collapsible (was forced open), and the label style matches the rest of the app.
Adds a search icon to the dialog input matching every other search field.
2026-07-28 03:58:33 -05:00
Brooklyn Nicholson
e682a9c0a7 refactor: drop the shared subpath export, keeping package.json untouched
The subpath entry existed only so the TUI could import the client-side
projection fallback. The TUI spawns its gateway from this same checkout and
cannot version-skew with it, so that fallback was dead weight — it reads
`display` directly now. With its last caller gone the dispatch helper folds
back into the desktop, where an older backend is genuinely reachable.

apps/shared/package.json is byte-identical to main again.
2026-07-28 03:54:49 -05:00
Brooklyn Nicholson
ff7418315f fix(tui,cli): render a resumed interrupted turn as an event
Desktop already did; the TUI transcript and the CLI resume recap fell
through to the default and printed the raw system note.
2026-07-28 03:54:25 -05:00
Brooklyn Nicholson
9a6b69d9af feat(ui): indeterminate support on the shared Checkbox
Add data-[state=indeterminate] styling (same primary fill as checked) and a
dash glyph that shows when the root is in the indeterminate state, so a
partial select-all reads as a dash rather than a full check.
2026-07-28 03:52:33 -05:00
Brooklyn Nicholson
64166f87ba feat(desktop): default the model picker to the featured shortlist
expandProviderDefaults prefers a provider's featured_models when present and
falls back to the existing top-N for providers that ship none (single-lab,
local, custom). Only aggregators get curated, so exactly the providers with
the everything-under-the-sun problem are trimmed; the rest are unchanged.
Every non-featured model stays one search or Edit Models toggle away.
2026-07-28 03:52:33 -05:00
Brooklyn Nicholson
ddd6b57938 test(desktop): a leading slash now chips, superseding #71664's exclusion
#71664 asserted a leading slash never chips, correctly: a command only ever
executed, so it never reached a rendered message as text. Projecting a skill
turn back onto its invocation removes that precondition.
2026-07-28 03:47:49 -05:00
Brooklyn Nicholson
20b2022b8c fix(desktop): render a skill send as its invocation everywhere it surfaces
The bubble, the queue panel, and the queue editor all showed a queued or
sent /skill turn's expanded body. Thread the invocation through submit and
the queue entry, and chip a leading slash command the way a mid-prose one
already chips.
2026-07-28 03:44:31 -05:00
Brooklyn Nicholson
b700f9f253 feat(shared): read a skill invocation out of its expanded scaffolding
The client-side twin of the gateway's projection, shared by the desktop and
the TUI so a surface talking to an older gateway still renders the
invocation rather than the whole skill body.
2026-07-28 03:44:23 -05:00
brooklyn!
48cadf197e
Merge pull request #73229 from NousResearch/bb/slash-catalog-cache
Cache slash completions and show skills on a bare /
2026-07-28 03:34:45 -05:00
Brooklyn Nicholson
9ad400580c fix(desktop): pin E2E sandboxes to Chromium zoom 0
Fresh installs now default to ~91%, but Playwright hit-testing and
visual baselines still assume 100%. Seed zoom-state.json so isolated
E2E profiles don't inherit the product default.
2026-07-28 03:33:10 -05:00
Brooklyn Nicholson
3ae9b16928 test(desktop): cover skills-only completions for a mid-message slash 2026-07-28 03:25:00 -05:00
brooklyn!
99d766fcac
Merge pull request #73220 from NousResearch/bb/subagent-runs
Show a delegation as its subagents, live
2026-07-28 03:22:43 -05:00