Commit Graph

43559 Commits

Author SHA1 Message Date
fangliquan
285768fdfb fix(pm): drop stale Hindsight extra
Merge 27df3b8847 brought back the hermes-agent[hindsight] extra
(hindsight-client==0.6.1) that 73c598e319 removed. The catalog
Hindsight plugin now requires hindsight-client>=0.10.1,<1, so any
workspace containing it fails `uv lock`. Remove the extra, its
exclude-newer entry and its legacy-takeover mapping, and regenerate
uv.lock.

The workspace-member rename from the original PR is dropped here;
#122098 landed that half.

Salvaged from #122092.
2026-09-24 22:12:34 -04:00
ethernet
996c252914 Merge pull request #122098 from liuhao1024/liuhao/cron-bugfix-122071
fix(pm): name virtual workspace members by their unique key
2026-09-24 22:11:02 -04:00
ethernet
f3fdee8788 Merge pull request #122101 from NousResearch/fix/record-msix-executable
fix(release): Windows release builds no longer fail recording the executable version
2026-09-24 22:10:00 -04:00
Austin Pickett
6ee0ae4d5e fix(desktop): open markdown preview links inside the app
Electron denies every target=_blank window, so a web link in a previewed
.md file looked clickable and did nothing; a #fragment link rode the
HashRouter and changed the app route instead of scrolling the note; a
relative link to a sibling note never opened it.

Links now take the same doors chat links take: web links render through
ExternalLink (in-app browser, Cmd/Ctrl for native), file links are wrapped
in the existing #preview/ hash before Streamdown's hardener sees them and
resolved against the note's directory by normalizeOrLocalPreviewTarget,
and headings get GitHub-style ids from a rehype plugin so a TOC click
scrolls within the pane. Slugs and lookups are NFC-normalized so a TOC
written on another editor still finds its heading.

Fixes #81055
Co-authored-by: xxxigm <tuancanhnguyen706@gmail.com>
2026-09-24 22:00:20 -04:00
Austin Pickett
7fd7f33cc2 fix(desktop): collapse ../ in the renderer preview path fallback
A note linking ../other.md resolved to /notes/../other.md when the Electron
normalizer is unavailable, a path the fs bridge then rejects.
2026-09-24 22:00:20 -04:00
liuhao1024
574c5d940a docs(pm): note why tool.uv.package = true makes a member buildable
Review suggestion from #122098: spell out on the condition line that
uv package mode produces real build metadata, so such members keep
their declared name.
2026-09-25 09:37:18 +08:00
ethernet
3d7fc8fc03 Merge pull request #122095 from NousResearch/fix/desktop-ismain-eval
fix(desktop): isMain returns false when there is no entry script
2026-09-24 21:35:02 -04:00
ethernet
051febedfd fix(release): read the Windows executable name from the MSIX manifest
record() looked up stamp['identity']['windowsExecutableName'], a key no
stamp writer emits, so every Windows record on a runner resolved no
executable and failed. The built package's Application/@Executable
names the exe; match it in the unpacked dir only, which also skips
before-pack's .bak rollback copy.
2026-09-24 21:28:32 -04:00
liuhao1024
0fc90369a5 fix(pm): name virtual workspace members by their unique key
uv identifies a workspace member by its declared project name, so the
same plugin enabled in two profiles declares one name twice and the
dependency sync fails with 'Two workspace members are both named ...'.
Metadata-only members (no build backend) now carry the unique member
key in their name, exactly like manifest-only members already do. A
buildable member keeps the name it declares, since uv verifies it
against the package metadata its backend produces.
2026-09-25 09:24:18 +08:00
brooklyn!
71239b7003 fix(desktop): treat a missing plugin-decision door as bot mode on
Other plugin tests mock host without that door. A missing read is the
default, not a crash.
2026-09-24 20:23:18 -05:00
brooklyn!
7bde1dfe20 fix(desktop): read bot-mode state through the plugin SDK
Plugins cannot import the contrib store. host.pluginDecisions is the
read-only door, and absence still means the plugin default.
2026-09-24 20:23:18 -05:00
brooklyn!
ea3478d520 fix(desktop): do not dial the bot relay drain with nothing to deliver
The 30s drain opened a throwaway gateway socket even when Bot Mode was off or the route had no outbox work. With the push door, only a route that signaled bot_relay.outbox.pending is drained. Older shells without that door still poll. The background-scope reset from #119836 is unchanged.
2026-09-24 20:23:18 -05:00
ethernet
4920ca4160 fix(desktop): isMain returns false when there is no entry script
Importing a script from `node -e` leaves process.argv[1] undefined, and
pathToFileURL(undefined) throws on import. The bootstrap installer's
signing step imports batch-sign-binaries.mjs this way, and it crashed
when that pulled in sanitize-pe-signatures.mjs.
2026-09-24 21:22:18 -04:00
Hermes Agent
6c4a983e8d style(desktop): format session action hook 2026-09-24 20:22:11 -05:00
brooklyn!
f0d6d2151c fix(desktop): publish pending clarify before transcript hydration
An authoritative still-pending clarify was answerable only after
getLatestSessionMessages returned. Publish the snapshot tool-call row
in the same needsInput view update, and keep provenance checks from
hiding that row.
2026-09-24 20:22:11 -05:00
Hukla
f7a861379f fix(desktop): decode fetched link metadata by charset 2026-09-24 20:13:12 -05:00
Hermes Agent
f1247d2e01 fix(desktop): adopt a published session token on the post-update relaunch
The post-update relaunch refused a backend that had published a session token
and spawned another. Adopt the host rendezvous token when GET / withholds it.

The stale app.asar half is dropped: main now judges desktop freshness from the
compiler receipt written inside the packaged output (26c4e8b160), so a skipped
or failed rebuild no longer looks current.
2026-09-24 20:06:38 -05:00
Hermes Agent
f26825eb2d test(desktop): keep rebased checks green 2026-09-24 20:04:24 -05:00
brooklyn!
6391c3c7e4 fix(desktop): route omitted-profile messaging approvals to the list server
A Telegram row that omits profile is owned by the backend that served
the messaging list. Keep a non-primary connection id, send a primary-pool
list through that profile door, and do not guess primary or ambient when
the list server was never recorded.
2026-09-24 20:04:24 -05:00
brooklyn!
c4ff1d89f4 fix(desktop): settle a silent live turn and offer retry
A dead or interrupted turn left the thinking spinner up, including after
a partial assistant payload. When that session stops producing events,
force-settle it and stamp the existing retryable error card instead of
leaving the spinner spinning.
2026-09-24 20:04:19 -05:00
brooklyn!
d371e21d07 fix(desktop): Enter on a focused clarify choice reaches activateActive
Clicking a single-select choice leaves focus on the option button, and the
window keydown handler bailed for every button, so Enter never submitted the
staged answer. Exempt button[data-choice] and let activateActive handle Enter:
a staged single-select answer submits, a multi-select row toggles, and Continue
still confirms the set. Choice buttons do not call submitAnswer.

Fixes #92816

Co-authored-by: echohn <echohn@gmail.com>
2026-09-24 20:02:48 -05:00
brooklyn!
b609c745df fix(desktop): scope the config-record cache to the active gateway
The shared ['hermes-config-record'] slot survived a gateway switch, so a
settings save painted the previous machine's record and PUT it onto the
other config.yaml. Key that existing slot by $activeConnectionId.

Fixes #101640
2026-09-24 20:02:37 -05:00
brooklyn!
da77a7e2e8 fix(desktop): defer tray hide and log main-process stalls
Hiding inside the minimize handler wedges the Windows minimized flag, and
showInactive() restores a painted window that never takes input. Defer hide
until after that dispatch, skip a stale hide if the user already restored,
and activate with show()+focus() on Windows. Log main-process event-loop
stalls in desktop.log; renderer unresponsive handlers cannot see AppHangB1.

Reported in #119252.

