Commit Graph

181 Commits

Author SHA1 Message Date
ethernet
9214174e07 fix(ci): lint legs after the main merge
- check_no_tmp_literals: resolve scratch via tempfile/os.tmpdir; the termux
  container mount point is one marked variable per script
- ruff TID251: desktop E2E fixtures may reach PM internals like tests do;
  the pm.runtime_stage ban message no longer names a module that never existed
- auth_codex: build the capped httpx stream subclass on first use so importing
  hermes_cli.auth_codex no longer forces httpx (the lazy proxy in auth_constants
  was defeated by a module-scope base class; broke lanes without httpx)
- desktop-smoke: launchApp is a parameter; the bundle-env test substitutes a
  refusing launcher instead of letting Playwright spawn a dying binary
  (3 unhandled rejections failed the tests-js lane)
2026-09-19 23:25:18 -04:00
ethernet
5cb0a5073b fix(install): a diverged checkout cannot keep its old tree
`stage_repository` treated a failed `pull --ff-only` as "keep local state"
and carried on. Every later stage reads files only the new tree has (`pm/`),
so an install whose checkout could not fast-forward failed further down with
`ModuleNotFoundError: No module named 'pm'` instead of updating.

A release cut off the main line diverges for every user who installed from it:
v2026.5.29.2 is not an ancestor of main, so its checkout can never fast-forward
to a later main. Match the remote the way `hermes update` already does, after
parking the old tip behind refs/hermes-install-backup/<stamp>-<sha> and
stashing any local work so neither is destroyed silently.

Verified against a real diverged repository: before, the checkout stayed on
the old line (HEAD=6a2e091, remote=43eea63) with no backup; after, HEAD equals
the new main, the backup ref and an autostash exist, and both are named in the
installer's output.
2026-09-18 02:57:30 -04:00
ethernet
d5c2e3a830 fix(install): rebuild an existing desktop app on a plain installer rerun
installer-script+desktop -> installer-script failed as "desktop output is
missing, stale, or damaged": the leg installs with the desktop, then re-runs the
plain one-liner, which built only tui/web -- while the driver's verifier still
expects the desktop product it installed (EXPECT_DESKTOP comes from the install
method). The artifacts live inside the tree, so an update makes them stale rather
than absent, and a desktop build left over from the previous code is exactly what
the freshness receipt rejects.

The products stage now selects the desktop when --include-desktop/-IncludeDesktop
is given OR the checkout already carries a built app. Verified: install.sh syntax
clean and the new predicate returns absent/present against real temp trees;
install.ps1 parses clean.
2026-09-17 18:05:31 -04:00
ethernet
dbeebaadfc refactor(install): one completion tail shared by install and update
The installer ladder stopped at node-deps/path/desktop with its own
semantics while an update ran launchers, product builds and post-build
maintenance, so a fresh install and a finished update ended in different
states: after re-running the installer at HEAD the products had no receipts
and the read-only source acceptance failed.

hermes_cli/source_completion.py now owns that tail -- publish launchers,
build the products, run the maintenance -- and update_completion's
_complete_selected calls it, so there is one implementation. install.sh and
install.ps1 keep the bootstrap stages (prerequisites, repository, venv,
python-deps, config) and hand off to it in a single `products` stage;
--include-desktop selects the desktop product inside that stage instead of
adding a second build stage, and `desktop` stays dispatchable via --stage for
external callers.

Windows keeps its installer-owned PATH publication (expose_cli answers
"windows-installer-owned" on Windows) plus the packaged-artifact probe, ACL
grant and shortcuts. The desktop stage no longer pre-syncs wake/voice: pm
lazy-installs them at first use, as the update path does.
2026-09-17 15:26:05 -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
53e6f001c7 refactor(pm): consolidate runtime ownership and updater completion
Run historical updater completion in a fresh interpreter so cached imports
cannot revive retired dependency installers. Share Git and ZIP completion,
carry receipt and recovery state, and preserve child exit status.

Route plugin admission, binary acquisition, desktop launch and build paths
through PM. Replace redundant helpers and tests with real worker, package,
publication and launch checks. Keep the shipped compatibility surface fixed.

Targeted Python and desktop checks pass. Native update journeys and fresh
production image qualification remain pending. This is a checkpoint before
those acceptance runs.
2026-09-12 16:30:35 -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
teknium1
f7990661a0 fix(install): reuse probe skips the active venv; venv creation failure is fatal
Review finding: with the install's own venv activated (a re-run), `uv python
find <range>` returned venv/bin/python3, which setup_venv then deleted before
handing the dead path to `uv venv`; the stage still exited 0 and printed
"Virtual environment ready". Probe with --system so only base interpreters
qualify, and fail the stage when the venv was not created.
2026-09-12 08:32:09 -07:00
teknium1
5bdb4742b0 fix(install): reuse an installed supported Python instead of downloading 3.11
check_python only asked uv for 3.11 and, missing that, downloaded it —
a hard failure on hosts that cannot reach GitHub releases even when a
3.12/3.13 inside requires-python is already installed. Ask uv for the
supported range second and pin the venv onto that interpreter.

Same direction as #10824 (@gnanirahulnutakki), reduced to the shell
path; the PowerShell installer already has this fallback.

Refs #10778
2026-09-12 08:32:09 -07:00
teknium1
a7cf8ef8b2 fix(install): pick the .tar.gz Node.js archive when the host has no xz
`tar xf node-*.tar.xz` shells out to the xz binary; minimal Debian, DietPi
and WSL images ship tar without it, so extraction died mid-way and the
installer then failed on a missing directory. Select .tar.xz only when
`xz` is on PATH, in both the installer and the runtime node bootstrap.

Same approach as the earlier #4229 (@JoshuaMart) and #39541 (@karnull);
#11278 (@vominh1919) attempted an apt-only install of xz-utils instead.

Refs #11197
2026-09-12 08:26:17 -07:00
teknium1
a5228204b4 fix(branding): Caduceus in the WhatsApp bridge, installers and favicon too
Review finding on #109142: the JS bridge's DEFAULT_REPLY_PREFIX (the sender
actually used at runtime), scripts/install.sh, setup-hermes.sh and the site
favicon still carried the old glyph. Python and JS defaults now agree.
2026-09-12 08:25:54 -07:00
ethernet
884a0241ca Drop retired Python environment references from bootstrap documentation 2026-09-11 18:15:13 -04:00
ethernet
a154b89b9f Route build and CI Python preparation through PM operations 2026-09-11 18:14:43 -04:00
ethernet
c184f04838 Use prepared Python for bootstrap and desktop build helpers 2026-09-11 17:59:55 -04:00
ethernet
ad231c164b fix(setup): parse lock and mirror json by structure, not indentation
Two pre-Python readers anchored their awk patterns on the exact leading
whitespace of the machine-written json they parse:

- setup-hermes.sh read artifact-mirror.json with /^  "origin"/ (exact
  two spaces), so any other one-member-per-line layout lost the mirror
  fallback silently: the mirror url resolved empty and fetch_pinned
  reported only the primary url it failed on.
- scripts/install.sh read packages.python.version from pm/lock.json
  with exact 4-space and 6-space anchors; on any other layout the pin
  resolved empty and the install fell back to the hardcoded "3.14".

setup-hermes.sh's own pin() already established the contract for the
same file ("follow object names and braces, not indentation"); both
sites now follow it. The mirror read is a flat key match; the python
pin uses the same brace-tracking reader as pin().

No other site in the tree has the pattern: setup-hermes.ps1 uses
ConvertFrom-Json, nix uses builtins.fromJSON, python readers use
json.loads, and remaining awk users parse command output, not config.

Tests:

- tests/pm/test_setup_lock_format.py now parametrizes the mirror json
  indent (it was fixed at 2, leaving the mirror read unguarded) and
  adds a mirror-fallback E2E: the primary url is a dead port (curl
  exit 7, retriable), so the staged uv must come from the mirror,
  with the mirror json written at 9-space indent. Red on the old
  regex (mirror url resolves empty, exit 1), green with the fix.
- tests/test_install_sh_python_pin_indent.py (new) sources
  install.sh --manifest and proves bootstrap_python resolves the
  pinned version through a fake uv that records its calls, across
  2/4/0/tab/blank-line lock layouts.

Verified via scripts/run_tests.sh: 16 passed, 0 failed on the fix; the
new mirror-fallback test fails against the old parsers (verified by
stashing the two script changes and re-running). Sibling install/setup
tests (test_install_sh_node_deps_workspaces, test_install_stage_frames,
test_install_sh_desktop_stage, pm/test_activate_scripts) all green.
2026-09-11 16:33:02 -04:00
ethernet
284dbaf537 fix(pm): isolate bootstrap dependencies and unify YAML on ruamel
Activation reaches plugin discovery before the application dependencies
exist. Give PM its own locked Python project and runtime so it can install
or repair the application without importing that dependency tree.

Keep PM outside the application workspace. A shared uv workspace resolves
the application graph and cannot provide this isolation. Route mutations
through an isolated worker and preserve transaction callbacks, cancellation,
custom package registrations, and correlated receipts.

Use the same runtime builder for source installs and packaged payloads.
Keep offline wheelhouse support in that builder. Nix builds the independent
PM lock as a separate derivation. Refuse lazy-disabled bootstrap before
installing tools or dependencies.

Move first-party YAML readers and writers to ruamel. Keep the application
lock's transitive PyYAML requirements for third-party packages.

Verification:
- Focused canonical Python suite: 177 passed, 1 host-gated skip.
- Electron backend probes: 12 passed. Electron typecheck passed.
- Both uv locks, scoped lint, Bash syntax, and whitespace checks passed.
- Cold activation, corrupt-app repair, offline staging, and relocation ran.
- Built and exercised the Nix PM runtime and standalone YAML merge script.

Six broader caller test files retain the same 24 failing test IDs as an
archive of HEAD. The existing real-home guard blocks those tests before
they can exercise the affected paths. No full-suite pass is claimed.
Native Windows signing and full Bionic package execution remain unverified.
2026-09-11 12:23:51 -04:00
ethernet
c8a9682505 fix(pm): preserve pinned binary inputs in R2
Termux removes old package files, so a pinned URL and hash do not keep
build inputs available. Preserve the exact bytes without changing pins.

Archive every PM HTTP artifact and the Termux runtime inputs by SHA256.
CI reads R2 first. Only a missing object permits an upstream download,
hash verification, immutable upload, and verified readback. Seed the
actual toolchain and payload stores before their consumers run.

Use the public archive as a pinned fallback in PM, bootstrap installers,
and Nix fetchers. Keep network retries bounded and report attempted URLs.
Keep publication credentials in protected CI jobs, not installed clients.

Verification:
- 283 targeted tests passed; five POSIX tests skipped on Windows.
- All 87 preserved Termux packages passed local archive miss/hit checks.
- Native ARM64 ripgrep installed through the mirror and ran successfully.
- Wheel import, workflow lint, Python lint, shell syntax, and pins passed.

Live R2 publication, POSIX tests, and Nix builds remain for native CI.
The real-byte archive checks used loopback HTTP, not the live bucket.
2026-09-10 18:17:08 -04:00
ethernet
e49073ecf3 fix(install): publish source launchers through one writer
Setup searched for console scripts in a checkout-local venv that PM no
longer creates. Publish both commands through the shared writer after
PM setup, using store Python rather than a dependency interpreter.

Native and shell launchers load the selected dependency generation at
boot, retain the custom-home default, and ignore foreign Python paths.
Both installer stages reuse their bootstrap interpreter for publication.
PowerShell passes the resolved home to child processes.

Verified: 57 focused tests passed with one POSIX host skip. Real Windows
launchers, generated shell scripts, and both stage callers ran against
temporary trees. No full cold dependency install or native POSIX install
was run. The user-PATH edge is stubbed in the PowerShell test.
2026-09-10 01:58:34 -04:00
ethernet
3991d4e8e6 fix(install): report every stage outcome once
Explicit failures exited before the dispatcher could emit its result.
Unchecked writes could instead reach the success log. Run each stage
in an errexit subshell and emit one EXIT result with the actual status.
Escape JSON text before reporting paths or diagnostic messages.

Accept the desktop's home argument, derive the default install path
from it, and export it to child processes. Explicit install paths win.

Verified actual Bash stage calls over disposable repositories and
failed writes, plus the real CLI's argument and platform boundaries.
No complete POSIX installation or native package acceptance is claimed.
2026-09-09 19:49:45 -04:00
ethernet
712734436e fix(pm): make bootstrap and bundle ownership explicit
Finish bootstrap uv before PM replaces its store entry. Keep failure
receipts stdlib-only and align the cryptography requirement and override
with the locked version.

Let bundle builders declare launch paths and update ownership. Remove
payload discovery, Store probing, and the unused develop command.
Derive Nix Python from the PM lock and share its provenance stamp.

Document setup, activation, optional dependencies, and distribution
ownership. Targeted Windows tests, relocated runtime launches, Electron
bundling, and bilingual docs builds pass. Native Nix and signed-package
acceptance remain CI gates.
2026-09-08 00:24:51 -04:00
ethernet
cd0f97f833 feat(python): pin bundled runtime to 3.14 everywhere (pm, termux lane, CI, installers)
pm python node: 3.14.7+20260901 (freshest python-build-standalone 3.14
build) for the 6 desktop targets; the bionic row moves from the third-party
TUR python3.11 deb to the official termux-main python_3.14.6-1 deb (which
lags PBS by one patch — pinned manually, documented). All 7 digests fetched
from the live sources (PBS release API + termux-main Packages index).
pm/packages.py: main_bin_rel python3.14, deb_package python, bionic fetch
constant, latest_versions guards bionic (no PBS build exists).

termux lane: PYTHON_ABI cp311->cp314, python3.11->python3.14 paths,
libpython3.11.so->3.14, TARGET_ENV 3.11.15->3.14.6 AND sys_platform
linux->android (CPython 3.13+ reports 'android', docs-verified) — linux-
gated markers no longer admit the termux target. runtime_libs.json needs no
change: every python 3.14.6-1 Depends is already staged.

CI: python-version/--python 3.11->3.14 across all 11 workflows incl. the
uv lockfile-check lane. Installers derive the minor from the lock already;
fallbacks bumped. Sandbox images nikolaik/python-nodejs:python3.11-nodejs20
-> python3.14-nodejs22 (tag exists). runtime_repair fall-forward cap now
tracks the <3.15 requires-python window. Docs/README python version claims
updated.
2026-09-07 14:19:18 -04:00
ethernet
92686159d1 fix(pm): integrate audited runtime and lifecycle repairs
Prepare dependency generations before selecting them. Keep shipped tool
bytes separate from writable additions, and store facts beside their entries.
Validate proposed plugin sets before config publication. Restore the previous
config if the facts write fails.

Consolidate duplicate updater, backup, setup, and voice helpers. Repair
launcher selection, dependency consumers, download ownership, update feeds,
and native Windows process and file handling.

Verification: 206 changed/prior-failing Python files reported 4630 passed,
one failed, and 330 skipped. Fix the remaining Hindsight fixture boundary.
The final targeted rerun reported 234 passed and two skipped. The store
review regression batch reported 83 passed and one skipped. Desktop
TypeScript checks, 56 selected Electron tests, 24 release tests, and the
removed-import/compatibility guards passed.

This is an integration checkpoint, not full audit acceptance. The complete
Python suite has not run on this fixed tree. Crash-atomic plugin publication,
generation cleanup, receipt correlation, and packaged lifecycle acceptance
remain open in docs/pm-audit-status.md.
2026-09-05 22:36:48 -04:00
Teknium
95d4265602 fix(install): provision supported Python from TUR on Termux and update docs 2026-09-01 11:25:10 -07:00
gustbr
8581120011 fix(install): enforce Termux Python upper bound 2026-09-01 11:25:10 -07:00
ethernet
a9be133aea merge: local-models (upstream/feat/local-models) onto the pm-clean stack 2026-08-31 18:00:49 -04:00
ethernet
3d12e86ef1 feat(pm): unified package manager — pm store foundation
Introduce the pm store: a unified, hash-verified package store that
replaces lazy_deps and the old installer's ad-hoc tool downloads.
Store tools are provisioned on PATH (ffmpeg, node/npm via pinned uv),
with a resumable 8-way downloader, verify() returning failure reasons,
and adopt() made EPERM-safe. chromium ships in the payload for every
target. The 3600-line install.sh is replaced by a staged bootstrapper
(heavy deps are pm's job after this); setup-hermes.sh, Dockerfile and
nix pin tables are rewired onto the store. Old install-script tests,
lazy_deps/managed_uv/build_info, and the ps1/bash installer test
batteries are removed with the machinery they tested.

Rebuilt from ethie/pm onto upstream/main (ac6c8028e0) after the
utf-8-sig sweep. 16 hot files (main also churned them) hand-merged:
platform adapters, main.py, electron/main.ts, tui_gateway/server.py,
cua_backend, installer-tests workflow, install.sh (full rewrite),
setup-hermes.sh, plugins doc.
2026-08-31 18:00:48 -04:00
semao0
39540a03fc fix(install): never adopt a pre-release Node.js build
install_node() picks the newest tarball out of
nodejs.org/dist/latest-v${NODE_VERSION}.x/ and installs it without ever asking
whether the binary inside is usable. That index currently serves
node-v26.8.0-<os>-<arch>.tar.xz -- a final-looking filename -- whose binary
reports v26.8.0-alpha.0.0.0. Node publishes the headers tarball named by
process.release.headersUrl only for final releases, so node-gyp cannot compile
against that build and every native module fails to install.

Probe the extracted tree before it replaces anything on disk, and fall back to
an older release line when the probe rejects it, instead of leaving the install
with an unbuildable runtime. Mirror the guard in node-bootstrap.sh, and let
_managed_node_tree_outdated() treat a pre-release tree as outdated so an
already-broken install heals itself -- the existing heal only fires below the
target major, and a pre-release sits above it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GrEaXSjvFoBXKxAHTjnUbS
2026-08-31 10:53:18 -07:00
xxxigm
cffc8bb2a4 fix(install): stop a CLI install from building the desktop's node-pty
The browser-tools step ran a bare `npm install` at the repo root, which
resolves the root package.json's `apps/*` workspace glob. That materializes
apps/desktop and with it node-pty, which ships no Linux prebuild and falls
back to `node-gyp rebuild` — so the installer needs make/gcc on a machine
that will never launch Electron or a PTY addon. Since #85297 made a failed
npm install fatal, a host without a C toolchain (a stock CentOS/RHEL box,
for instance) cannot complete a CLI-only install at all; it just reports
"npm install failed or timed out".

Name the workspaces the install actually needs instead. ui-tui and web are
selected when present, with --include-workspace-root so the root's shared
ESLint devDependencies are not pruned by the scoped install — the same
closure `hermes update` already installs. A checkout with neither workspace
falls back to a root-only install, since npm fails hard on a workspace it
cannot find. Desktop dependencies keep coming from install_desktop(), which
is only reachable via --include-desktop.

Against a pristine tree the unscoped install reifies 1362 packages including
node-pty 1.1.0; the scoped one reifies 582 with no native desktop addon.

A fork force-push can 404 the compare API used by detect-changes, which
fail-opens with ci_review=true and blocks the PR on a ci-reviewed label
the install change does not need. Recover the file list from the pull
request files endpoint before that fail-open.
2026-08-31 10:00:55 -07:00
liuhao1024
cb89872c28 fix(install): clean up a broken managed Node and guard the termux probe
AI-review follow-up on #87467:
- On probe failure, remove the extracted ~/.hermes/node tree and the
  node/npm/npx bin links so later installer steps and retry runs start
  clean instead of resolving node to a binary that cannot start.
- The termux pkg branch had the same silent-success class: an empty
  version probe logged success and set HAS_NODE=true. Degrade with the
  binary's own error instead.
2026-08-31 10:00:46 -07:00
liuhao1024
8f8351aff3 fix(install): report a managed Node that cannot start, and preinstall libatomic1
install_node's post-install probe was
installed_ver=$(node --version 2>/dev/null) under set -e: when the
downloaded Node exists but cannot start (Node 26 linux-x64 builds link
libatomic.so.1, missing on minimal Debian/Ubuntu), the assignment
aborted the whole installer at exit 127 with the loader's explanation
discarded — installs died mid-sentence with no output at all (#87460).

- Probe now captures stderr and degrades with log_error carrying the
  loader message plus the libatomic1 hint instead of aborting.
- Debian/Ubuntu installs preinstall libatomic1 (best-effort, mirroring
  the existing apt idiom) so the common case just works.
- Termux branch's same-shaped probe gets a || true guard.
Fixes #87460
2026-08-31 10:00:46 -07:00
liuhao1024
0aa6b44917 fix(install): defer the partial clone's checkout so the throttle fallback engages
Review feedback on this PR: without --no-checkout, the blob fetch runs
inside git clone's own checkout step, so when the repo-scoped 429 hits
that fetch the whole clone exits non-zero, the else branch removes the
directory, and the fallback degrades to one more failed clone under
exactly the condition it exists for.

- Clone with --no-checkout (commits+trees only — small, passes the
  throttle); the blobs are then fetched by a separate 'git reset --hard
  HEAD' the retry can actually wrap. Verified on a local file://
  filtering remote: the no-checkout clone materializes nothing and the
  reset alone produces the full working tree.
- Fail closed: both reset attempts failing now removes the checkout and
  reports 'Failed to clone repository' instead of the previous '|| true'
  + unconditional clone_ok=true handing the installer a half-materialized
  tree printed as a success.
- The reset runs under a subshell cd so a failed materialization never
  leaves the shell in a deleted cwd, and the direct-retry loop bound now
  derives from $max_attempts (seq) instead of a hardcoded 1 2 3 4 that
  could drift from the reported attempt count.
2026-08-31 10:00:38 -07:00
liuhao1024
11afd07f16 fix(install): retry the HTTPS clone and degrade past repo-scoped 429s
GitHub throttles packfile generation for this repository with
repo-scoped HTTP 429s that are not client IP rate limits: an
anonymous clone of a small repo succeeds and the API quota is
untouched, but the single big pack behind --depth 1 dies
mid-transfer with 'RPC failed; HTTP 429 / expected packfile'. The
fresh-install clone path had no retry and no fallback, so a clean
machine exited 1 at the download stage and left a half-populated
install directory (same throttle as the update path in #89287).

Retry the HTTPS clone with linear backoff, removing the partial
clone between attempts; when every direct attempt fails, degrade
to a blobless partial clone and materialize the working tree with
a hard reset — many small packs instead of one big one, which is
what gets past the throttle. SSH-first ordering, the existing
installation update branch, and the commit-pin flow are unchanged.
2026-08-31 10:00:38 -07:00
nftpoetrist
0610291b5a fix(prompt): sync DEFAULT_SOUL_MD with the #95681 identity rewrite
DEFAULT_AGENT_IDENTITY was rewritten in agent/prompt_builder.py (behavior
spec, exploration-thrift line deliberately removed) but the actual seed
written to disk on first run, hermes_cli/default_soul.py's
DEFAULT_SOUL_MD, was never updated. ensure_hermes_home() writes
DEFAULT_SOUL_MD into SOUL.md on every fresh install before the agent's
first turn, so virtually all real users end up as "SOUL.md users" seeded
with the pre-rewrite text -- including the exact "targeted and efficient
exploration" line the rewrite explicitly banned -- while the new
DEFAULT_AGENT_IDENTITY fallback essentially never serves the "fresh
install" audience its own PR body named as the target.

- DEFAULT_SOUL_MD now matches DEFAULT_AGENT_IDENTITY exactly.
- The pre-rewrite text is added to _LEGACY_TEMPLATE_SOULS so installs
  already seeded with it self-heal via the existing upgrade-in-place
  mechanism (same guarantee as the comment-only scaffold entries: the
  string carries zero user intent, so it's safe to replace).
- Synced the other places install.sh's own comment says "MUST match
  DEFAULT_SOUL_MD": scripts/install.sh, scripts/install.ps1,
  docker/SOUL.md, and the docs/i18n pages that quote the fallback text
  verbatim.
2026-08-29 18:10:47 -07:00
Mohamed HAMMANE
6da0ae1cf5 fix(install): preserve project config for locked uv sync (#82446) 2026-08-28 12:36:15 -07:00
Teknium
15eb5caf7a fix(install): Node 24.0–24.10 no longer passes the gates only to die at npm EBADENGINE
The locked dependency tree now carries @babel/* 8.x, which requires
node ^22.18.0 || >=24.11.0. Our engines.node arm said ^24.0.0 and the
installer gates (node_satisfies_build / Test-NodeVersionOk) accepted any
Node 24 — so a system Node 24.0–24.10 cleared every gate we own and then
failed 'npm install' with EBADENGINE under engine-strict=true.

- Raise the 24 arm to ^24.11.0 in root + desktop package.json and the
  package-lock.json mirrors
- Tighten node_satisfies_build (install.sh) and Test-NodeVersionOk
  (install.ps1) to 24.11+; update user-facing wording
- Add invariant tests: every engines.node arm floor must satisfy every
  locked dependency's engines.node, and the installer gates must encode
  the same floors as the manifest — so the next babel-style floor bump
  turns into a CI red instead of a user install outage
- docs: correct stale 'Node.js v22' provisioning claim
2026-08-28 12:20:40 -07:00
Jefferson Nunn
c9fa2bba45 fix(install): tier-0 locked sync no longer trips over UV_NO_CONFIG
The installer exports UV_NO_CONFIG=1 at script start (sudo -u hygiene,
#21269). That export also hides the project's own [tool.uv] policy —
exclude-newer and its package exemptions — from uv. The resolver then
runs under a different policy than uv.lock was resolved under, and
--locked makes that mismatch fatal:

  error: The lockfile at `uv.lock` needs to be updated, but `--locked` was provided.

Every fresh install hit this and fell through to the non-hash-verified
PyPI fallback tiers, defeating the point of Tier 0. Strip the variable
for this one invocation only; it stays exported for every other uv call.
Runtime code already strips UV_NO_CONFIG before its own locked syncs for
the same reason (hermes_cli/managed_uv.py).
2026-08-28 12:20:40 -07:00
fangliquanflq
4d08f51581 fix(install): reject prerelease Node toolchains 2026-08-28 05:12:33 -07:00
fangliquanflq
bb06a8d414 fix(dashboard): harden Node engine alignment checks 2026-08-28 05:12:33 -07:00
fangliquan
56409af4ad fix(scripts): report unsupported Node lines accurately 2026-08-28 05:12:33 -07:00
fangliquan
4a7df1d070 fix(dashboard): align supported Node engine lines 2026-08-28 05:12:33 -07:00
Teknium
a6bec08f35 fix(install): actually invoke check_cxx_compiler in both install stages
Salvage follow-up for #88993: the preflight was defined but never
called from the prerequisites stage or the full-install path, so the
Fedora node-gyp failure (#93063) would still occur. Wire it in after
check_node in both sequences.
2026-08-23 18:25:42 -07:00
osgeek90
60cbc70430 installer: check for a C++ compiler before building native Node modules
npm install inside install_node_deps() builds native addons (e.g. node-pty) via node-gyp, which needs a C/C++ compiler. That was never checked, so a missing g++ only surfaced as a generic "npm install failed or timed out" deep inside npm's own output — and because install_node_deps failing short-circuits the rest of main() via `|| return`, users end up with no `hermes` command and no clue why.
2026-08-23 18:25:42 -07:00
Francesco Bonacci
81af2ef013 fix(computer-use): reconcile existing cua-driver installs 2026-08-16 11:34:40 -07:00
RelaxJonh
eac1f65340 fix(install): validate --commit SHA and fail hard on fetch/checkout errors (#87268)
Three problems with install.sh --commit:

1. No validation: non-hex or too-short arguments passed through to git,
   producing misleading errors.

2. Fetch failure swallowed by || true: abbreviated SHAs are refused by
   GitHub's server ("couldn't find remote ref"), but the error was
   silently ignored.

3. Checkout failure not checked: git checkout --detach with a missing
   object produces a misleading "does not take a path argument" error
   and the install continues unpinned, exiting 0.

Fix:
- Validate --commit is a 7-40 hex string up front
- Remove || true from fetch; fail with actionable message directing
  users to full 40-char SHAs
- Check git checkout --detach result and fail hard on error

Fixes #87268
2026-08-16 01:45:10 -07:00
RelaxJonh
f887819421 fix(install): capture npm output on failure for diagnosable errors (#87340)
Both install-blocking npm install call sites (browser tools and TUI) ran
with --silent and no output capture, so failures printed only a generic
error message with no npm diagnostics.

Apply the same pattern used by the camofox install path: redirect npm
output to a temp file and replay it on failure, so users can see the
actual error (EBADENGINE, ETARGET, network timeout, registry 5xx, etc.).

Fixes #87340
2026-08-16 01:44:45 -07:00
Teknium
bc36d7d6c8 fix(deps): exempt no-upload-date and exact-pinned packages from exclude-newer bricking
The relative exclude-newer = "14 days" cutoff bricks installs whenever the
resolver cannot see (or accept) a package's upload date:

- defusedxml / python-olm / unpaddedbase64 (#80387, #79434): ancient frozen
  releases (2021-2023) whose upload dates are often absent from mirror
  indexes and stale uv HTTP caches. uv then filters them entirely
  ("there are no versions of defusedxml"), breaking [youtube]/[wecom]/
  [matrix] resolution and daily `uv sync --locked` runs.

- setuptools / pillow / mcp (#78227, #75992, #76020): exact-pinned deps.
  When the pinned version's upload date is invisible, the resolver filters
  the ONLY acceptable candidate — setuptools==83.0.0 in
  [build-system].requires meant the project could not even be built from a
  git checkout on released v0.20.0. Exempting an exact pin costs nothing:
  the version cannot float without a reviewed pin bump.

Changes:
- pyproject.toml: add all six to the existing exclude-newer-package
  whitelist, with rationale comments per class.
- uv.lock: regenerated; diff is the whitelist metadata only (verified
  zero version drift, still 249 packages).
- tests/test_packaging_metadata.py: new standing guard
  test_build_system_requires_exempt_from_exclude_newer — every
  [build-system].requires package must be whitelisted while a relative
  exclude-newer cutoff is configured. Verified both directions (fails
  when setuptools is removed from the whitelist).
- scripts/install.sh: fix the stale tier-name comparison ("all (with
  RL/matrix extras)" vs actual "all") that mislabeled every successful
  Tier-1 install as a fallback-tier install (#79434 bonus finding).

Verification: uv lock --check green on uv 0.11.19 and 0.12.5;
uv sync --extra all --locked green; uv pip install -e '.[all]' resolves;
whitelist mechanism A/B-proven on a minimal project (unsatisfiable ->
resolves; build-requires variant: uv build fails -> succeeds).

Reported-by: MichaelClawHub (#80387), liujianqiu (#79434), maxonliu (#78227)
2026-08-14 21:50:48 -07:00
Christopher
9b3823d08b fix(install): provision Node 26 so managed npm satisfies engines
Node 22 ships npm 11.16.0, which engines.npm rejects (11.10–11.16
ignore min-release-age-exclude). Fresh Hermes-managed installs then
fail npm ci with EBADENGINE. Node 26 ships 11.17.0.
2026-08-14 21:48:14 -07:00
Teknium
20e5d51bea fix(browser): managed-first browser-use CLI resolution
Everything Browser Use is now managed by Hermes: the canonical binary
is the one install_cli() provisions into HERMES_HOME/bin, and every
resolution and provisioning site prefers it.

- _find_cli(): probe order flipped to managed bin -> PATH ->
  user-level tool dir (then uvx across the same order). A user's own
  uv tool install can no longer shadow the Hermes-managed copy with a
  drifted version; side installs only matter when we have nothing.
- install_cli(): a browser-use on PATH no longer short-circuits the
  install — only the managed copy does, so selecting any backend
  provisions the copy Hermes controls and updates.
- _ensure_browser_use_cli() (hermes tools): drops its own PATH check
  and always delegates to install_cli(), the single owner of the
  managed-copy policy.
- install.sh / install.ps1: same short-circuit fix — only
  HERMES_HOME/bin/browser-use counts as installed.

Follow-up to #86240 and #86320: with every non-Camofox backend
selection installing the CLI, managed-first closes the remaining
version-drift/shadowing class instead of guarding single sites.

Tests updated to pin the new contract: managed beats PATH and
user-local; PATH install does not satisfy install_cli; helper always
delegates. E2E-verified precedence chain with real files and a real
degraded-PATH install attempt.
2026-08-14 13:51:07 -07:00
brooklyn!
6a198f8a12 fix(install): a failed Node dependency install now fails the install instead of printing success (#85537)
* fix(install): fail when Node dependencies cannot install (#85297)

The POSIX installer converted root and TUI npm failures into warnings, then
printed a dependency-success message and reached the installation-complete
banner with a zero exit status. This left consumers with no usable
node_modules while reporting success.

Treat both required npm installs as fatal: log an error, restore tracked
lockfile churn, return status 1, and propagate the failure from the monolithic
and node-deps stage callers. Successful installs, Termux and missing-Node
skips, missing-manifest skips, and optional Playwright/Browser Use/Computer
Use best-effort behavior remain unchanged. The fix is limited to the POSIX
installer; the PowerShell installer is outside this issue's scope.

Focused and adjacent installer tests passed (32), with bash syntax,
py_compile, and diff checks clean. The broader installer family had 90 passes,
one unrelated pre-existing failure, and two skips; the full suite was
environment-limited by missing dependencies. CodeRabbit, iterative deep
security/compatibility reviews, and final confidence security/compatibility
reviews were clean against the final diff.

Fixes #85297

* fix(install): require npm alongside node in check_node (#77003)

A stray `node` symlink without a sibling `npm` (leftover from a node
version manager) made check_node report "Node.js found"; every later
npm install then failed and the desktop build died with an opaque
"Node.js / npm unavailable". Node now only counts as found when npm
resolves on the same PATH, with an explicit "stray node symlink?" branch
that falls through to the Hermes-managed Node (which bundles npm).

The overlapping success-log honesty half of the original PR is subsumed
by the previous commit, which makes a failed npm install fatal rather
than conditionally-logged; the behavioral tests there cover it, so this
commit keeps only the check_node PATH-gate assertions.

Fixes #77003.

Co-authored-by: criptogus <criptogus@users.noreply.github.com>

---------

Co-authored-by: Eugeniusz Gilewski <egilewski@egilewski.com>
Co-authored-by: CriptoGus <128640021+criptogus@users.noreply.github.com>
Co-authored-by: criptogus <criptogus@users.noreply.github.com>
2026-08-13 18:39:54 +00:00
Teknium
7060ac7bed feat(computer-use): provision cua-driver at install time and on toolset enable
Choosing Computer Use should be a config flip, not a hunt for
'hermes computer-use install'. Three provisioning rungs:

- install.sh / install.ps1 pre-install cua-driver (best-effort,
  non-fatal, time-boxed at 660s above the upstream installer's 600s
  lock window; --skip-computer-use / -SkipComputerUse to opt out;
  Termux and unwritable-/Applications skipped cleanly)
- PUT /api/tools/toolsets/{name} (dashboard + desktop toggle) spawns
  the background 'hermes tools post-setup cua_driver' action when the
  toolset is enabled while the binary is missing — previously the
  toggle 'saved' but the tool never appeared in the schema because
  check_computer_use_requirements() couldn't find the binary
- hermes tools interactive flow already installed via
  _toolset_needs_configuration_prompt/_POST_SETUP_INSTALLED (unchanged)

Docs: computer-use.md enabling section rewritten around the new flow;
installation.md documents --skip-computer-use.
2026-08-13 02:44:48 -07:00