Skip to content

fix(tour): an unanswered tour bridge no longer costs 45s per action - #90112

Merged
OutThisLife merged 1 commit into
mainfrom
bb/tour-unanswered-bridge
Aug 19, 2026
Merged

fix(tour): an unanswered tour bridge no longer costs 45s per action#90112
OutThisLife merged 1 commit into
mainfrom
bb/tour-unanswered-bridge

Conversation

@OutThisLife

Copy link
Copy Markdown
Collaborator

Fast follow on #89620. A user trying to reproduce the tour demo reported the model timing out on tour.

What's wrong

tour is a blocking bridge: Python emits tour.request and waits up to 45s for the renderer to answer tour.respond. The handler that answers ships in the desktop bundle; the tool is offered by the backend. Those update on different clocks.

Against a desktop build older than the tour tool, the event lands in a renderer with no branch for it. Nothing answers, and the agent blocks the full 45s — once per action the model tries. Because the schema (correctly) tells the model to call action='targets' first and then narrate, one "give me a tour" turn stacks those waits:

targets  45s  →  show  45s  →  show  45s  →  show  45s  →  stop  45s

Roughly four minutes of dead air, ending in "The tour request timed out, or no GUI window answered." Nothing in the system noticed that the client provably could not answer the first one.

This is not limited to old builds — a renderer that crashed or a window that went away fails the same way.

The fix

Hold a session's first tour action to a deadline a working renderer cannot miss, and let an unanswered probe mark the bridge unavailable for that session. Later calls then return immediately with an error naming the actual fix, instead of stalling again.

Once a client has answered once, real actions get the full deadline back, so a preview tour injecting its engine into a live page still works, and a single slow action no longer condemns a live client. The verdict lives on the session record, so it dies with the session and a new one re-probes.

Measured on the five-action sequence above: ~225s of dead air becomes a single 10s probe, then instant.

targets   10.0s  {"success": false, "error": "No Hermes Desktop window answered ...
show       0.0s  {"success": false, ...
show       0.0s  {"success": false, ...
show       0.0s  {"success": false, ...
stop       0.0s  {"success": false, ...

What this deliberately does not do

It does not stop the tool being offered to a client that can't run it. Hiding it needs a capability the client declares at session.create, and since prompt caching fixes the toolset for the life of a conversation, that can only ever take effect for a new session. It would not have helped the reported session, and it cannot help a renderer that dies mid-conversation — which is why the in-session behaviour is the part worth fixing first. Happy to follow up with the handshake if we want the tool gone entirely for older apps.

Tests

tour shipped without any, so this adds both halves:

  • tests/tools/test_tour_tool.py — the tool's own contract (toolset placement, action/surface/step validation, payload shape, JSON pass-through, callback failure).
  • tests/tui_gateway/test_tour_bridge_fail_fast.py — the bridge: first action probes, an answering client gets the full deadline back, an unanswered probe explains the real problem, later actions short-circuit, a proven client survives one slow action, a new session re-probes.

scripts/run_tests.sh tests/tui_gateway/ tests/tools/tests/tui_gateway/ fully green. The tests/tools/ failures (daytona, voice, web_tools_config, file_tools) reproduce identically on clean main with the same counts; they're missing optional deps in my local env.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/tui Terminal UI (ui-tui/ + tui_gateway/) labels Aug 19, 2026
The renderer's `tour.request` handler ships in the desktop bundle, but the
tool is offered by the backend, and the two update on different clocks. A
desktop build older than the tour tool receives the event in a renderer with
no branch for it, so `tour.respond` never comes and the agent blocks for the
full 45s deadline — once per action the model tries. A single "give me a
tour" turn (targets, then narrate, then stop) stacked those waits into
minutes of dead air, which is what got reported against #89620.

Hold a session's first action to a deadline a working renderer cannot miss,
and let an unanswered probe mark the bridge unavailable for that session:
later calls return immediately with an error naming the actual fix instead
of stalling again. Once a client has answered, real actions get the full
deadline back, so a preview tour injecting into a live page still works and
one slow action no longer condemns a live client. The verdict lives on the
session record, so it dies with the session and a new one re-probes.

The same five-action sequence goes from ~225s of dead air to a single 10s
probe. Toolset gating is unchanged: removing the tool outright needs a
client capability declared at session.create, which prompt caching means
can only take effect for a new session.
@OutThisLife
OutThisLife force-pushed the bb/tour-unanswered-bridge branch from ff81c42 to 84d81d2 Compare August 19, 2026 16:20
@OutThisLife
OutThisLife enabled auto-merge August 19, 2026 16:20
@OutThisLife
OutThisLife merged commit 6c6c17e into main Aug 19, 2026
47 checks passed
@OutThisLife
OutThisLife deleted the bb/tour-unanswered-bridge branch August 19, 2026 16:28
bobaba76 pushed a commit to bobaba76/hermes-agent that referenced this pull request Aug 27, 2026
…wered-bridge

fix(tour): an unanswered tour bridge no longer costs 45s per action
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
…wered-bridge

fix(tour): an unanswered tour bridge no longer costs 45s per action
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants