16 Commits

Author SHA1 Message Date
teknium1
d49da55d4c fix(update): skip recompiling the TUI and web UI when their inputs are unchanged
The source update rebuilt every frontend unconditionally, although each
compiler writes a receipt that the launch path already trusts to answer
"would a rebuild produce these bytes?". Reuse it in the update too, as the
pre-PM updater did for the web UI (_web_ui_build_needed). Desktop keeps its
unconditional build: the baked install stamp carries the commit, so every
update that moves HEAD changes a desktop input anyway.

Credit: #125749 by @Froraut proposed the same receipt-gated TUI/web skip first.

Co-authored-by: Froraut <79215537+Froraut@users.noreply.github.com>
2026-10-01 15:28:55 -07:00
Brooklyn Nicholson
749ef18b16 fix(desktop): serialize the mutable build preflight behind a cross-process lock
Two concurrent `hermes desktop`/`hermes update` invocations mutate the same
checkout-scoped node_modules and apps/desktop/release with no serialization:
the check-then-build preflight (_desktop_build_needed → npm install → pack) runs
unguarded, so the loser trips over the winner's half-installed tree
(ENOTEMPTY) or packs against a missing tsc.

Lock the whole mutable window behind DesktopBuildLock, a checkout-keyed flock
under the profile-common Hermes root (kept out of the checkout so read-only
prebuilt launches still work, released by the OS if a builder crashes):

- `hermes desktop` acquires it before the freshness check and refuses a
  contended build with an explicit message (exit 2) instead of racing.
- `hermes update`'s desktop rebuild (build_update_products) waits for an
  in-flight build rather than failing the update.
- the lock is released before the Electron handoff (source, packaged, and
  --build-only paths), so an open Desktop window never blocks a rebuild,
  and after a build failure so the next run isn't deadlocked.

Supersedes #94101 (lock design; its main.py wiring no longer applies after the
cmd_gui extraction) and #96581 (wait/exit-2 UX; not checkout-keyed).

Co-authored-by: worlldz <cryptoworlldz@gmail.com>
Co-authored-by: lepetitprince716-prog <lepetitprince716-prog@users.noreply.github.com>
2026-09-29 19:49:52 -05:00
teknium1
a153adcc2e fix(update): install the SDKs of configured gateway platforms instead of only warning (#124228)
A PM environment build keeps the recorded extras selection and never reads
config, so a home with platforms.discord (or telegram) enabled came out of
`hermes update` without the SDK: the update child detected it
(_configured_features_missing_deps) and only printed a warning, and the
gateway's lazy install at connect time is the only recovery — which fails
silently when lazy installs are off or the process is not on the selected
generation.

The update-build child (fresh, selected interpreter) now maps each missing
configured feature to the pyproject extra named after it (platform name ->
extra, MCP -> mcp), keeps only declared, platform-supported extras, and adds
them with an explicit pm.sync_venv. Detection reuses the gateway's own
connected-platform semantics (config + env credentials), so it installs exactly
what the gateway will try to load on restart. Features it could not install
still get the warning; an install failure never fails the update.

Flips tests/e2e/core/upgrade/pm/test_configured_features.py::
test_configured_gateway_platform_has_its_sdk_after_update.
2026-09-28 06:42:00 -07:00
teknium1
b63c138d78 fix: Hermes never falls back to the user's node/npm/npx/uv
Maintainer ruling: only Hermes and only its packaged package managers
(uv/pip/node/npm) are ever used; no PATH fallback when the managed tool is
missing, no "prefer the user's if new enough".

- hermes_constants.find_node_executable: node/npm/npx resolve to PM's
  installed copy or None. Every caller already pm.ensure()s on None, so a
  missing runtime is now provisioned instead of silently borrowing the
  user's Node (native-addon ABI / npm cache mismatches).
- agent/lsp/install._install_npm: pm.ensure('npm') when PM npm is absent,
  instead of failing over to whatever npm is on PATH.
- gateway._append_node_dir_for_service: stop baking the invoker's PATH node
  dir into generated systemd/launchd units.
- main_install_repair._resolve_node_runtime_npm: drop the PATH re-scan for
  another npm.
- source_build.source_product_current: run the freshness reader only with
  PM's node.
- doctor: Node/npm rows and npm audit use PM's copies (Termux APT distro
  keeps its system Node).
- install.sh ensure_uv / install.ps1 Get-Uv: always stage the pinned uv
  artifact; delete the "uv on PATH if new enough" developer shortcut.
2026-09-27 22:04:26 -07:00
Brooklyn Nicholson
831c3d2441 fix(desktop): launch the installed Hermes.app from hermes desktop
On macOS `hermes desktop` launched the release/ bundle even when an
installed Hermes.app existed, so the app ran from two paths (two Dock
icons, TCC grants keyed to a different bundle) while the one Finder opens
stayed stale. Refresh the installed copies first and launch the one that
now matches the checkout build; release/ stays the fallback.
2026-09-26 21:42:48 -05:00
Austin Pickett
b00b4bb7f2 fix(cli): pin utf-8 decoding on all text-mode subprocess readers
On Windows, subprocess text=True without an explicit encoding decodes
child output with the ANSI code page (e.g. 'gbk'); non-ASCII bytes then
raise UnicodeDecodeError inside subprocess._readerthread, killing the
Hermes backend before it becomes ready and surfacing as the desktop boot
timeout.

Sweep every hermes_cli text=True subprocess call to encoding='utf-8',
errors='replace', and add an AST-based regression test that fails when a
future text-mode call omits the encoding.

Fixes #55658
2026-09-25 14:22:12 -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
a564b9dc17 feat(pm): contain child output interactively, stream it in CI
uv, npm ci, icon generation and the desktop build printed hundreds of
lines on every interactive install, update and `hermes desktop`.

pm/progress.py holds one policy. CI (CI / GITHUB_ACTIONS) or
HERMES_VERBOSE=1 streams child output unchanged. Otherwise each child is
one "→ label…" line, rewritten in place with its latest output on a
terminal, or just the start and finish lines when piped. A failure
always prints the last 80 lines. HERMES_VERBOSE=0 forces containment.