Co-authored-by: finn763 <165816600+finn763@users.noreply.github.com>
Co-authored-by: KoNit-K <konit.block@protonmail.com>
2026-09-24 19:53:53 -05:00
brooklyn!
8b88c6b6c8 fix(desktop): speak each assistant turn once across id rewrite
Auto-speak keyed already-spoken on the assistant row id and ordinal.
Hydration rewrites the live id and tool-row folds move that ordinal, so
the same turn looked unspoken and was played again. A second
playSpeechText stopped the first clip. markSpoken also ran before a play
that never started, so the turn stayed silent.

Key the anchor on the user turn, which survives both. A later turn that
says the same thing is still spoken. Release the anchor when playback
never starts, and do not let a second start of the same turn stop the first.
2026-09-24 19:52:31 -05:00
Hermes Agent
b50bb77ec5 fix(desktop): preserve implicit default local route 2026-09-24 19:51:33 -05:00
brooklyn!
76460be82d fix(desktop): refuse remote-only profile spawns when the connection id is missing
An empty connection id is no longer rewritten to registry.primary, which dialed
another SSH host. A concrete remote-only profile, including default, is refused
on the forced-local branch instead of spawned. A profile open without an owner
route no longer stamps mode local when the live connection is remote.
2026-09-24 19:51:33 -05:00
brooklyn!
f86db25ef5 fix(desktop): refresh peer windows and refuse a stale multi-window submit (#65047)
A second Desktop window on the same stored session never saw the first
window's completed turn, and submitting from that stale transcript could
fork the session. Notify other windows to re-pull on turn completion, and
refuse composer and session-tile submit when the local transcript is behind
the authoritative latest page.

Co-authored-by: stantheman0128 <stanshih888@gmail.com>
2026-09-24 19:49:23 -05:00
brooklyn!
b81c5811b7 fix(tts): review the speak-stream ffmpeg lookup
ffmpeg is a system decoder, same as the other TTS sites. The new call
belongs on the resolution allowlist.
2026-09-24 19:28:05 -05:00
brooklyn!
20bcc9bd14 fix(tts): stream Edge speak-stream per sentence instead of whole-text fallback
Desktop speak-stream sent type=fallback whenever the provider had no
chunked PCM API. Edge is that case, so the client waited for the full
reply and POSTed it. Cut sentences with the existing sync TTS tool and
stream that PCM. Fallback stays the last resort when synthesis produces
no audio.

Refs #91997
2026-09-24 19:28:05 -05:00
Brooklyn Nicholson
434b1011a9 fix(desktop): aggregate session dot state per profile on the rail (#91710)
A profile that finished work (or blocked on input, or is still running)
while another profile was selected showed no indicator on its rail square
or dropdown row — the session-level unread/attention layers
($sessionDotStateById, the persisted per-profile unread markers, the
backend row.unread watermark) all existed, but the profile rail
subscribed to none of them.

Add $profileDotStateByScope: a per-(connection, profile) rollup of the
shared session dot state, derived in three passes:

  1. loaded chat/messaging rows claim their row-tagged scope
     (stalled/background fold into working, like the sidebar's buckets;
     cron rows stay excluded so a profile square is not a cron counter),
  2. unlisted live runtimes claim the scope their socket proved
     (runtimeSessionOwner; no proven owner claims nothing),
  3. persisted unread markers with no loaded row claim their profile's
     scope only when the owning gateway is unambiguous — two gateways
     sharing a profile name leave the bucket unpainted rather than
     guessing.

Priority: needs-input > working > unread, matching the session rank.

The busy->idle finish edge now passes the socket-proven owner profile
into markSessionUnreadFinished, so a background profile's finish can no
longer land in the ACTIVE profile's marker bucket (which would light
the wrong square). The reconnect parked-set becomes a Map so a later
confirm keeps the runtime id for that lookup.

The rail paints the rollup as a small dot on ProfileSquare, RestSquare,
ProfilePill and both dropdown rows, with every non-zero count in the
accessible name and tooltip ("writer, 1 unread session"). The active
profile's square stays clean — the workspace is homed there and its
rows are on screen in the sidebar below.
2026-09-24 19:27:41 -05:00
rainbowgits
189f051679 fix(desktop): show full-resolution attachments in sent bubble and lightbox
Attached images rendered at a 512px thumbnail in the live sent bubble and
click-to-zoom lightbox, only becoming sharp after a session reload rehydrated
the turn from disk. Route the in-flight bubble through the same DirectiveImage
path as a reloaded turn: a bounded thumbnail is painted inline (preserving the
deliberate anti-freeze cap) while the full-resolution file is handed to the
on-demand lightbox and download.

Fixes #93204
2026-09-24 19:27:41 -05:00
andyst-dev
83c8d1353b docs(artifacts): document shell-output scan false-positive budget + array-index drop (reviewer)
Review points:
1. Noisy shell stdout (curl -v, build logs) is kept from flooding the panel:
   every prose candidate passes through looksLikeArtifact, which requires a
   file/image extension or http(s)/data: scheme, and local file existence is
   resolved via the media ladder (artifactImageSrc -> resolveMediaDisplaySrc
   -> readFileDataUrl). Documented at the shell-output scan site.
2. Bare numeric array indices are dropped from the key path intentionally so
   array-of-results payloads (e.g. outputs.0.output) match via their real
   segments; noted that switching to exact-key matching would silently break
   those shapes.
2026-09-24 19:27:41 -05:00
andyst-dev
276f224742 fix(artifacts): index files produced by the terminal tool
Terminal results were never scanned for artifact references: 'terminal'
is not matched by ARTIFACT_PRODUCER_TOOL_RE, and its stdout lives under
the bare 'output' key which STRONG_TOOL_ARTIFACT_KEY_RE does not list.
Script-generated figures (matplotlib, ffmpeg, pandoc, ...) never showed
up in the Artifacts panel unless the assistant remembered to re-emit a
MEDIA: tag in prose.

Treat terminal as an artifact producer: scan its free-text result
(MEDIA tags, markdown refs, URLs, absolute paths) and accept
'output'/'stdout' as scannable keys for that tool only. Non-terminal
tools keep their existing strict key gate.

Fixes #92220
2026-09-24 19:27:41 -05:00
Jony
f588166691 fix(google-chat): avoid guessing auth failure cause 2026-09-24 19:27:41 -05:00
ethernet
ac4181fdfa Merge pull request #122030 from NousResearch/ci/bootstrap-installer-build
ci: dispatch-only signed builds of the bootstrap installer
2026-09-24 20:02:06 -04:00
ethernet
13cd98b06b ci: dispatch-only signed builds of the bootstrap installer
Hermes-Setup has never had a CI build; every published copy was built by
hand. This workflow_dispatch lane builds the Windows x64 exe (signed via
the desktop MSIX's Azure Trusted Signing path, batch-sign-binaries.mjs)
and the macOS arm64 dmg (Developer ID signed, app + dmg notarized and
stapled) from any ref, and uploads them as run artifacts. Nothing is
published. No caches: no actions/cache or setup-node cache, and fresh
npm/cargo/electron-builder cache dirs.
2026-09-24 19:39:19 -04:00
ethernet
e9cd7e900b Merge pull request #122023 from ethernet8023/fix/desktop-legacy-source-main
Desktop updates older source checkouts from main instead of a manual card
2026-09-24 19:26:32 -04:00
ethernet
473fd1663c fix(desktop): update pre-probe source checkouts from main over git
A source checkout without hermes_cli/source_check.py predates release
channels, so the only line it can be on is git. The desktop treated the
missing probe as unsupported and parked the user on a manual
`hermes update --help` card ("This checkout predates desktop
source-channel checks"), so an older non-bundled install could never
update itself from the app.

Report such a checkout as tracking main with an update available and let
apply take the normal git handoff. That update brings in the probe, so
later checks resolve normally. A probe that exists but fails still throws.
2026-09-24 19:23:27 -04:00
ethernet
26b5494667 Merge pull request #122022 from NousResearch/fix/commit-icons
fix(icons): commit generated icons; installs and regular builds never render
2026-09-24 19:19:33 -04:00
ethernet
4b7229d612 fix(icons): commit generated icons; installs and regular builds never render
User installs failed with 'resvg-py is missing' because the web/desktop
source builds rendered icons on whatever python was on PATH. The default
brand outputs are now committed; source_build, apps/desktop build.mjs and
the npm/docusaurus pre-hooks consume them directly. Flavored release
bundles (canary/commit) still render into their own product dir.

icons-freshness-check now regenerates and fails on any byte diff.
2026-09-24 19:17:34 -04:00
ethernet
27df3b8847 bundles & unified installer 2026-09-24 18:57:27 -04:00
ethernet
f05c6c522a fix(install): ask for UAC approval to install ARM64 build tools
A non-elevated Windows ARM64 install threw "run setup-hermes.ps1 in an
Administrator PowerShell" whenever Visual Studio ARM64 C++/Clang were
missing. Interactive runs now launch the signed VS installer through a
UAC prompt; CI, ssh and scheduled runs keep the explicit instruction.
2026-09-24 18:55:21 -04:00
ethernet
40d5519836 pm: install the optional defaults after the venv sync
A bare `pm install` fetched agent-browser (~200 MB with Chromium) before
the venv sync, so a Windows ARM64 machine without build tools downloaded
it and then failed preparing the native build. Install defaults only once
the venv (and its build tools) succeeded.
2026-09-24 18:51:00 -04:00
ethernet
b5dcdbf151 ci(e2e): run the e2e lane at 3 workers 2026-09-24 18:45:32 -04:00
ethernet
d6e78f37b6 ci(e2e): run fewer process-tree suites at once
The e2e lane at 4 workers still starved the PTY turn and serve-SIGTERM
deadlines intermittently; drop it to 2. The upgrade lane had no cap and
ran cpu_count real-updater trees at once; cap it at 4.
2026-09-24 18:40:44 -04:00
ethernet
950401d507 test(boot): let the counted pm check pass keyword arguments through
activate() now tells check() whether to include the venv verdict; the
no-argument stand-in raised TypeError, which startup swallows, so the
check was never counted.
2026-09-24 18:18:28 -04:00
ethernet
7581ef4866 fix(pm): keep the pre-sync tool gate out of the venv check
activate(allow_incomplete=True) discarded the venv verdict but still
computed it, and venv_is_current reads plugin selection through the
application config reader (ruamel). The update child runs the bare
bootstrap interpreter, so `hermes update` failed with "No module named
'ruamel'" once it published tools before the sync.
2026-09-24 17:56:48 -04:00
ethernet
189b71c72c fix(update): install lockfile tools before the launch-time sync too
prepare_launch finishes an interrupted update, or a hand-run git pull, by
syncing the venv at startup. It skipped the required-tool step that
`hermes update` now runs first, so a bumped ripgrep/ffmpeg/python pin stayed
uninstalled and activation warned on every start.
2026-09-24 17:34:51 -04:00
ethernet
f67b3fcece fix(update): install required PM tools before the venv sync on every update
A normal `hermes update` only re-synced the venv. The sync pulls uv/python
in through its own dependency, but a ripgrep/ffmpeg/node/npm pin bump in
pm/lock.json was never installed, so PATH activation warned and skipped the
managed tool dirs on every CLI start and gateway boot. Only the takeover
route for historical releases ensured the tool roots.

Move the takeover's loop into pm.client.ensure_tools_for_sync() and call it
from both routes before the sync: update_completion._prepare (the CLI and
Desktop route, running from the new tree so the new lockfile applies) and
_update_takeover.prepare. It uses explicit=True like the takeover (an update
is an explicit user action) and a failed download fails the update.

post_update.step_provision_runtimes / MACHINE_STEPS stay: `python -m
hermes_cli.post_update --scope machine` and tests still reference them.
The ffmpeg docstring no longer claims that step re-ensures it.
2026-09-24 17:30:44 -04:00
ethernet
241bc72c1d test(e2e): let PM installers exercise the default browser install
The driver passed --skip-browser whenever the installer's help listed it.
That was for historical installers' own Playwright step; on a PM installer
the flag is now a real opt-out and would hide the default users get.
2026-09-24 17:27:45 -04:00