mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
* fix(install): use `--extra all` not `--all-extras`; drop lazy-covered extras from [all]
Two coupled fixes for the Windows install hang where uv sync built
python-olm from sdist and failed on missing make.
# Root cause: --all-extras vs --extra all (credit: ethernet)
`uv sync --all-extras` installs every key in [project.optional-
dependencies], bypassing the curated [all] extra entirely. So even
when [all] excluded [matrix], [rl], [yc-bench], etc., the installer
pulled them anyway because they were still defined as extras. On
Windows that meant python-olm (no wheel, needs make to build from
sdist) and the install died there.
The right flag is `--extra all` — install just the [all] extra's
contents, respecting curation. Empirically verified via dry-run:
--all-extras: pulls python-olm, mautrix, ctranslate2, onnxruntime,
atroposlib, tinker, wandb, modal, daytona, vercel,
python-telegram-bot, discord.py, slack-bolt,
dingtalk-stream, lark-oapi, anthropic, boto3,
edge-tts, elevenlabs, exa-py, fal-client, faster-
whisper, firecrawl-py, honcho-ai, parallel-web
--extra all: pulls none of those — just [all]'s curated set
Dockerfile already uses `--extra all` (with comment explaining the
gotcha) — knowledge existed; the gap was install.sh / install.ps1 /
setup-hermes.sh.
Sites fixed: scripts/install.sh L1118, scripts/install.ps1 L809,
setup-hermes.sh L245.
# Companion fix: drop lazy-covered extras from [all]
`tools/lazy_deps.py` already covers anthropic, bedrock, exa,
firecrawl, parallel-web, fal, edge-tts, elevenlabs, modal, daytona,
vercel, all messaging platforms (telegram/discord/slack/matrix/
dingtalk/feishu), honcho, and faster-whisper. They were ALSO in
[all], which defeats the whole point of lazy-install — fresh
installs eager-pulled them and inherited whatever was broken
upstream (the matrix → python-olm → no Windows wheel chain being
the proximate symptom).
[all] now contains only what genuinely can't be lazy-installed:
cron, cli, dev, pty, mcp, homeassistant, sms, acp, google, web,
youtube. Same trim applied to [termux-all]. New regression test
asserts the contract: every extra in LAZY_DEPS must NOT also appear
in [all].
# Companion fix: surface uv progress + errors
setup-hermes.sh's hash-verified path swallowed uv's stderr to a
tempfile, identical to the install.sh bug fixed in PR #24504. Same
fix applied: stream stderr through directly so users see live
progress instead of staring at a frozen prompt.
# Files
- pyproject.toml: trim [all] and [termux-all] to non-lazy extras only.
- scripts/install.sh: --all-extras → --extra all; trim _ALL_EXTRAS /
_PYPI_EXTRAS to match.
- scripts/install.ps1: --all-extras → --extra all; trim $allExtras /
$pypiExtras to match.
- setup-hermes.sh: --all-extras → --extra all; stream stderr.
- tests/test_project_metadata.py: invert matrix-in-[all] assertion;
add lazy-coverage contract test.
- uv.lock: regenerated.
# Validation
5/5 metadata tests pass. 37/37 in update_autostash + tool_token_
estimation. `uv lock --check` passes. Empirical dry-run confirms
`--extra all` excludes python-olm + RL chain on the new lockfile.
* fix(install): parse [all] from pyproject.toml instead of mirroring it
ethernet's review point: the previous patch left two hand-mirrored
copies of [all]'s contents (in install.sh's $_ALL_EXTRAS and
install.ps1's $allExtras). That guarantees future drift the next
time pyproject.toml's [all] changes.
Now both scripts parse pyproject.toml at install time using stdlib
tomllib (Python 3.11+, which the bootstrap step already requires).
Single source of truth. The only purpose of the parsed list is to
build the 'Tier 2: [all] minus broken extras' fallback spec — so we
parse, filter against $brokenExtras, and rebuild the .[a,b,c] spec.
Also: removed redundant fallback tiers.
Before: Tier 1 [all]
Tier 2 [all] minus broken
Tier 3 PyPI-only extras (no git deps)
Tier 4 [web,mcp,cron,cli,messaging,dev]
Tier 5 .
After: Tier 1 [all]
Tier 2 [all] minus broken
Tier 3 .
Tier 3 (PyPI-only) and Tier 4 (dashboard+core) used to dodge the [rl]
git+sdist deps and the [matrix] python-olm build. Both are no longer
in [all] post-2026-05-12 lazy-install migration, so the carve-out
tiers had no remaining content. Tier 4 also referenced [messaging],
which is now lazy-installed — the hardcoded fallback was actually
inconsistent with the new policy.
Defensive fallback: if tomllib parse fails (corrupted pyproject,
unexpected schema), Tier 2 collapses to '.[all]' (same as Tier 1) so
the broken-extras path becomes a no-op rather than crashing.
* fix(gateway): hide Matrix from setup picker on Windows
Matrix is the one messaging platform that has no working install path
on Windows: [matrix] -> mautrix[encryption] -> python-olm, which has
Linux-only wheels and needs make + libolm to build from sdist. The
[all] cleanup in this PR keeps mautrix out of fresh installs, but a
user who picked Matrix in 'hermes setup gateway' would still walk
into the same sdist build failure when the wizard tried to install
the extra.
Hide the option at the picker so users never get the chance to try.
The gate lives in _all_platforms() — single source of truth for the
setup wizard, the curses gateway-config menu, and any future picker.
Adapter loading at runtime is intentionally NOT gated: users who
already have MATRIX_* env vars set (e.g. config copied from a Linux
install) keep working if they somehow have python-olm available.
This is the lowest-friction fix — picker visibility only.
Tests cover linux/darwin/win32 and verify other platforms aren't
collateral damage.
264 lines
12 KiB
TOML
264 lines
12 KiB
TOML
[build-system]
|
|
requires = ["setuptools>=61.0"]
|
|
build-backend = "setuptools.build_meta"
|
|
|
|
[project]
|
|
name = "hermes-agent"
|
|
version = "0.13.0"
|
|
description = "The self-improving AI agent — creates skills from experience, improves them during use, and runs anywhere"
|
|
readme = "README.md"
|
|
requires-python = ">=3.11"
|
|
authors = [{ name = "Nous Research" }]
|
|
license = { text = "MIT" }
|
|
dependencies = [
|
|
# Core — every direct dep is exact-pinned to ==X.Y.Z (no ranges).
|
|
# Rationale: ranges allow PyPI to ship a fresh version of a transitive
|
|
# at any time without a code review on our side. Exact pins mean the
|
|
# only way a new package version reaches a user is via an intentional
|
|
# update on our end (bump the pin in this file, regenerate uv.lock).
|
|
# This was tightened on 2026-05-12 in response to the Mini Shai-Hulud
|
|
# worm hitting mistralai 2.4.6 on PyPI; if that release had been
|
|
# captured by `mistralai>=2.3.0,<3` rather than an exact pin, every
|
|
# install in the hours before the quarantine would have pulled it.
|
|
#
|
|
# When updating: bump the version below AND regenerate uv.lock with
|
|
# `uv lock` so the transitive resolution stays consistent. Don't
|
|
# introduce ranges back without a written justification.
|
|
#
|
|
# Scope rule: only packages used by EVERY hermes session belong here.
|
|
# Anything that's provider-specific (`anthropic`, `firecrawl-py`,
|
|
# `exa-py`, `fal-client`, `edge-tts`, `parallel-web`) belongs in an
|
|
# extra and gets lazy-installed via `tools/lazy_deps.py` when the
|
|
# user picks that backend. Smaller `dependencies` = smaller blast
|
|
# radius for the next supply-chain attack.
|
|
"openai==2.24.0",
|
|
"python-dotenv==1.2.1",
|
|
"fire==0.7.1",
|
|
"httpx[socks]==0.28.1",
|
|
"rich==14.3.3",
|
|
"tenacity==9.1.4",
|
|
"pyyaml==6.0.3",
|
|
"ruamel.yaml==0.18.17",
|
|
"requests==2.33.0", # CVE-2026-25645
|
|
"jinja2==3.1.6",
|
|
"pydantic==2.12.5",
|
|
# Interactive CLI (prompt_toolkit is used directly by cli.py)
|
|
"prompt_toolkit==3.0.52",
|
|
# Cron scheduler (built-in feature — scheduled cron/interval jobs use croniter).
|
|
"croniter==6.0.0",
|
|
# Skills Hub (GitHub App JWT auth — optional, only needed for bot identity)
|
|
"PyJWT[crypto]==2.12.1", # CVE-2026-32597
|
|
# Windows has no IANA tzdata shipped with the OS, so Python's ``zoneinfo``
|
|
# (PEP 615) raises ``ZoneInfoNotFoundError`` for every non-UTC timezone
|
|
# out of the box. ``tzdata`` ships the Olson database as a data package
|
|
# Python resolves automatically. No-op on Linux/macOS (which have
|
|
# /usr/share/zoneinfo). Credits: PR #13182 (@sprmn24).
|
|
"tzdata==2025.3; sys_platform == 'win32'",
|
|
# Cross-platform process / PID management. `psutil` is the canonical
|
|
# answer for "is this PID alive" and process-tree walking across Linux,
|
|
# macOS and Windows. It replaces POSIX-only idioms like `os.kill(pid, 0)`
|
|
# (which is a silent killer on Windows — see CONTRIBUTING.md) and
|
|
# `os.killpg` (which doesn't exist on Windows).
|
|
"psutil==7.2.2",
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
# Native Anthropic provider — only needed when provider=anthropic (not via
|
|
# OpenRouter or other aggregators).
|
|
anthropic = ["anthropic==0.86.0"]
|
|
# Web search backends — each only loaded when the user picks it as their
|
|
# search provider (configured via `hermes tools` or config.yaml).
|
|
exa = ["exa-py==2.10.2"]
|
|
firecrawl = ["firecrawl-py==4.17.0"]
|
|
parallel-web = ["parallel-web==0.4.2"]
|
|
# Image generation backends
|
|
fal = ["fal-client==0.13.1"]
|
|
# Edge TTS — default TTS provider but still optional (users can pick
|
|
# ElevenLabs / OpenAI / MiniMax instead).
|
|
edge-tts = ["edge-tts==7.2.7"]
|
|
modal = ["modal==1.3.4"]
|
|
daytona = ["daytona==0.155.0"]
|
|
vercel = ["vercel==0.5.7"]
|
|
hindsight = ["hindsight-client==0.6.1"]
|
|
dev = ["debugpy==1.8.20", "pytest==9.0.2", "pytest-asyncio==1.3.0", "pytest-xdist==3.8.0", "pytest-split==0.11.0", "mcp==1.26.0", "ty==0.0.21", "ruff==0.15.10"]
|
|
messaging = ["python-telegram-bot[webhooks]==22.6", "discord.py[voice]==2.7.1", "aiohttp==3.13.3", "slack-bolt==1.27.0", "slack-sdk==3.40.1", "qrcode==7.4.2"]
|
|
cron = [] # croniter is now a core dependency; this extra kept for back-compat
|
|
slack = ["slack-bolt==1.27.0", "slack-sdk==3.40.1"]
|
|
matrix = ["mautrix[encryption]==0.21.0", "Markdown==3.10.2", "aiosqlite==0.22.1", "asyncpg==0.31.0", "aiohttp-socks==0.11.0"]
|
|
cli = ["simple-term-menu==1.6.6"]
|
|
tts-premium = ["elevenlabs==1.59.0"]
|
|
voice = [
|
|
# Local STT pulls in wheel-only transitive deps (ctranslate2, onnxruntime),
|
|
# so keep it out of the base install for source-build packagers like Homebrew.
|
|
"faster-whisper==1.2.1",
|
|
"sounddevice==0.5.5",
|
|
"numpy==2.4.3",
|
|
]
|
|
pty = [
|
|
"ptyprocess==0.7.0; sys_platform != 'win32'",
|
|
"pywinpty==2.0.15; sys_platform == 'win32'",
|
|
]
|
|
honcho = ["honcho-ai==2.0.1"]
|
|
mcp = ["mcp==1.26.0"]
|
|
homeassistant = ["aiohttp==3.13.3"]
|
|
sms = ["aiohttp==3.13.3"]
|
|
# Computer use — macOS background desktop control via cua-driver (MCP stdio).
|
|
# The cua-driver binary itself is installed via `hermes tools` post-setup
|
|
# (curl install script); this extra just pins the MCP client used to talk
|
|
# to it, which is already provided by the `mcp` extra.
|
|
computer-use = ["mcp==1.26.0"]
|
|
acp = ["agent-client-protocol==0.9.0"]
|
|
# mistral: extra REMOVED 2026-05-12 — `mistralai` PyPI project quarantined
|
|
# after malicious 2.4.6 release (Mini Shai-Hulud worm). Every version of
|
|
# `mistralai` returns 404 on PyPI right now, so any pin we'd write is
|
|
# unresolvable, which breaks `uv lock --check` in CI.
|
|
#
|
|
# To restore once PyPI un-quarantines:
|
|
# 1. Verify the new release is clean (read the changelog, check Socket
|
|
# advisory page, confirm no malicious code review findings).
|
|
# 2. Add back: mistral = ["mistralai==<verified-version>"]
|
|
# 3. Re-enable Mistral in:
|
|
# - tools/lazy_deps.py (LAZY_DEPS["tts.mistral"], LAZY_DEPS["stt.mistral"])
|
|
# - hermes_cli/tools_config.py (un-hide from provider picker)
|
|
# - hermes_cli/web_server.py (re-add to dashboard STT options)
|
|
# - tools/transcription_tools.py / tools/tts_tool.py (drop disabled stubs)
|
|
# 4. Run `uv lock` to regenerate transitives.
|
|
# 5. Optionally re-add to [all] only after a few days of clean operation.
|
|
bedrock = ["boto3==1.42.89"]
|
|
termux = [
|
|
# Baseline Android / Termux path for reliable fresh installs.
|
|
"python-telegram-bot[webhooks]==22.6",
|
|
"hermes-agent[cron]",
|
|
"hermes-agent[cli]",
|
|
"hermes-agent[pty]",
|
|
"hermes-agent[mcp]",
|
|
"hermes-agent[honcho]",
|
|
"hermes-agent[acp]",
|
|
]
|
|
termux-all = [
|
|
# Best-effort "install all" profile for Termux. Same policy as [all]:
|
|
# only includes extras that aren't covered by `tools/lazy_deps.py`.
|
|
# Backends like telegram/slack/dingtalk/feishu/honcho lazy-install at
|
|
# first use, so they're no longer eager-installed here.
|
|
"hermes-agent[termux]",
|
|
"hermes-agent[google]",
|
|
"hermes-agent[homeassistant]",
|
|
"hermes-agent[sms]",
|
|
"hermes-agent[web]",
|
|
]
|
|
dingtalk = ["dingtalk-stream==0.24.3", "alibabacloud-dingtalk==2.2.42", "qrcode==7.4.2"]
|
|
feishu = ["lark-oapi==1.5.3", "qrcode==7.4.2"]
|
|
google = [
|
|
# Required by the google-workspace skill (Gmail, Calendar, Drive, Contacts,
|
|
# Sheets, Docs). Declared here so packagers (Nix, Homebrew) ship them with
|
|
# the [all] extra and users don't hit runtime `pip install` paths that fail
|
|
# in environments without pip (e.g. Nix-managed Python).
|
|
"google-api-python-client==2.194.0",
|
|
"google-auth-oauthlib==1.3.1",
|
|
"google-auth-httplib2==0.3.1",
|
|
]
|
|
youtube = [
|
|
# Required by skills/media/youtube-content and
|
|
# optional-skills/productivity/memento-flashcards (youtube_quiz.py).
|
|
# Without this declaration uv sync omits the package and both skills fail
|
|
# at first invocation with ModuleNotFoundError (issue #22243).
|
|
"youtube-transcript-api==1.2.4",
|
|
]
|
|
# `hermes dashboard` (localhost SPA + API). Not in core to keep the default install lean.
|
|
web = ["fastapi==0.133.1", "uvicorn[standard]==0.41.0"]
|
|
rl = [
|
|
"atroposlib @ git+https://github.com/NousResearch/atropos.git@c20c85256e5a45ad31edf8b7276e9c5ee1995a30",
|
|
"tinker @ git+https://github.com/thinking-machines-lab/tinker.git@30517b667f18a3dfb7ef33fb56cf686d5820ba2b",
|
|
"fastapi==0.133.1",
|
|
"uvicorn[standard]==0.41.0",
|
|
"wandb==0.25.1",
|
|
]
|
|
yc-bench = ["yc-bench @ git+https://github.com/collinear-ai/yc-bench.git@bfb0c88062450f46341bd9a5298903fc2e952a5c ; python_version >= '3.12'"]
|
|
all = [
|
|
# Policy (2026-05-12): `[all]` includes only extras that genuinely
|
|
# CAN'T be lazy-installed via `tools/lazy_deps.py` — i.e. things every
|
|
# session can use, things needed before the agent loop is alive
|
|
# (terminal/CLI), and skill deps that packagers (Nix, AUR, Homebrew)
|
|
# need in the wheel. Anything an opt-in backend (provider, search,
|
|
# TTS, image, memory, messaging platform, terminal sandbox) needs
|
|
# MUST live exclusively in `LAZY_DEPS` and resolve at first use —
|
|
# otherwise one quarantined PyPI release breaks every fresh install.
|
|
#
|
|
# Removed from [all] on 2026-05-12 (covered by lazy-install):
|
|
# anthropic, exa, firecrawl, parallel-web, fal, edge-tts,
|
|
# modal, daytona, vercel, messaging (telegram/discord/slack),
|
|
# matrix, slack, honcho, voice (faster-whisper),
|
|
# dingtalk, feishu, bedrock, tts-premium (elevenlabs)
|
|
#
|
|
# Why: the matrix extra in particular pulls `mautrix[encryption]`
|
|
# which depends on `python-olm`. python-olm has Linux-only wheels and
|
|
# no native build path on Windows or modern macOS. With matrix in
|
|
# [all], `uv sync --locked` on Windows tried to build it from sdist
|
|
# and failed on `make`. Lazy-install routes that build to first use,
|
|
# where the user is expected to have a toolchain available.
|
|
"hermes-agent[cron]",
|
|
"hermes-agent[cli]",
|
|
"hermes-agent[dev]",
|
|
"hermes-agent[pty]",
|
|
"hermes-agent[mcp]",
|
|
"hermes-agent[homeassistant]",
|
|
"hermes-agent[sms]",
|
|
"hermes-agent[acp]",
|
|
"hermes-agent[google]",
|
|
"hermes-agent[web]",
|
|
"hermes-agent[youtube]",
|
|
]
|
|
|
|
[project.scripts]
|
|
hermes = "hermes_cli.main:main"
|
|
hermes-agent = "run_agent:main"
|
|
hermes-acp = "acp_adapter.entry:main"
|
|
|
|
[tool.setuptools]
|
|
py-modules = ["run_agent", "model_tools", "toolsets", "batch_runner", "trajectory_compressor", "toolset_distributions", "cli", "hermes_bootstrap", "hermes_constants", "hermes_state", "hermes_time", "hermes_logging", "rl_cli", "utils"]
|
|
|
|
[tool.setuptools.package-data]
|
|
hermes_cli = ["web_dist/**/*"]
|
|
gateway = ["assets/**/*"]
|
|
|
|
[tool.setuptools.packages.find]
|
|
include = ["agent", "agent.*", "tools", "tools.*", "hermes_cli", "gateway", "gateway.*", "tui_gateway", "tui_gateway.*", "cron", "acp_adapter", "plugins", "plugins.*", "providers", "providers.*"]
|
|
|
|
[tool.pytest.ini_options]
|
|
testpaths = ["tests"]
|
|
markers = [
|
|
"integration: marks tests requiring external services (API keys, Modal, etc.)",
|
|
]
|
|
addopts = "-m 'not integration' -n auto"
|
|
|
|
[tool.ty.environment]
|
|
python-version = "3.13"
|
|
|
|
[tool.ty.rules]
|
|
unknown-argument = "warn"
|
|
redundant-cast = "ignore"
|
|
|
|
[tool.ty.src]
|
|
exclude = ["tinker-atropos"]
|
|
|
|
[tool.ruff]
|
|
exclude = ["tinker-atropos"]
|
|
preview = true # required for PLW1514 (unspecified-encoding) — preview rule
|
|
|
|
[tool.ruff.lint]
|
|
# All other lints are intentionally disabled (see comment history on this
|
|
# file) while we wrangle typechecks — but PLW1514 is too load-bearing to
|
|
# keep off. Bare open()/read_text()/write_text() in text mode defaults to
|
|
# the system locale encoding on Windows (cp1252 on US-locale installs),
|
|
# which silently corrupts any non-ASCII file content. We had three
|
|
# separate Windows sandbox regressions in one debug session before
|
|
# adding the explicit encoding. This rule keeps new code honest.
|
|
select = ["PLW1514"]
|
|
|
|
[tool.ruff.lint.per-file-ignores]
|
|
# Tests can intentionally exercise locale-encoding edge cases.
|
|
"tests/**" = ["PLW1514"]
|
|
# Skills and plugins are partially user-authored — their own conventions.
|
|
"skills/**" = ["PLW1514"]
|
|
"optional-skills/**" = ["PLW1514"]
|
|
"plugins/**" = ["PLW1514"]
|