Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ modal = ["modal==1.3.4"]
daytona = ["daytona==0.155.0"]
hindsight = ["hindsight-client==0.6.1"]
dev = ["debugpy==1.8.20", "pytest==9.0.2", "pytest-asyncio==1.3.0", "pytest-timeout==2.4.0", "mcp==1.26.0", "starlette==1.0.1", "ty==0.0.21", "ruff==0.15.10", "setuptools==82.0.1"] # starlette: CVE-2026-48710
messaging = ["python-telegram-bot[webhooks]==22.6", "discord.py[voice]==2.7.1", "aiohttp==3.13.3", "brotlicffi==1.2.0.1", "slack-bolt==1.27.0", "slack-sdk==3.40.1", "qrcode==7.4.2"]
messaging = ["python-telegram-bot[webhooks]==22.6", "discord.py[voice]==2.7.1", "aiohttp==3.13.4", "brotlicffi==1.2.0.1", "slack-bolt==1.27.0", "slack-sdk==3.40.1", "qrcode==7.4.2"] # aiohttp: CVE-2026-34513/34518/34519/34520/34525
cron = [] # croniter is now a core dependency; this extra kept for back-compat
slack = ["slack-bolt==1.27.0", "slack-sdk==3.40.1", "aiohttp==3.13.3"]
slack = ["slack-bolt==1.27.0", "slack-sdk==3.40.1", "aiohttp==3.13.4"]
matrix = ["mautrix[encryption]==0.21.0", "Markdown==3.10.2", "aiosqlite==0.22.1", "asyncpg==0.31.0", "aiohttp-socks==0.11.0"]
# WeCom callback-mode adapter β€” parses untrusted XML POST bodies from
# WeCom-controlled callback endpoints, so we use defusedxml (drop-in
Expand Down Expand Up @@ -116,8 +116,8 @@ honcho = ["honcho-ai==2.0.1"]
# extra that exposes a Starlette-backed server surface so pip/uv can't resolve
# a vulnerable pre-1.0.1 transitive. Bump in lockstep with uv.lock.
mcp = ["mcp==1.26.0", "starlette==1.0.1"] # starlette: CVE-2026-48710
homeassistant = ["aiohttp==3.13.3"]
sms = ["aiohttp==3.13.3"]
homeassistant = ["aiohttp==3.13.4"]
sms = ["aiohttp==3.13.4"]
# 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
Expand Down
39 changes: 39 additions & 0 deletions tests/test_project_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ def _load_package_data():
return tool["setuptools"]["package-data"]


def _exact_pins(specs):
pins = {}
for spec in specs:
requirement = spec.split(";", 1)[0].strip()
if "==" not in requirement:
continue
package, version = requirement.split("==", 1)
package = package.split("[", 1)[0].lower().replace("_", "-")
pins[package] = version
return pins


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
Expand Down Expand Up @@ -88,6 +100,33 @@ def test_lazy_installable_extras_excluded_from_all():
)


def test_pyproject_aiohttp_pins_match_lazy_slack_pin():
"""Avoid update/lazy-install churn from conflicting aiohttp pins."""
from tools.lazy_deps import LAZY_DEPS

optional_dependencies = _load_optional_dependencies()
lazy_aiohttp = _exact_pins(LAZY_DEPS["platform.slack"])["aiohttp"]

pyproject_aiohttp_pins = {
extra: pins["aiohttp"]
for extra, specs in optional_dependencies.items()
if "aiohttp" in (pins := _exact_pins(specs))
}

assert pyproject_aiohttp_pins, "expected at least one pyproject extra to pin aiohttp"
mismatches = {
extra: pin
for extra, pin in pyproject_aiohttp_pins.items()
if pin != lazy_aiohttp
}
assert not mismatches, (
"pyproject.toml aiohttp pins must match "
"LAZY_DEPS['platform.slack'] to avoid hermes update downgrading "
"aiohttp before Slack's lazy refresh upgrades it again. "
f"lazy aiohttp=={lazy_aiohttp}; mismatched extras: {mismatches}"
)


def test_messaging_extra_includes_qrcode_for_weixin_setup():
optional_dependencies = _load_optional_dependencies()

Expand Down
Loading
Loading