fix(tui-gateway): subagent lifecycle survives display.tool_progress=off

`_on_tool_progress` bailed on the tool-progress gate before dispatching
`subagent.*`, so a Desktop/TUI user who hid tool-call chrome also lost the
subagent rows in the status stack and spawn tree. Subagent lifecycle is
application state (like `todo.updated`, clarify and MCP consent cards,
which already bypass the gate); the gate now applies only to the optional
progress chrome (reasoning previews, MoA rows, tool.generating).
This commit is contained in:
Teknium
2026-09-07 09:19:31 -07:00
parent 610c869ac6
commit 03f3b09222
3 changed files with 22 additions and 1 deletions

View File

@@ -98,3 +98,18 @@ def test_empty_list_at_nonzero_revision_is_a_real_clear():
state = server._normalize_todo_state({"todos": [], "revision": 2})
assert state == {"todos": [], "revision": 2}
def test_subagent_lifecycle_bypasses_tool_progress_off(monkeypatch):
"""Subagent rows feed the Desktop status stack / TUI spawn tree — application state, not
tool-progress chrome — so display.tool_progress=off must not swallow them."""
sid = "subagent-progress-off"
events = []
monkeypatch.setitem(server._sessions, sid, {"agent": None, "tool_progress_mode": "off"})
monkeypatch.setattr(server, "_tool_progress_enabled", lambda _sid: False)
monkeypatch.setattr(server, "_emit", lambda event, event_sid, payload=None: events.append(event))
server._on_tool_progress(sid, "subagent.start", "delegate_task", "goal", None, goal="goal", subagent_id="s1")
server._on_tool_progress(sid, "reasoning.available", "_thinking", "hmm", None)
assert events == ["subagent.start"]

View File

@@ -358,10 +358,14 @@ def _on_tool_progress(
sid: str, event_type: str, name: str | None = None, preview: str | None = None,
_args: dict | None = None, **_kwargs,
):
if not _tool_progress_enabled(sid) or (event_type == "tool.started" and name):
if event_type == "tool.started" and name:
return
# Subagent lifecycle is application state (Desktop status stack, TUI spawn tree), not
# tool-progress chrome: it must survive display.tool_progress=off like todo.updated does.
if event_type.startswith("subagent."):
return _progress_subagent(sid, name, preview, _kwargs, event_type)
if not _tool_progress_enabled(sid):
return
handler, requires = _PROGRESS_HANDLERS.get(event_type, (None, None))
if handler is not None and (requires is None or {"name": name, "preview": preview}[requires]):
handler(sid, name, preview, _kwargs)

View File

@@ -2076,6 +2076,8 @@ In the CLI, cycle through these modes with `/verbose`. To use `/verbose` in mess
Tool progress requires a gateway adapter that can display progress updates safely. Platforms without message editing support, including Signal, suppress tool-progress bubbles even if `/verbose` saves a non-`off` mode.
`off` hides tool-call *chrome* only. Application state that has its own surface in the Desktop app and TUI — the task list (`todo_list`), subagent progress, clarify questions, and MCP consent cards — keeps flowing regardless of this setting.
### Focus view (`/focus`, CLI + TUI)
`display.focus_view: true` enables **focus view** — a reduced-output display mode for when you want the answer, not the play-by-play. It is a thin layer over the same `tool_progress` machinery rather than a second suppression path: