fix(tui_gateway): record why a deferred agent build attached nothing

A deferred build whose session record was closed or replaced while it ran
leaves through `_build` early, yet the `finally` still sets `agent_ready`
with `agent` None and `agent_error` None. Record the cause on the record so
the turn that was waiting on it can refuse with a real reason instead of
running against a missing agent (#111531).

Trimmed from PR #111534: the `_wait_agent_for_prompt` readiness hunk and the
turn-body guard are superseded by a single refusal at `_admit_prompt_turn`
(next commit), which every turn source crosses.
This commit is contained in:
zqy1-1
2026-09-15 12:05:34 -07:00
committed by Teknium
parent 88593f0f0d
commit a944a4d8ae
2 changed files with 16 additions and 1 deletions

View File

@@ -39,7 +39,8 @@ from tui_gateway.turn_marker import clear_turn_marker, read_turn_marker, record_
from tui_gateway.contracts import registry as _contracts
# User-facing copy shared with the split method modules (they close over this namespace).
from tui_gateway.user_messages import ( # noqa: F401
AGENT_STILL_STARTING, agent_init_failed_message, busy_message, resume_failed_message, turn_error_text)
AGENT_BUILD_ABANDONED, AGENT_MISSING_FOR_TURN, AGENT_STILL_STARTING, agent_init_failed_message, busy_message,
resume_failed_message, turn_error_text)
from tui_gateway.transport import (FanoutTransport, StdioTransport, Transport, bind_transport,
current_transport, reset_transport)
@@ -1109,12 +1110,18 @@ def _start_agent_build(sid: str, session: dict) -> None:
with _sessions_lock:
current = _sessions.get(sid)
if current is None:
# Closed/reaped before the build started: nothing will ever attach an agent to this
# record, yet ``agent_ready`` must be set so a prompt waiting on it fails instead of hanging.
session["agent_error"] = AGENT_BUILD_ABANDONED
ready.set()
return
notify_registered, scopes, session_db = False, None, None
profile_home = current.get("profile_home")
try:
if not _await_resume_history(sid, current):
# Replaced mid-build: the finally still sets ``agent_ready`` with ``agent`` None, so record
# why — a turn admitted against this record refuses with the real reason (#111531).
current["agent_error"] = AGENT_BUILD_ABANDONED
return
tokens = _set_session_context(key)
# Global-remote: bind the session profile's HERMES_HOME and hand the agent that profile's db —

View File

@@ -99,6 +99,14 @@ AGENT_STILL_STARTING = (
"Hermes is still starting this session (loading tools), so this command could not run yet. "
"Wait for the status bar to show ready and try again.")
# A deferred build that finished WITHOUT attaching an agent (its session record was replaced or
# closed while it ran) leaves ``agent_ready`` set and ``agent`` None; this is the recorded cause.
AGENT_BUILD_ABANDONED = "agent build aborted: the session record was replaced before the build finished"
# Turn refusal when the record still has no agent at admission time (reason unknown).
AGENT_MISSING_FOR_TURN = (
"Hermes could not start the assistant for this session, so your message was not run. "
"Reopen the session (or start a new one with /new) and send it again.")
def resume_failed_message(exc: Any) -> str:
return (f"Could not reopen that session (its transcript could not be read). Details: {exc}. "