Skip to content

feat(sessions): configurable per-session exclusivity with queue-instead-of-refuse (#101279) - #103797

Open
salch-cred wants to merge 2 commits into
NousResearch:mainfrom
salch-cred:feat/per-session-exclusive-queue-mode-101279
Open

salch-cred wants to merge 2 commits into
NousResearch:mainfrom
salch-cred:feat/per-session-exclusive-queue-mode-101279

Conversation

@salch-cred

Copy link
Copy Markdown

What

Configurable per-session exclusivity for shared-brain deployments (#101279): a new gateway.per_session_exclusive config switch (default true — behavior unchanged) that, when set to false, turns a SESSION_NOT_OWNED refusal into a bounded wait for the current owner to finish, then re-acquire — the queue semantics the messaging gateway already gives Telegram — instead of pushing a mid-day failure onto the second surface.

This implements the issue's proposed Option 1 (config switch, default on) + Option 2 (queue instead of refuse) as one coherent design: the switch decides whether the desktop/CLI submit paths queue or refuse.

Changes

  • hermes_cli/active_sessions.py
    • per_session_exclusive(config) — resolves gateway.per_session_exclusive (top-level fallback, bool/"true"/"false" strings accepted, invalid values warn once and keep the safe default True).
    • wait_for_session_ownership(...) — bounded poll of the live-lease registry until no other lease holds the session (or timeout/abort). Never raises: an unreadable registry returns False so the caller falls back to the original refusal. on_wait fires exactly once for a status line ("Another Hermes process is using this session; waiting...").
  • tui_gateway/session_lifecycle.py — _ensure_active_session_slot: on a SESSION_NOT_OWNED refusal, when the operator opted out, wait for the owner and re-acquire (_maybe_queue_for_session_ownership). All other refusal reasons (registry unavailable, capacity) are untouched, and the default path is a single dict check as before.
  • cli.py _claim_active_session — same queue-instead-of-refuse on the CLI surface, with a yellow hourglass notice.
  • tui_gateway/methods_voice.py gateway.capabilities — advertises per_session_exclusive_queue_mode (config-dependent, separate from the build-enforced per_session_exclusive_submit) so clients can surface "queued" instead of "refused" only when it is actually true.

What does NOT change

  • Default deployments: exclusivity stays exactly as enforced today (the existing tests/test_active_session_exclusivity.py suite passes untouched).
  • SESSION_COORDINATION_UNAVAILABLE (registry unreadable) still fails closed — queueing is never used to smuggle a second writer through an unknown-ownership state.
  • MAX_CONCURRENT_SESSIONS capacity policy is untouched.
  • The wait is bounded (default 1800s, matching LEASE_WAIT_SECONDS); a timeout falls back to the original refusal.
  • PER_SESSION_EXCLUSIVE_SUBMIT remains a module constant: the build-level enforcement is unchanged; the config only governs how a surfaced refusal is handled.

Tests

tests/test_per_session_exclusive_opt_out.py (13 new):

  • resolution: default true; gateway false/true; top-level fallback; string coercion; invalid values warn + keep default; attribute-style config objects
  • exclusivity still enforced when opted in
  • wait semantics: free session returns immediately; held session blocks until release then re-acquire succeeds; bounded timeout returns False and the refusal still applies; on_wait fires exactly once (not per poll); other sessions' leases are not waited on

Regression: tests/test_active_session_exclusivity.py + tests/hermes_cli/test_active_sessions.py (29) pass unchanged; tests/tui_gateway/test_protocol.py (68) and the cross-process ownership suites (10) pass. ruff check clean.

Closes #101279

@alt-glitch alt-glitch added type/feature New feature or request P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) area/sessions Session lifecycle, resume, persistence, history area/config Config system, migrations, profiles sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Sep 5, 2026
@alt-glitch

Copy link
Copy Markdown

This was generated by AI during triage.

Related: implements #101279 (Options 1+2). Note this branch also carries the unrelated agent/turn_context.py row-addressed api_content backfill and its test from #103721 (same author) — consider rebasing so this PR only contains the per-session-exclusivity change. Adjacent lease fixes in flight: #103713, #103737.

This branch has not been deployed

No deployments
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/sessions Session lifecycle, resume, persistence, history comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make per-session exclusivity (SESSION_NOT_OWNED) configurable — shared-brain deployments need multi-writer sessions

2 participants