# Copyright 2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 DISTUTILS_SINGLE_IMPL=1 DISTUTILS_USE_PEP517=setuptools # Upstream caps requires-python at <3.14 only because PyPI lacked cp314 # wheels for its Rust-backed transitives; Portage builds those from source. PYTHON_COMPAT=( python3_{12..14} ) inherit distutils-r1 optfeature # Git tags are dated (vYYYY.M.D); pyproject carries the SemVer that is PV. # Both move together on every release: bump MY_TAG with PV. MY_TAG="2026.9.24" DESCRIPTION="Self-improving AI agent CLI by Nous Research" HOMEPAGE=" https://hermes-agent.nousresearch.com/ https://github.com/NousResearch/hermes-agent/ " # PyPI stopped at 0.19.0: upstream retired the pip channel. SRC_URI=" https://github.com/NousResearch/hermes-agent/archive/refs/tags/v${MY_TAG}.tar.gz -> ${P}.gh.tar.gz " S="${WORKDIR}/${PN}-${MY_TAG}" LICENSE="MIT" SLOT="0" # ~arm64 is blocked by dev-python/fastapi, which ::gentoo keywords ~amd64 # only. Everything else in the graph, including the Rust extensions, builds # on aarch64; add ~arm64 once fastapi carries it. KEYWORDS="~amd64" # The suite (~85 directories) mixes network, Docker, browser and gateway # tests and expects a uv-built venv; it is not runnable in the sandbox. RESTRICT="test" # Upstream exact-pins every core dependency (supply-chain policy, see the # comment block in pyproject.toml). Here they are floors where Gentoo is at # or ahead, bare where Gentoo is behind and the API is stable. # - openai: upstream pins 2.24.0; this overlay carries 3.x, which still # accepts the legacy httpx client hermes injects (runtime escape hatch # documented in openai's httpx2.md). # - httpx[socks] is socksio. httpx is in ::gentoo package.deprecated # (upstream stopped taking bug reports), but hermes imports it directly # throughout; there is no drop-in until upstream moves to httpx2. # - nemo-relay is left out: it is imported lazily, only by the # `hermes migrate relay` subcommand, and needs aws-lc-sys plus protoc. # - git and ripgrep are on the default tool path (nix/hermes-agent.nix). RDEPEND=" dev-vcs/git sys-apps/ripgrep $(python_gen_cond_dep ' dev-python/certifi[${PYTHON_USEDEP}] >=dev-python/croniter-6.0.0[${PYTHON_USEDEP}] >=dev-python/cryptography-50.0.0[${PYTHON_USEDEP}] >=dev-python/fastapi-0.104.0[${PYTHON_USEDEP}] >=dev-python/fire-0.7.1[${PYTHON_USEDEP}] >=dev-python/firecrawl-anydoc-0.2.4[${PYTHON_USEDEP}] >=dev-python/httptools-0.6.3[${PYTHON_USEDEP}] >=dev-python/httpx-0.28.1[${PYTHON_USEDEP}] >=dev-python/jinja2-3.1.6[${PYTHON_USEDEP}] >=dev-python/markdown-3.10.2[${PYTHON_USEDEP}] dev-python/openai[${PYTHON_USEDEP}] >=dev-python/packaging-26.0[${PYTHON_USEDEP}] >=dev-python/pathspec-1.1.1[${PYTHON_USEDEP}] >=dev-python/pillow-12.3.0[${PYTHON_USEDEP}] >=dev-python/pillow-heif-1.4.0[${PYTHON_USEDEP}] >=dev-python/prompt-toolkit-3.0.52[${PYTHON_USEDEP}] >=dev-python/psutil-7.2.2[${PYTHON_USEDEP}] >=dev-python/ptyprocess-0.7.0[${PYTHON_USEDEP}] >=dev-python/pydantic-2.13.4[${PYTHON_USEDEP}] >=dev-python/pyjwt-2.13.0[${PYTHON_USEDEP}] >=dev-python/python-dotenv-1.2.2[${PYTHON_USEDEP}] >=dev-python/python-multipart-0.0.9[${PYTHON_USEDEP}] >=dev-python/pyyaml-6.0.3[${PYTHON_USEDEP}] >=dev-python/requests-2.33.0[${PYTHON_USEDEP}] >=dev-python/rich-14.3.3[${PYTHON_USEDEP}] >=dev-python/ruamel-yaml-0.18.17[${PYTHON_USEDEP}] >=dev-python/snowballstemmer-3.1.1[${PYTHON_USEDEP}] dev-python/socksio[${PYTHON_USEDEP}] >=dev-python/tenacity-9.1.4[${PYTHON_USEDEP}] >=dev-python/urllib3-2.7.0[${PYTHON_USEDEP}] >=dev-python/uvicorn-0.31.0[${PYTHON_USEDEP}] >=dev-python/uvloop-0.15.1[${PYTHON_USEDEP}] >=dev-python/watchfiles-0.20[${PYTHON_USEDEP}] >=dev-python/websockets-15.0.1[${PYTHON_USEDEP}] ') " # setup.py refuses to build a wheel unless HERMES_NIX_BUILD=1: upstream # retired the pip/PyPI channels and only lets its uv2nix derivation build # one. Portage is the same kind of sealed, pinned build, so opt in. export HERMES_NIX_BUILD=1 src_install() { distutils-r1_src_install # The wheel puts ~50 top-level modules into site-packages, many with # generic names (agent, cli, cron, gateway, plugins, providers, tools, # utils). Move them into a private venv that still sees the system # site-packages for its dependencies -- the layout upstream's own # uv2nix build uses. sys.executable then points into the venv, so the # 27 places that respawn `sys.executable -m hermes_cli...` keep working, # and nothing leaks onto PYTHONPATH of the commands the agent runs. local venv=/usr/lib/${PN} local vsite=${venv}/lib/${EPYTHON}/site-packages dodir "${vsite}" "${venv}/bin" mv "${D}$(python_get_sitedir)"/* "${ED}${vsite}/" || die rmdir "${D}$(python_get_sitedir)" || die find "${ED}${vsite}" -name __pycache__ -type d -prune -exec rm -r {} + || die python_optimize "${ED}${vsite}" dosym -r "/usr/bin/${EPYTHON}" "${venv}/bin/python" cat > "${ED}${venv}/pyvenv.cfg" <<-EOF || die home = ${EPREFIX}/usr/bin include-system-site-packages = true version = $("${EPYTHON}" -c 'import platform; print(platform.python_version())') EOF # Assets the wheel omits; resolved at runtime via the HERMES_* overrides # below (hermes_constants.py, mirroring nix/hermes-agent.nix). # HERMES_WEB_DIST and HERMES_TUI_DIR stay unset: they point at Node # bundles (dashboard, TUI) this ebuild does not build. # cp, not doins: 39 skill/plugin scripts are 0755 and are exec'd by the # agent; doins -r would flatten them to 0644. dodir /usr/share/${PN} cp -dR --preserve=mode skills optional-skills plugins locales \ optional-mcps "${ED}/usr/share/${PN}/" || die # Entry points: the generated /usr/bin scripts are replaced by venv # scripts plus shell wrappers that set the distribution policy: # - HERMES_DISABLE_LAZY_INSTALLS=1: tools/lazy_deps.py otherwise runs # `pip install` / `uv pip install` whenever a tool needs a missing # extra (the venv being root-owned is a second barrier). # - TIRITH_BIN: with the default value "tirith", tools/tirith_security.py # downloads a binary from GitHub into ~/.hermes/bin and executes it. # An explicit path is authoritative and never auto-downloaded. # Each is a default (${VAR:-...}), so an exported value still wins. local s mod for s in hermes:hermes_cli.main hermes-agent:agent.legacy_cli \ hermes-acp:acp_adapter.entry; do mod=${s#*:} s=${s%%:*} rm "${ED}/usr/bin/${s}" || die cat > "${ED}${venv}/bin/${s}" <<-EOF || die #!${EPREFIX}${venv}/bin/python import sys from ${mod} import main sys.exit(main()) EOF fperms +x "${venv}/bin/${s}" newbin - "${s}" <<-EOF #!/bin/sh export HERMES_BUNDLED_SKILLS="\${HERMES_BUNDLED_SKILLS:-${EPREFIX}/usr/share/${PN}/skills}" export HERMES_OPTIONAL_SKILLS="\${HERMES_OPTIONAL_SKILLS:-${EPREFIX}/usr/share/${PN}/optional-skills}" export HERMES_BUNDLED_PLUGINS="\${HERMES_BUNDLED_PLUGINS:-${EPREFIX}/usr/share/${PN}/plugins}" export HERMES_BUNDLED_LOCALES="\${HERMES_BUNDLED_LOCALES:-${EPREFIX}/usr/share/${PN}/locales}" export HERMES_OPTIONAL_MCPS="\${HERMES_OPTIONAL_MCPS:-${EPREFIX}/usr/share/${PN}/optional-mcps}" export HERMES_PYTHON="\${HERMES_PYTHON:-${EPREFIX}${venv}/bin/python}" export HERMES_BIN="\${HERMES_BIN:-${EPREFIX}/usr/bin/hermes}" export HERMES_DISABLE_LAZY_INSTALLS="\${HERMES_DISABLE_LAZY_INSTALLS:-1}" export TIRITH_BIN="\${TIRITH_BIN:-${EPREFIX}/usr/bin/tirith}" exec "${EPREFIX}${venv}/bin/${s}" "\$@" EOF done dodoc README.md SECURITY.md cli-config.yaml.example } pkg_postinst() { ewarn "hermes-agent runs shell commands on this host by default" ewarn "(terminal.backend: local). Upstream's SECURITY.md is explicit that" ewarn "the approval prompt is not a security boundary: only OS isolation" ewarn "is. Never run it as root; prefer a container/ssh terminal backend" ewarn "or a sandbox such as sys-apps/ai-jail or sys-apps/firejail." elog elog "Runtime package installation is disabled by the wrapper" elog "(HERMES_DISABLE_LAZY_INSTALLS=1). Features whose Python extras are" elog "not packaged report themselves unavailable instead of running pip." elog "The browser tool still falls back to 'npx agent-browser', which" elog "fetches code from the npm registry into your home directory." elog elog "Command pre-scanning uses tirith from ${EPREFIX}/usr/bin/tirith and" elog "is skipped (fail-open) when it is absent; the binary is never" elog "downloaded automatically." optfeature "audio/video tools" media-video/ffmpeg optfeature "the ssh terminal backend" virtual/openssh optfeature "the browser tool (npx agent-browser)" net-libs/nodejs optfeature "clipboard access on Wayland" gui-apps/wl-clipboard optfeature "clipboard access on X11" x11-misc/xclip }