mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
gateway_runtime_multiplex picked the cold-start owner for a named profile with its own reading of gateway.multiplex_profiles: a retired explicit `false` was treated as an opt-out (spawning a per-profile owner the boot would refuse/ignore), and an explicit `true` selected the root even for a `gateway.standalone: true` secondary that profiles_to_serve never serves, so the client waited on a multiplexer that would not serve it. Export the boot verdict from gateway_multiplex_mode (settled_explicit_flag, used by resolve_multiplex_mode, and host_serves_named_profile) and have implied_host_root / multiplexer_serves_home call it: retired false settles like unset (fall back to the recorded served set, else the host root) and a standalone secondary always gets its own owner. Finding: R1/R2 minor "cold-start multiplex target selection vs policy". (cherry picked from commit e5144060d8a49ef77ba4de5149279c1942dbe235)
64 lines
3.2 KiB
Python
64 lines
3.2 KiB
Python
"""Which daemon a named-profile client must start when nothing serves its home.
|
|
|
|
A ``<root>/profiles/<name>`` home that the default multiplexer serves has no daemon of its own.
|
|
When the multiplexer is down (restart, update, crash) the profile looks ``absent`` and the ensure
|
|
loop would spawn a per-profile gateway for it — a second owner that answers this client and then
|
|
blocks the multiplexer's next all-or-nothing reserve (``Cannot reserve gateway profiles``). The
|
|
client must start the multiplexer instead, so the start target is decided here from the same facts
|
|
the multiplexer boots on (``gateway_multiplex_mode.host_serves_named_profile``): a profile's own
|
|
``gateway.standalone: true``, the default profile's explicit ``true``, or — when the flag is unset or
|
|
the retired ``false`` that boot settles like unset — the served set the last multiplexer recorded in
|
|
the root's ``gateway_state.json`` (a standalone default gateway clears it).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
def multiplexer_root_for(home: Path) -> Path | None:
|
|
"""Default root that may multiplex *home*, when *home* is a named profile under it."""
|
|
from hermes_constants import named_profile_home
|
|
if named_profile_home(home) is None:
|
|
return None
|
|
root = home.parent.parent
|
|
return root if root != home else None
|
|
|
|
|
|
def implied_host_root(home: Path) -> Path | None:
|
|
"""The root whose gateway must serve named profile *home* when nothing records a verdict.
|
|
|
|
``hermes gateway run`` refuses a ``profiles/<name>`` home a gateway of its own unless it authored
|
|
``gateway.standalone: true`` (or passes ``--force``); the retired ``multiplex_profiles: false``
|
|
no longer opts out, so only a standalone profile keeps its own daemon.
|
|
"""
|
|
root = multiplexer_root_for(home)
|
|
if root is None:
|
|
return None
|
|
from hermes_cli.gateway_multiplex_mode import host_serves_named_profile
|
|
return None if host_serves_named_profile(root, home) is False else root
|
|
|
|
|
|
def multiplexer_serves_home(home: Path) -> Path | None:
|
|
"""The root whose (possibly stopped) multiplexer serves *home*, else None.
|
|
|
|
``hermes_cli.gateway.named_profile_served_by_running_multiplexer`` answers the live question;
|
|
this is its offline twin for the moment no gateway runs, deciding from the boot policy
|
|
(:func:`hermes_cli.gateway_multiplex_mode.host_serves_named_profile`): a standalone secondary is
|
|
never served, an explicit ``true`` serves the rest, and an unset or retired ``false`` flag falls
|
|
back to the ``served_profiles`` the last multiplexer recorded. No evidence keeps it unanswered.
|
|
"""
|
|
root = multiplexer_root_for(home)
|
|
if root is None:
|
|
return None
|
|
from hermes_cli.gateway_multiplex_mode import host_serves_named_profile
|
|
verdict = host_serves_named_profile(root, home)
|
|
if verdict is not None:
|
|
return root if verdict else None
|
|
from gateway.status import read_runtime_status
|
|
from hermes_cli.profiles import normalize_profile_name
|
|
served = (read_runtime_status(root / "gateway_state.json") or {}).get("served_profiles")
|
|
if not isinstance(served, list):
|
|
return None
|
|
names = {normalize_profile_name(str(p)) for p in served if p}
|
|
return root if normalize_profile_name(home.name) in names else None
|