mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
One module, tools/managed_gateway_identity.py, builds the caller identity: - user_agent(): hermes-agent/<base_version> (<surface>; <os_family>), surface from the session platform through the shared-metrics surface map. Every managed-gateway client gets it through managed_gateway_auth_headers; the connector clients add it per attempt. - correlation_headers(call): x-request-id per HTTP attempt plus the conversation, turn and tool call ids, only when the profile has opted in to sending telemetry. The x-nous- prefix is what the gateway strips before a vendor call. - log_connector_outcome(): one line per connector HTTP outcome with the gateway's x-request-id, WARNING on failure, DEBUG on success and on the account route's not-created-yet 404. ConnectorClient assembles an attempt's headers in one method, _attempt_headers, and records the response request id on the raised error. The connect request gains an optional initiator field; a gateway that predates it ignores unknown fields.
81 lines
2.9 KiB
Python
81 lines
2.9 KiB
Python
"""First-party gateway bearer trust, ported from connectors-only 43608084f4."""
|
|
|
|
import logging
|
|
from typing import Callable, Optional
|
|
from urllib.parse import urlsplit
|
|
|
|
from tools.managed_tool_gateway import build_vendor_gateway_url, read_nous_access_token
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def managed_gateway_origin() -> str:
|
|
"""Origin for on-origin managed vendors and media uploads."""
|
|
return build_vendor_gateway_url("tool")
|
|
|
|
|
|
def connector_gateway_origin() -> str:
|
|
"""Separate connector deployment; honors CONNECTOR_GATEWAY_URL."""
|
|
return build_vendor_gateway_url("connector")
|
|
|
|
|
|
def is_managed_nous_gateway_url(
|
|
url: object,
|
|
gateway_builder: Optional[Callable[[str], str]] = None,
|
|
) -> bool:
|
|
"""True when ``url`` is on one of the first-party gateway origins we build.
|
|
|
|
Both first-party surfaces count: the media/on-origin-vendor host
|
|
(:func:`managed_gateway_origin`) and the connectors host
|
|
(:func:`connector_gateway_origin`). Each is compared as an exact
|
|
``(scheme, netloc)`` pair — never as a name or a domain suffix — so
|
|
``evil-connector-gateway.nousresearch.com.attacker.dev`` and an ``http``
|
|
downgrade of a real host both stay outside the set.
|
|
|
|
Anything granting a URL extra trust — our bearer, reading files off disk to
|
|
upload — must gate on this, so an arbitrary URL can never inherit it.
|
|
"""
|
|
if not isinstance(url, str) or not url.strip():
|
|
return False
|
|
|
|
build_origin = gateway_builder or build_vendor_gateway_url
|
|
try:
|
|
expected = {
|
|
urlsplit(build_origin(label))[:2]
|
|
for label in ("tool", "connector")
|
|
}
|
|
actual = urlsplit(url.strip())
|
|
except ValueError:
|
|
return False
|
|
|
|
return bool(actual.scheme) and (actual.scheme, actual.netloc) in expected
|
|
|
|
|
|
def managed_gateway_auth_headers(
|
|
url: object,
|
|
gateway_builder: Optional[Callable[[str], str]] = None,
|
|
token_reader: Optional[Callable[[], Optional[str]]] = None,
|
|
) -> dict:
|
|
"""Live auth headers for a managed gateway URL, or ``{}`` when not managed.
|
|
|
|
Read fresh on every call rather than cached: a Nous access token expires
|
|
within the hour, and a long session would otherwise keep presenting a dead
|
|
bearer. Returns ``{}`` rather than raising when no token is available, so a
|
|
caller can report "sign in" instead of sending an unauthenticated request.
|
|
"""
|
|
if not is_managed_nous_gateway_url(url, gateway_builder):
|
|
return {}
|
|
|
|
resolved_token_reader = token_reader or read_nous_access_token
|
|
try:
|
|
token = resolved_token_reader()
|
|
except Exception as exc: # pragma: no cover — defensive
|
|
logger.debug("Managed gateway token read failed for %s: %s", url, exc)
|
|
return {}
|
|
if not isinstance(token, str) or not token.strip():
|
|
return {}
|
|
|
|
from tools.managed_gateway_identity import user_agent
|
|
|
|
return {"Authorization": f"Bearer {token.strip()}", "User-Agent": user_agent()}
|