Applied to PM's uv runs (no uv --verbose outside CI; quick steps stay
silent unless they fail), the source build scripts (node-deps, TUI,
icons, web; npm warn lines stay out of the live line), the desktop
build and package steps, and the Windows ARM64 vcpkg/OpenSSL provider.
The policy reads the parent's environment, so the CI=1 the builders
force on their node children does not switch it to verbose.
2026-09-24 14:23:48 -04:00
ethernet
25d3604fe7 fix(update): use selected Python for isolated icon generation 2026-09-24 01:40:42 -04:00
ethernet
dcd2bca06a fix(desktop): render icons with core runtime dependencies 2026-09-24 01:30:15 -04:00
ethernet
2eec0d9b64 fix(install): the shared completion tail runs from a source slice
- build_update_products builds only the frontends the checkout carries; a
  python-only slice (the installer's acceptance fixture) publishes commands
  and runs maintenance without asking PM for node.
- stderr_timestamp.py is a launcher boot file copied into published
  commands; it inlines the EX_CONFIG code instead of importing gateway.restart.
- Tests: the stamp-writer slice gains hermes_cli/release_channels.py and
  pm/paths.py (the modules update_channel now imports); the source-launch
  fixture records the source_completion hand-off (--finish-update) instead
  of building products; the stdlib recovery probe blocks PM's engine
  modules, not the pm.environments boot leaf; the memory-provider restart
  test selects a generation the running interpreter has not activated;
  the warm-path installer stage is `products`.
2026-09-19 02:53:58 -04:00
ethernet
a6ae6ace51 Merge remote-tracking branch 'origin/main' into ethie/pm-clean
# Conflicts:
#	.github/workflows/js-tests.yml
#	agent/model_metadata.py
#	apps/desktop/electron/main.ts
#	apps/desktop/scripts/bundle-electron-main.mjs
#	apps/desktop/src/app/settings/about-settings.tsx
#	apps/desktop/src/app/settings/gateway-settings.test.tsx
#	apps/desktop/src/app/settings/gateway-settings.tsx
#	apps/desktop/src/app/updates-overlay.tsx
#	gateway/shutdown_flush.py
#	hermes_bootstrap.py
#	hermes_cli/local_runtime/binaries.py
#	hermes_cli/main.py
#	hermes_cli/managed_uv.py
#	hermes_cli/update_cmd.py
#	hermes_cli/update_cmd_deps.py
#	hermes_cli/update_cmd_fleet.py
#	hermes_cli/update_cmd_maint.py
#	hermes_cli/update_receipt.py
#	hermes_cli/update_serve_obligations.py
#	hermes_constants.py
#	tests/hermes_cli/test_doctor.py
#	tests/hermes_cli/test_managed_uv.py
#	tests/hermes_cli/test_pending_supervisor_recovery.py
#	tests/hermes_cli/test_startup_fast_guards.py
#	tests/hermes_cli/test_update_desktop_stale_warning.py
#	tests/hermes_cli/test_update_fleet_restart_pending.py
#	tests/hermes_state/test_hermes_state.py
#	tests/tools/test_tirith_security.py
#	tools/bot_relay.py
#	tools/checkpoint_manager.py
#	tools/write_approval.py
#	website/docs/getting-started/updating.md
#	website/docs/reference/environment-variables.md
2026-09-18 17:26:10 -04:00
ethernet
aaf5889207 feat(update): stream long update stages into the desktop hand-off UI
The shim renders progress from its status JSON file, but everything
after 'Updating code and dependencies' — the PM dependency sync, Node
deps, the TUI/web/desktop builds — ran for minutes without touching
that file, so the window froze on a stale stage (or showed nothing at
all when the old shim skipped its browser window).

hermes_cli/update_stage.py publishes stages to the watching UI,
std-only and never raising. Two discovery paths: the exported
HERMES_UPDATE_STATUS_FILE (current shim), and, for an OLD shim that
never exported it (every old-to-new checkout transition), the marker's
owner pid, which names the shim whose status file is deterministically
/tmp/hermes-update-status.<pid>. On macOS the takeover child also pops
update-panel.applescript from the freshly pulled tree when the old
shim's log shows it started no renderer.

Publishes: PM sync (_update_takeover.prepare, venv_sync.sync), Node
deps and each product build (source_build.build_update_products).
Only 'running' stages are ever written — terminal states stay the
shim's.
2026-09-17 18:58:42 -04:00
ethernet
b681f0c50e merge: reconcile origin/main with PM runtime ownership
Preserve upstream fixes without restoring retired dependency installers.
Run configured-feature checks in the selected build interpreter. Reuse a
supported base Python during bootstrap, and preserve durable backup media.

Refresh the dependency lock through PM. Keep the frozen historical import
surface unchanged. Adapt incoming native tests to the platform markers.

Verification: the incoming 86-file pass found two fixture mismatches;
both passed after correction. Targeted PM/update/compatibility checks,
Electron and renderer typechecks, and desktop tests passed.
Native Windows/macOS update journeys and the full suite remain unrun.
2026-09-12 16:55:32 -04:00
ethernet
5e4a2a3d24 refactor(pm): remove legacy dependency and launch managers
Competing installers and checkout-local venv assumptions bypassed PM
selection, install consent, and generation lifetimes. Route consumers
through PM and installation-bound launchers. Refresh source launchers
before obsolete Python entries can be collected.

Remove Node, browser, and CUA acquisition engines, obsolete venv-holder
handling, detached sync, and unused PM APIs. Keep historical updater
exports inert and preserve external tool ownership and native integration.

Share product freshness and prepared inputs across builders. Align plugin
admission, Docker provisioning, setup instructions, and behavioral tests.

Verified targeted Python and JavaScript tests, desktop and web typechecks,
scoped lint, real product builds, and the Docker frontend smoke test.
The missed post-setup test cleanup is included and verified.

Native Windows/macOS execution, full Rust compilation, and the complete
repository suite remain unverified. Historical compatibility requirements
were preserved and extended, not fully rescanned.
2026-09-12 14:57:38 -04:00
ethernet
26c4e8b160 refactor(update): use PM and shared source builders
PM owns Python dependency generations. Shared frontend builders own Node
preparation and compilation. Route source updates and launchers through
these owners instead of separate repair ladders.

Remove obsolete live-venv holder gates and soft build-failure plumbing.
Preserve source validation, staged publication, fleet outcomes, and
historical relaunch hooks.

Verification: 956 tests passed in the combined focused run, with 43 skips.
After the final ZIP exit fix, 583 focused tests passed. Shared JavaScript
builder tests, Ruff, and diff checks passed. Native Windows/macOS and full
packaged-app builds were not run.
2026-09-12 13:45:08 -04:00