mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-08 13:12:08 +00:00
Reconciles the "pluginify everything" refactor (moving provider adapters, platform adapters, and terminal backends out of core into plugins/) with 267 commits of main. main had 515 edits across 635 files; our branch moved 98 files into plugins/. The dangerous class was main editing files at OLD paths that we relocated — handled below. STRUCTURAL DECISIONS - ai-gateway + vercel_sandbox: honored main's PR #33067 deletion. Dropped our orphaned plugins/terminals/vercel/ dir and all 3 stale vercel refs that survived in pyproject (workspace member, [all] entry, uv source). - tools/lazy_deps.py: kept main's version. Our branch deleted it incompletely (WIP) while main has 30 live callers + active maintenance. main's intent won. - agent/agent_runtime_helpers.py: COMBINED — main's atomic swap+rollback snapshot envelope wraps our pluginified registries-based anthropic imports. - agent/credential_pool.py: took our plugin-hook indirection AND ported main's api_key_path_explicit OAuth-masquerade security fix INTO the anthropic credential_pool_hook (it was pluginified from a pre-fix base and would have been silently dropped otherwise). - hermes_cli/model_switch.py, hermes_cli/models.py: took main's logic (cached_provider_model_ids, AI-gateway removal) then re-applied our pluginify import rewrites (agent.bedrock_adapter/anthropic_adapter -> registries namespace lookups) since those modules moved to plugins. - tools/terminal_tool.py: took main's version (vercel_sandbox deletion) then restored our lazy registry resolution for modal + daytona environments (both moved to plugins; main's direct imports would crash at runtime). - nix/checks.nix: kept both — our hermetic-boundary checks + main's messaging-variant discord.py guard. - pyproject.toml: plugin extras as workspace members (ours) + main's new wecom extra; vercel removed throughout. - uv.lock: regenerated with `uv lock` (not hand-merged); 233 packages. VERIFICATION - Zero conflict markers anywhere. - Exhaustive 17-moved-module grep: no dead imports of relocated modules in core. - Import smoke test: all hot core modules import clean. - Targeted tests (21 files incl. credential_pool, anthropic plugin, run_agent, project_metadata): pass. - 3 credential_pool security tests rewired to register the real plugin hook (core tests don't trigger plugin discovery): pass. - Full suite: remaining failures are pre-existing on premerge-oh-god (telegram collection error: setUpModule MagicMock __code__) or environmental (matrix DNS/e2ee-deps, network) — NOT introduced by this merge. See PR notes.
109 lines
4.1 KiB
Python
109 lines
4.1 KiB
Python
"""Regression tests for packaging metadata in pyproject.toml."""
|
|
|
|
from pathlib import Path
|
|
import tomllib
|
|
|
|
|
|
def _load_optional_dependencies():
|
|
pyproject_path = Path(__file__).resolve().parents[1] / "pyproject.toml"
|
|
with pyproject_path.open("rb") as handle:
|
|
project = tomllib.load(handle)["project"]
|
|
return project["optional-dependencies"]
|
|
|
|
|
|
def _load_package_data():
|
|
pyproject_path = Path(__file__).resolve().parents[1] / "pyproject.toml"
|
|
with pyproject_path.open("rb") as handle:
|
|
tool = tomllib.load(handle)["tool"]
|
|
return tool["setuptools"]["package-data"]
|
|
|
|
|
|
def test_matrix_extra_not_in_all():
|
|
"""The [matrix] extra pulls `mautrix[encryption]` -> `python-olm`,
|
|
which has Linux-only wheels and no native build path on Windows or
|
|
modern macOS (archived libolm, C++ errors with Clang 21+).
|
|
|
|
With matrix in [all], `uv sync --locked` on Windows tried to build
|
|
python-olm from sdist and failed on `make`. The [matrix] extra is
|
|
excluded from [all] — users opt in via `pip install hermes-agent[matrix]`.
|
|
"""
|
|
optional_dependencies = _load_optional_dependencies()
|
|
|
|
assert "matrix" in optional_dependencies, (
|
|
"[matrix] extra must still exist for explicit `pip install hermes-agent[matrix]`"
|
|
)
|
|
matrix_in_all = [
|
|
dep for dep in optional_dependencies["all"]
|
|
if "matrix" in dep
|
|
]
|
|
assert not matrix_in_all, (
|
|
f"matrix must not appear in [all] — it's an opt-in plugin. Found: "
|
|
f"{matrix_in_all}"
|
|
)
|
|
|
|
|
|
def test_plugin_extras_are_workspace_member_refs():
|
|
"""Every plugin extra in pyproject.toml should reference a workspace
|
|
member package (e.g. ``hermes-agent-anthropic``), not inline dep specs.
|
|
|
|
This ensures the single source of truth for plugin deps is the plugin's
|
|
own pyproject.toml, not the main package's extras.
|
|
"""
|
|
optional_dependencies = _load_optional_dependencies()
|
|
|
|
# Extras that are known plugin workspace members
|
|
plugin_extras = {
|
|
"anthropic", "bedrock", "azure-identity",
|
|
"discord", "exa", "firecrawl", "parallel",
|
|
"honcho", "hindsight",
|
|
"fal", "tts", "stt",
|
|
"daytona", "modal",
|
|
"telegram", "slack", "dingtalk", "feishu", "matrix",
|
|
"dashboard",
|
|
}
|
|
|
|
for extra in plugin_extras:
|
|
if extra not in optional_dependencies:
|
|
continue
|
|
specs = optional_dependencies[extra]
|
|
for spec in specs:
|
|
assert spec.startswith("hermes-agent-"), (
|
|
f"[{extra}] extra should reference a workspace member, "
|
|
f"not an inline dep spec. Got: {spec}"
|
|
)
|
|
|
|
|
|
def test_dingtalk_extra_includes_qrcode_for_qr_auth():
|
|
"""DingTalk's QR-code device-flow auth (hermes_cli/dingtalk_auth.py)
|
|
needs the qrcode package — verify it's in the dingtalk plugin's deps."""
|
|
pyproject_path = Path(__file__).resolve().parents[1] / "plugins" / "platforms" / "dingtalk" / "pyproject.toml"
|
|
with pyproject_path.open("rb") as handle:
|
|
project = tomllib.load(handle)["project"]
|
|
deps = project.get("dependencies", [])
|
|
assert any("qrcode" in d for d in deps), (
|
|
f"hermes-agent-dingtalk should depend on qrcode. deps: {deps}"
|
|
)
|
|
|
|
|
|
def test_feishu_extra_includes_qrcode_for_qr_login():
|
|
"""Feishu's QR login flow needs the qrcode package — verify it's in
|
|
the feishu plugin's deps."""
|
|
pyproject_path = Path(__file__).resolve().parents[1] / "plugins" / "platforms" / "feishu" / "pyproject.toml"
|
|
with pyproject_path.open("rb") as handle:
|
|
project = tomllib.load(handle)["project"]
|
|
deps = project.get("dependencies", [])
|
|
assert any("qrcode" in d for d in deps), (
|
|
f"hermes-agent-feishu should depend on qrcode. deps: {deps}"
|
|
)
|
|
|
|
|
|
def test_dashboard_plugin_manifests_and_assets_are_packaged():
|
|
"""Bundled dashboard plugins need their manifests and built assets in
|
|
wheel installs so /api/dashboard/plugins can discover them outside a
|
|
source checkout."""
|
|
package_data = _load_package_data()
|
|
plugin_data = package_data["plugins"]
|
|
|
|
assert "*/dashboard/manifest.json" in plugin_data
|
|
assert "*/dashboard/dist/*" in plugin_data
|
|
assert "*/dashboard/dist/**/*" in plugin_data
|