fix(hermes): preserve proven Claude authentication

This commit is contained in:
jenkins 2026-08-23 11:18:18 -03:00
parent 0682b2b813
commit 6b378fd410
2 changed files with 61 additions and 26 deletions

View File

@ -67,11 +67,6 @@ _auth_probe_value: dict[str, Any] = {}
HEALTH_POLL_SECONDS: Final = max(
30, int(os.environ.get("HERMES_CLAUDE_HEALTH_POLL_SECONDS", "60"))
)
AUTH_PROBE_GRACE_SECONDS: Final = max(
60, int(os.environ.get("HERMES_CLAUDE_AUTH_PROBE_GRACE_SECONDS", "300"))
)
def _read_secret(file_env_name: str) -> str:
"""Read a secret only from its runtime-mounted file."""
path = os.environ.get(file_env_name, "").strip()
@ -296,32 +291,28 @@ def _subscription_health(force: bool = False) -> dict[str, Any]:
)
api_provider = raw.get("apiProvider") if isinstance(raw, dict) else None
auth_method = raw.get("authMethod") if isinstance(raw, dict) else None
# A concurrent inference can make Claude's secondary auth-status
# process time out or return no JSON. A real, recent inference is
# stronger evidence than that inconclusive probe. Preserve the last
# proven-good auth state briefly, but never mask an explicit logged-out
# response from the CLI.
recent_success = False
try:
last_success = datetime.fromisoformat(
str(previous.get("last_success_at") or "").replace("Z", "+00:00")
# Cold starts and concurrent inference can make Claude's secondary
# auth-status process time out or return no JSON. Once this broker has
# proven first-party access, only an explicit logged-out response or an
# authoritative inference auth failure may revoke that evidence.
# Otherwise periodic probe timeouts would repeatedly eject a working
# subscription from routing.
known_authenticated = bool(
(
previous.get("authenticated") is True
and previous.get("api_provider") == "firstParty"
)
if last_success.tzinfo is None:
last_success = last_success.replace(tzinfo=timezone.utc)
age = (checked_time - last_success.astimezone(timezone.utc)).total_seconds()
recent_success = 0 <= age <= AUTH_PROBE_GRACE_SECONDS
except (TypeError, ValueError):
pass
or previous.get("last_authenticated_at")
or previous.get("last_success_at")
)
probe_stale = bool(
not probe_conclusive
and recent_success
and previous.get("authenticated") is True
and previous.get("api_provider") == "firstParty"
and known_authenticated
)
if probe_stale:
authenticated = True
api_provider = previous.get("api_provider")
auth_method = previous.get("auth_method")
api_provider = previous.get("api_provider") or "firstParty"
auth_method = previous.get("auth_method") or "oauth_token"
value = {
"transport": "claude-code-cli-subscription",
"state": "available" if authenticated else "unavailable",
@ -332,8 +323,16 @@ def _subscription_health(force: bool = False) -> dict[str, Any]:
"auth_probe_stale": probe_stale,
"subscription_type": raw.get("subscriptionType")
if isinstance(raw, dict)
else None,
else previous.get("subscription_type"),
}
if authenticated:
value["last_authenticated_at"] = (
checked_at
if probe_conclusive
else previous.get("last_authenticated_at")
or previous.get("last_success_at")
or checked_at
)
# Readiness probes must not erase the most recently observed native
# usage window or successful route metadata. They only refresh auth.
for key in (
@ -447,6 +446,11 @@ def _invoke(payload: dict[str, Any]) -> tuple[dict[str, Any], dict[str, int], st
health.update(
{
"state": "available",
"authenticated": True,
"api_provider": "firstParty",
"auth_method": "oauth_token",
"auth_probe_stale": False,
"last_authenticated_at": datetime.now(timezone.utc).isoformat(),
"last_success_at": datetime.now(timezone.utc).isoformat(),
"latency_ms": int((time.monotonic() - started) * 1000),
"model": actual_model,

View File

@ -154,6 +154,36 @@ def test_transient_auth_probe_keeps_recent_proven_access(monkeypatch, tmp_path)
assert health["auth_probe_stale"] is True
def test_transient_auth_probe_keeps_older_proven_access(monkeypatch, tmp_path) -> None:
module = _module(monkeypatch)
module.HEALTH_PATH = tmp_path / "claude.json"
module.HEALTH_PATH.write_text(
json.dumps(
{
"authenticated": False,
"last_success_at": "2026-01-01T00:00:00+00:00",
}
),
encoding="utf-8",
)
monkeypatch.setattr(
module.subprocess,
"run",
lambda *_args, **_kwargs: SimpleNamespace(
returncode=1, stdout="", stderr="transient timeout"
),
)
health = module._subscription_health(force=True)
assert health["authenticated"] is True
assert health["state"] == "available"
assert health["api_provider"] == "firstParty"
assert health["auth_method"] == "oauth_token"
assert health["auth_probe_stale"] is True
assert health["last_authenticated_at"] == "2026-01-01T00:00:00+00:00"
def test_explicit_logged_out_probe_clears_recent_access(monkeypatch, tmp_path) -> None:
module = _module(monkeypatch)
module.HEALTH_PATH = tmp_path / "claude.json"
@ -183,3 +213,4 @@ def test_explicit_logged_out_probe_clears_recent_access(monkeypatch, tmp_path) -
assert health["authenticated"] is False
assert health["state"] == "unavailable"
assert health["auth_probe_stale"] is False
assert "last_authenticated_at" not in health