fix: status falls back to the live agent before the first host frame; recent route ties break deterministically

Under turn isolation `session.status` passed agent=None and only the metadata
mirror's model/provider, so until the compute host sent its first frame the
mirror was empty and the TUI rendered "Model: (unknown) (unknown)" where main
showed the in-process agent's route. Fall back to the live agent's model and
provider like `server._session_info` already does.

`get_recent_session_model_route` ordered by `last_seen DESC` alone; two rows
stamped in the same flush tie and SQLite's temp-sort order is unspecified, so
the retired route could be reported as current. Order by `rowid DESC` as the
secondary key so the route that appeared later wins.
This commit is contained in:
teknium1
2026-09-13 13:51:19 -07:00
committed by Teknium
parent f26335752b
commit bdb14f5f81
4 changed files with 42 additions and 5 deletions
+4 -2
View File
@@ -748,7 +748,9 @@ class SessionSessionsMixin:
"""Most recently used main-loop model route as one coherent per-call tuple
(``session_model_usage`` keeps model+provider together; ``sessions`` mixes route changes).
Recency, not lifetime call count: on a long session a route retired weeks ago can hold the
highest ``api_call_count`` forever, and /status and /usage would keep calling it current."""
highest ``api_call_count`` forever, and /status and /usage would keep calling it current.
``rowid DESC`` breaks same-timestamp ties toward the route that first appeared later; without
it SQLite's temp-sort order is unspecified and the retired route can win."""
self.flush_token_counts()
row = self._read_one(
"""SELECT model, billing_provider, billing_base_url, billing_mode,
@@ -758,7 +760,7 @@ class SessionSessionsMixin:
AND task = ''
AND model <> 'unknown'
AND billing_provider <> ''
ORDER BY last_seen DESC
ORDER BY last_seen DESC, rowid DESC
LIMIT 1""",
(session_id,),
)
@@ -0,0 +1,17 @@
"""get_recent_session_model_route picks the newest route deterministically on equal last_seen."""
from hermes_state import SessionDB
def test_recent_route_tie_on_last_seen_prefers_later_row(tmp_path):
db = SessionDB(tmp_path / "state.db")
db.create_session(session_id="s1", source="tui", model="alpha")
with db._lock:
for model in ("alpha", "zeta"):
db._conn.execute(
"INSERT INTO session_model_usage (session_id, model, billing_provider, task,"
" api_call_count, last_seen) VALUES ('s1', ?, 'p', '', 1, 100.0)",
(model,),
)
db._conn.commit()
assert db.get_recent_session_model_route("s1")["model"] == "zeta"
@@ -11770,6 +11770,21 @@ def test_session_status_reads_live_compute_host_metadata(monkeypatch):
assert "Model: live-host-model (live-host-provider)" in resp["result"]["output"]
def test_session_status_falls_back_to_agent_before_first_host_frame(monkeypatch):
agent = types.SimpleNamespace(model="live-model", provider="live-provider")
server._sessions["sid"] = _session(agent=agent, _compute_host_active=True)
monkeypatch.setattr(server, "_get_db", lambda: None)
try:
resp = server.handle_request(
{"id": "1", "method": "session.status", "params": {"session_id": "sid"}}
)
finally:
server._sessions.pop("sid", None)
assert "Model: live-model (live-provider)" in resp["result"]["output"]
def test_skills_reload_runs_in_gateway_process(monkeypatch):
import agent.skill_commands as skill_commands
+6 -3
View File
@@ -1663,11 +1663,14 @@ def _(rid, params: dict, session: dict) -> dict:
key = session.get("session_key") or params.get("session_id") or ""
mirror = _metadata_mirror(session)
# Under turn isolation the compute host owns the live route: a stale in-process agent object
# must not outrank the host's mirrored model/provider.
agent = None if session.get("_compute_host_active") else session.get("agent")
# must not outrank the host's mirrored model/provider. Before the first host frame fills the
# mirror, the in-process agent is still the only route we know (same order as _session_info).
live_agent = session.get("agent")
agent = None if session.get("_compute_host_active") else live_agent
fields = build_status_fields(
key, agent, _status_row(session, params, key),
model=mirror.get("model"), provider=mirror.get("provider"),
model=mirror.get("model") or getattr(live_agent, "model", None),
provider=mirror.get("provider") or getattr(live_agent, "provider", None),
tokens=_session_usage_snapshot(session).get("total"), agent_running=bool(session.get("running")),
)
project = _project_info_for_cwd(_display_session_cwd(session))