fix(agent): keep the todo predicate off the model_tools/executor import path

is_todo_tool_call lived in agent/tool_executor.py and went through
canonical_tool_name, which imports model_tools. TUI resume calls it from
_todo_state_from_history on the RPC path, so the first resume in a
gateway loaded ~405 modules (2-3s) synchronously. tui_gateway/server.py
and run_agent.py also imported agent.tool_executor at module level,
adding ~142 modules to every TUI/desktop launch and breaking run_agent's
lazy-forward rule.

The predicate now lives in tools/todo_tool.py, which both startup paths
already load. It matches TODO_TOOL_NAMES ({TODO_SCHEMA name} + the legacy
aliases) and imports the bridge parser only when a tool_call entry's
args mention "todo". model_tools._LEGACY_TOOL_ALIASES derives its todo
entry from TODO_LEGACY_ALIASES, so there is one source of truth ("todo"
is the only alias mapping to todo_list). The live tool.complete path in
tool_progress uses is_todo_tool_name and the hand-kept _TODO_TOOL_NAMES
tuple is gone. The server.py noqa import is replaced by a function-local
import next to MAX_TODO_RESULT_CHARS, so a pruned name can't be swallowed
by the broad except. run_agent imports lazily. The dead TypeError arm is
dropped, and field reads use message_sanitization._tc_field.
agent/tool_executor.py is back to its pre-stack state.

Co-authored-by: JoaoMarcos44 <joaomarcosdias444@gmail.com>
This commit is contained in:
kshitijk4poor
2026-09-27 16:43:23 +05:30
committed by kshitij
parent 05254937a4
commit 622a296f7a
6 changed files with 54 additions and 39 deletions

View File

@@ -274,6 +274,49 @@ TODO_SCHEMA = {
}
}
# Pre-rename names that replay as the Todo tool. model_tools._LEGACY_TOOL_ALIASES derives its todo
# entries from this, so the alias map and the transcript/TUI predicates below cannot drift.
TODO_LEGACY_ALIASES = ("todo",)
TODO_TOOL_NAMES = frozenset((TODO_SCHEMA["name"], *TODO_LEGACY_ALIASES))
def is_todo_tool_name(name: Any) -> bool:
"""True for the Todo tool's current name or a legacy alias (an already-unwrapped dispatch name)."""
return name in TODO_TOOL_NAMES
def is_todo_tool_call(tool_call: Any) -> bool:
"""True when a transcript tool_call entry (dict or object) invoked the Todo tool.
Covers the current name, legacy aliases, and the ``tool_call`` bridge (``todo_list`` is deferred by
default, and the transcript keeps the bridge name). The bridge is peeled from the recorded arguments
only, never live tool-search config, and must wrap exactly one call. Lives here, not in
agent.tool_executor, so TUI resume and run_agent never pull model_tools / the executor in to answer it.
"""
from agent.message_sanitization import _tc_field
fn = _tc_field(tool_call, "function")
name, raw_args = _tc_field(fn, "name") or "", _tc_field(fn, "arguments")
if is_todo_tool_name(name):
return True
# Cheap pre-check before the bridge modules load: no "todo" in the raw args means no todo inside.
if isinstance(raw_args, str) and "todo" not in raw_args:
return False
from tools.tool_search_catalog import TOOL_CALL_NAME
if name != TOOL_CALL_NAME:
return False
try:
args = json.loads(raw_args) if isinstance(raw_args, str) else raw_args
except json.JSONDecodeError:
return False
if not isinstance(args, dict):
return False
from tools.tool_search_validation import normalize_tool_call_entries
entries, error = normalize_tool_call_entries(args)
return not error and len(entries) == 1 and is_todo_tool_name(entries[0]["name"])
from tools.registry import registry, tool_error