Skip to content

fix(gateway): respect routed profile busy modes - #83550

Merged
teknium1 merged 1 commit into
NousResearch:mainfrom
tmchow:fix/routed-profile-busy-input-mode
Aug 11, 2026
Merged

fix(gateway): respect routed profile busy modes#83550
teknium1 merged 1 commit into
NousResearch:mainfrom
tmchow:fix/routed-profile-busy-input-mode

Conversation

@tmchow

@tmchow tmchow commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Why this matters

A multiplexed gateway can serve several Hermes profiles from one process, and each profile can configure how new messages are handled while its agent is already running:

  • queue: wait until the current response finishes
  • steer: redirect the active response using the new message
  • interrupt: stop the active response and handle the new message immediately

Before this fix, those profile-specific settings were not actually independent. The gateway loaded the default profile's busy mode once and reused it for every routed profile. For example, if the default profile used queue and a secondary worker profile used steer, messages routed to the worker could still be queued.

That made a valid per-profile configuration silently behave incorrectly.

What this PR does

This PR makes every routed profile honor its own display.busy_input_mode and legacy busy_text_mode settings.

After this change:

  • secondary profiles can independently use queue, steer, or interrupt;
  • every active-session path uses the mode belonging to the routed profile;
  • default-profile and non-multiplexed gateways behave exactly as before;
  • missing or invalid secondary settings safely fall back to the gateway default;
  • profile config is read once during multiplex startup, not on every message;
  • no process environment or global state is mutated while routing messages.

Implementation

The gateway snapshots each served secondary profile's busy settings during multiplex startup and resolves the appropriate in-memory snapshot from the routed message source.

The profile-aware resolver is used by all busy-input paths:

  • the normal active-session busy handler;
  • the early running-agent priority path;
  • secondary-adapter busy guards;
  • Telegram follow-up grace handling;
  • restart draining in both directions;
  • legacy busy-text behavior.

Secondary adapter events are stamped with their owning profile before the routed session key and busy behavior are resolved.

Related issue

Fixes #83439

Type of change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Test coverage

The new regression suite verifies:

  • independent queue, steer, and interrupt behavior across profiles;
  • route/default-profile boundaries;
  • legacy busy_text_mode resolution;
  • both restart-drain directions and the priority drain path;
  • secondary-adapter busy guards;
  • startup-time config snapshotting with no per-message rereads;
  • safe fallback for missing and invalid secondary settings.

Commands

scripts/run_tests.sh tests/gateway/test_multiplex_busy_input_mode.py -q

scripts/run_tests.sh \
  tests/gateway/test_multiplex_busy_input_mode.py \
  tests/gateway/test_busy_session_ack.py \
  tests/gateway/test_multiplex_adapter_registry.py \
  tests/gateway/test_profile_routing.py \
  tests/gateway/test_profile_resolution.py \
  tests/gateway/test_restart_drain.py -q

ruff check .
python -m py_compile gateway/run.py tests/gateway/test_multiplex_busy_input_mode.py
python scripts/check-windows-footguns.py --all
ty check tests/gateway/test_multiplex_busy_input_mode.py

Results

  • Focused regression suite: 15 passed.
  • Six-file gateway suite: 66 passed, 2 host-specific tests skipped for their CI lane.
  • Ruff, compile, Windows-footgun, and focused type checks passed.
  • Sabotage test: reverting restart-drain resolution to the process default caused 3 expected failures; restoring the routed resolver produced 3 passes.

Checklist

Code

  • I've read the Contributing Guide and repository instructions.
  • My commit message follows Conventional Commits.
  • I searched existing issues and PRs to make sure this isn't a duplicate.
  • My PR contains only changes related to this fix.
  • I've run the repository's canonical scripts/run_tests.sh suites and they pass.
  • I've added tests for the bug fix.
  • I've tested on macOS.

Documentation and housekeeping

  • Documentation changes are N/A; this fixes existing configuration behavior.
  • cli-config.yaml.example changes are N/A; no config keys changed.
  • Contributor-guide changes are N/A; no workflow or architecture contract changed.
  • Cross-platform impact was considered; the Windows-footgun check passes.
  • Tool description/schema changes are N/A.

@tmchow
tmchow marked this pull request as ready for review August 11, 2026 00:40
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery area/config Config system, migrations, profiles area/profiles Multi-profile isolation, HERMES_HOME scoping sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 11, 2026
@teknium1
teknium1 merged commit a31be48 into NousResearch:main Aug 11, 2026
49 checks passed
@Enough1122

Copy link
Copy Markdown
Contributor

Heads-up: tests/gateway/test_multiplex_busy_input_mode.py::test_profile_route_and_nonmultiplexed_resolution_preserve_boundaries (line 339, assert 'interrupt' == 'steer') is failing consistently in CI on the current main base — not just on one PR.

Evidence:

  • Fails on slice 5/12 for 4 unrelated PRs (redact/desktop/signal/agent fixes — none touch multiplex logic), twice in a row each (original run + retrigger).
  • Also fails on other maintainers' PRs touching unrelated areas (e.g. fix/kanban-block-task-live-claim-guard, run 31467347350).
  • Passes locally on Windows/Python 3.11 (15/15 in the file) — environment-dependent, not a logic regression in the PRs.

Looks like this landed via #83550 (a31be4803). Any fix/relaxation of the assertion (or CI-side flake handling) on main would unblock the queue — happy to help verify once it's in.

@fangliquanflq

Copy link
Copy Markdown
Contributor

Follow-up on the CI failure above:

This is a deterministic fixture gap from this PR, not a flake and not caused by the unrelated PRs that are going red.

_profile_name_for_source only accepts a profile_routes hit when the target is in the served set from _multiplex_profile_homes / profiles_to_serve. test_profile_route_and_nonmultiplexed_resolution_preserve_boundaries snapshots research busy modes and configures a route, but never marks research as served, so the route is rejected (target profile 'research' is not served) and busy mode falls back to default interrupt.

It can pass locally if a real ~/.hermes/profiles/research exists (because profiles_to_serve scans Path.home()), which matches the Windows local green / CI red report above.

Tracked in #83743 with a fixture fix in #83745 (monkeypatch _multiplex_profile_homes so research is served for that boundary test).

686f6c61 added a commit to 686f6c61/hermes-agent that referenced this pull request Aug 11, 2026
After selective multiplex profile serving (NousResearch#83550), route resolution
rejects target profiles missing from the served set, so this boundary
test fell back to default interrupt on every CI slice (NousResearch#83743). Stamp
research as served so the route-path assertion matches production
rules. Same fixture shape as open NousResearch#83745.
686f6c61 added a commit to 686f6c61/hermes-agent that referenced this pull request Aug 11, 2026
After selective multiplex profile serving (NousResearch#83550), route resolution
rejects target profiles missing from the served set, so this boundary
test fell back to default interrupt on every CI slice (NousResearch#83743). Stamp
research as served so the route-path assertion matches production
rules. Same fixture shape as open NousResearch#83745.
686f6c61 added a commit to 686f6c61/hermes-agent that referenced this pull request Aug 11, 2026
After selective multiplex profile serving (NousResearch#83550), route resolution
rejects target profiles missing from the served set, so this boundary
test fell back to default interrupt on every CI slice (NousResearch#83743). Stamp
research as served so the route-path assertion matches production
rules. Same fixture shape as open NousResearch#83745.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles area/profiles Multi-profile isolation, HERMES_HOME scoping comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: multiplexed profiles inherit the default busy input mode

5 participants