diff --git a/agent/review_engine.py b/agent/review_engine.py index 68a00e3897..1196bd162d 100644 --- a/agent/review_engine.py +++ b/agent/review_engine.py @@ -106,8 +106,7 @@ def build_review_task(snapshot: List[Dict[str, str]], user_prompt: str = "", loa "--- Recent conversation (oldest first) ---", ] for message in snapshot: - label = "USER" if message["role"] == "user" else "PRIMARY AGENT" - lines += [f"[{label}]", message["text"], ""] + lines += [f"[{'USER' if message['role'] == 'user' else 'PRIMARY AGENT'}]", message["text"], ""] lines.append("--- End of conversation excerpt ---") if loaded_skills: skill_list = ", ".join(loaded_skills) @@ -135,12 +134,11 @@ def _load_review_credentials_cfg() -> Optional[Dict[str, Any]]: and no model/base_url) so the reviewer inherits the parent's credentials.""" try: from hermes_cli.config import load_config_readonly - review = (load_config_readonly().get("auxiliary") or {}).get("review") or {} - if not isinstance(review, dict): - return None except Exception: return None + if not isinstance(review, dict): + return None cfg = {k: str(review.get(k) or "").strip() for k in ("provider", "model", "base_url", "api_key", "api_mode")} if cfg["provider"].lower() == "auto": @@ -156,17 +154,13 @@ def start_review(parent_agent, messages: List[Dict[str, Any]], user_prompt: str when there is nothing to review or the dispatch is rejected/errored.""" if parent_agent is None: raise ValueError("No active agent — send a message first.") - snapshot = snapshot_recent_messages(messages) if not snapshot: raise ValueError("Nothing to review yet — the conversation is empty.") - - loaded_skills = collect_parent_loaded_skills(parent_agent, messages) - goal, context = build_review_task(snapshot, user_prompt, loaded_skills) + goal, context = build_review_task(snapshot, user_prompt, collect_parent_loaded_skills(parent_agent, messages)) credentials_cfg = _load_review_credentials_cfg() from tools.delegate_tool import delegate_task - raw = delegate_task(goal=goal, context=context, background=True, parent_agent=parent_agent, credentials_cfg=credentials_cfg) try: result = json.loads(raw) diff --git a/agent/review_idle_queue.py b/agent/review_idle_queue.py index 84bf204a9f..8d3fb52f4c 100644 --- a/agent/review_idle_queue.py +++ b/agent/review_idle_queue.py @@ -80,8 +80,6 @@ class ReviewIdleQueue: self._now: Callable[[], float] = time.monotonic self._server_idle: Callable[[], bool] = _managed_server_idle - # ── turn liveness (this process) ──────────────────────────── - def note_turn_started(self) -> None: with self._lock: self._live_turns += 1 @@ -94,8 +92,6 @@ class ReviewIdleQueue: self._quiet_since = self._now() self._wake.set() - # ── queue ──────────────────────────────────────────────────── - def enqueue(self, agent: Any, session_key: str, kwargs: Dict[str, Any]) -> None: """Add (or replace — newest snapshot wins) a session's pending review, keeping the ORIGINAL enqueue time on coalesce so a busy session cannot push its age-out forever.""" @@ -111,8 +107,6 @@ class ReviewIdleQueue: with self._lock: return len(self._pending) - # ── dispatcher ─────────────────────────────────────────────── - def _ensure_thread(self) -> None: with self._lock: if self._thread is None or not self._thread.is_alive(): @@ -193,8 +187,7 @@ def _managed_server_idle() -> bool: return True def _get(path: str) -> Any: - req = urllib.request.Request(f"{base}{path}", headers=headers) - with urllib.request.urlopen(req, timeout=3) as r: + with urllib.request.urlopen(urllib.request.Request(f"{base}{path}", headers=headers), timeout=3) as r: return json.loads(r.read()) loaded = [m["id"] for m in _get("/models").get("data", []) diff --git a/agent/side_question.py b/agent/side_question.py index 82d26487e5..2e993c0d96 100644 --- a/agent/side_question.py +++ b/agent/side_question.py @@ -59,10 +59,7 @@ def trim_snapshot_for_fork(history: Optional[List[Dict[str, Any]]]) -> List[Dict return msgs -def render_history_for_side_question( - history: Optional[List[Dict[str, Any]]], - char_budget: int = _TRANSCRIPT_CHAR_BUDGET, -) -> str: +def render_history_for_side_question(history: Optional[List[Dict[str, Any]]], char_budget: int = _TRANSCRIPT_CHAR_BUDGET) -> str: """Plain-text transcript for the fallback path: newest-biased fit to ``char_budget``, tool calls summarized by name, tool results truncated, system prompt skipped.""" lines: List[str] = [] @@ -95,11 +92,9 @@ def _side_question_task_config() -> Dict[str, Any]: """Return ``auxiliary.side_question`` from config (or ``{}``).""" try: from hermes_cli.config import load_config_readonly - - cfg = load_config_readonly() + aux = load_config_readonly().get("auxiliary") except Exception: return {} - aux = cfg.get("auxiliary") task = aux.get(SIDE_QUESTION_TASK) if isinstance(aux, dict) else None return task if isinstance(task, dict) else {} @@ -118,18 +113,12 @@ def _answer_via_fork(parent_agent: Any, question: str, history: Optional[List[Di fork, _rt, routed = build_cache_parity_fork(parent_agent, _side_question_task_config(), max_iterations=_FORK_MAX_ITERATIONS, write_origin="side_question") try: - set_thread_tool_whitelist( - set(), - deny_msg_fmt=( - "Side question (/btw) denied tool call: {tool_name}. " - "Tools are disabled here — answer directly from the conversation context." - ), - ) + set_thread_tool_whitelist(set(), deny_msg_fmt=( + "Side question (/btw) denied tool call: {tool_name}. " + "Tools are disabled here — answer directly from the conversation context.")) snapshot = trim_snapshot_for_fork(history) - result = fork.run_conversation( - user_message=f"{_FORK_PROMPT}\n\nSide question: {question}", - conversation_history=_digest_history(snapshot) if routed else snapshot, - ) + result = fork.run_conversation(user_message=f"{_FORK_PROMPT}\n\nSide question: {question}", + conversation_history=_digest_history(snapshot) if routed else snapshot) answer = (result or {}).get("final_response", "") or "" if not answer and result and result.get("error"): raise RuntimeError(str(result["error"])) @@ -137,11 +126,8 @@ def _answer_via_fork(parent_agent: Any, question: str, history: Optional[List[Di finally: clear_thread_tool_whitelist() # Attribute the fork's usage to the parent session; teardown never raises. - for step in ( - lambda: _record_review_usage_to_parent(parent_agent, _snapshot_review_usage(fork)), - fork.shutdown_memory_provider, - fork.close, - ): + for step in (lambda: _record_review_usage_to_parent(parent_agent, _snapshot_review_usage(fork)), + fork.shutdown_memory_provider, fork.close): try: step() except Exception: @@ -160,13 +146,8 @@ def _answer_via_oneshot(question: str, history: Optional[List[Dict[str, Any]]], def answer_side_question( - question: str, - history: Optional[List[Dict[str, Any]]], - *, - parent_agent: Any = None, - main_runtime: Optional[Dict[str, Any]] = None, - max_tokens: int = 2048, - temperature: Optional[float] = 0.3, + question: str, history: Optional[List[Dict[str, Any]]], *, parent_agent: Any = None, + main_runtime: Optional[Dict[str, Any]] = None, max_tokens: int = 2048, temperature: Optional[float] = 0.3, timeout: float = 180.0, ) -> str: """Fork when ``parent_agent`` is live, else (or on empty answer / failure) the one-shot diff --git a/agent/thinking_timeout_guidance.py b/agent/thinking_timeout_guidance.py index defc147d5a..074097ff9c 100644 --- a/agent/thinking_timeout_guidance.py +++ b/agent/thinking_timeout_guidance.py @@ -32,8 +32,7 @@ def is_thinking_timeout(classified: object, model: str, error_msg: str) -> bool: return False if get_reasoning_stale_timeout_floor(model) is None: return False - error_msg_lower = (error_msg or "").lower() - return any(p in error_msg_lower for p in _THINKING_TIMEOUT_SUBSTRINGS) + return any(p in (error_msg or "").lower() for p in _THINKING_TIMEOUT_SUBSTRINGS) def build_thinking_timeout_guidance(provider: str, model: str, model_label: Optional[str] = None) -> str: