fix(desktop): sessions with deleted/renamed providers resume cleanly; Bot Chats follow current profile config (salvage #96111 + #90343) - #97008
Merged
Conversation
… is stale A session row persists the provider identity a chat actually used. When that provider is later renamed or removed (e.g. a custom_providers:/providers: entry deleted, or a provider renamed oldone->newone), Desktop/TUI resume restores the stale name into agent init and dies with: agent init failed: Unknown provider '<name>' while the CLI resumes the same session fine with the configured default. - runtime_provider: add is_routable_provider() (full resolution chain: built-in -> providers: -> custom_providers: -> models.dev) - _stored_session_runtime_overrides: heal a non-routable provider via canonical_custom_identity (base_url -> model -> configured provider), drop to the configured default when unrecoverable, and clear the stale base_url after healing so a dead endpoint cannot override the registry URL - _start_agent_build: gate deferred-resume overrides on provider routability; when the stored provider is gone, prefer the model the user picked for THIS session, else the configured default - tests: is_routable_provider cases, heal/fallback round-trips, gate checks Refs #75128
…rent config
Room member sessions in Bot Mode are per-member scratch conversations
inside a group chat. session.resume restored their stored model/provider
pin from the row's model_config, so a room bot stayed stuck on whatever
provider was pinned when the row was first written — even after the
profile was switched. Every room message then failed on the stale
provider (e.g. 'out of Nous credits' after switching a profile from
Nous to ollama-cloud) while the same bot worked fine in DMs.
Add an explicit room_plumbing contract:
- session.create accepts room_plumbing: true, persisted in model_config
- _stored_session_runtime_overrides() returns {} for marked rows, so a
room session always rebuilds from the member profile's CURRENT config
- hidden + 'Group:' title shape is kept as a legacy fallback for rows
created by older desktop builds that never sent the marker; hidden
non-room chats keep the stored-runtime restore
- Desktop Bot Mode sends room_plumbing: true when creating the hidden
per-member room sessions
Fixes #89497
…config Bot-Mode canonical chats (the ONE forever DM per bot) and room plumbing sessions are plugin-owned scratch conversations. They are now created with an explicit follow_profile_config contract, persisted in the session row's model_config, so session.resume rebuilds from the member profile's CURRENT config instead of restoring the stored model/provider pin from an old row. That stale pin is what left bot DMs stuck on a dead provider (e.g. 'out of Nous credits' after the profile was switched to ollama-cloud) while the same bot worked fine in rooms — the mirror image of the room-plumbing bug (#89497 class). Normal 1:1 user chats keep the stored-runtime restore: opening an older chat must show the model it actually used. - tui_gateway/methods_session.py: accept follow_profile_config on session.create - tui_gateway/server.py: persist the marker in the row; skip stored-runtime overrides on resume when present - apps/desktop/src/plugins/hermes-bots/plugin.js: send the contract from createCanonicalChat and ensureGroupChatSession - tests: backend override + row-persist coverage; desktop source-contract coverage for both session kinds
…Bot Chats Bot Chats created before the follow_profile_config marker existed carry no contract in model_config, so they would stay pinned to a stale stored provider until deleted — the exact shape of the live reports (#89497, #94818). Mirror the plugin's own identity rule (the profile's session titled exactly 'Bot Chat') as a legacy fallback in _stored_session_runtime_overrides, matching the room-plumbing legacy 'Group:' title fallback. Follow-up to the salvaged #90343 (@curator8888) and #96111 (@lorzl).
…ispatch + state.db) Drives the REAL session.resume -> _make_agent -> AIAgent path (eager_build) through tui_gateway.server.handle_request against a real seeded state.db in an isolated HERMES_HOME — no mocks. Three scenarios from the live report: deleted provider falls back to default, renamed provider heals, and a legacy canonical Bot Chat (no follow_profile_config marker) follows the profile's current config. A/B verified: all 3 FAIL on origin/main with the reported "resume failed: Unknown provider '<name>'"; 3/3 pass with the salvaged fixes + backfill.
Contributor
૮ >ﻌ< ა ci reviewran on 9c71216 — chore: remove single-use Windows live-E2E proof workflow (ru
|
This was referenced Aug 28, 2026
teknium1
added a commit
that referenced
this pull request
Aug 28, 2026
createCanonicalChat sends follow_profile_config and ensureGroupChatSession sends room_plumbing + follow_profile_config again, as main's plugin.js did before the rebuild. Without them, bot sessions created by this branch fell back to the server's legacy title heuristics (exact 'Bot Chat' title; hidden + 'Group: ' prefix) — the exact dependence the explicit contracts were introduced to replace. Contract-shape tests pin both params.
melon-xf
added a commit
to melon-xf/hermes-agent
that referenced
this pull request
Sep 3, 2026
…built modules createCanonicalChat sends follow_profile_config and ensureGroupChatSession sends room_plumbing + follow_profile_config again, as main's plugin.js did before the rebuild. Without them, bot sessions created by this branch fell back to the server's legacy title heuristics (exact 'Bot Chat' title; hidden + 'Group: ' prefix) — the exact dependence the explicit contracts were introduced to replace. Contract-shape tests pin both params.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Desktop/TUI sessions whose saved provider was deleted or renamed now resume cleanly, and Bot Chats always follow the profile's CURRENT config — including Bot Chats created before the fix.
Salvage of PR #96111 (@lorzl) and PR #90343 (@curator8888), rebased onto current main with authorship preserved, plus our legacy-Bot-Chat backfill and a live E2E suite. Fixes the live Discord report shape ("wants to connect to endpoints I deleted ages ago", bots pinned to dead providers after profile changes) — #75128, #89497 class, validated by #94818.
Changes
hermes_cli/runtime_provider.py:is_routable_provider()— whether a stored provider name still resolves to a routable route (from fix(tui-gateway): heal or fall back when a resumed session's provider is stale #96111)tui_gateway/server.py:_stored_session_runtime_overridesheals a renamed custom provider viacanonical_custom_identity(base_url → model → configured provider), clears the stale base_url after healing, or drops the provider so resume falls back to the session-picked model / configured default (from fix(tui-gateway): heal or fall back when a resumed session's provider is stale #96111)_overrides_have_routable_provider) (from fix(tui-gateway): heal or fall back when a resumed session's provider is stale #96111)follow_profile_configcontract honored on resume for Bot-Mode sessions (from fix(bot-mode): bot sessions always follow the profile's current config #90343)Bot Chat(created before the marker existed) also follow the profile's current configtui_gateway/methods_session.py: acceptfollow_profile_configonsession.create(from fix(bot-mode): bot sessions always follow the profile's current config #90343)apps/desktop/src/plugins/hermes-bots/plugin.js: canonical Bot Chat + room plumbing sessions send the contract (from fix(bot-mode): bot sessions always follow the profile's current config #90343)tests/tui_gateway/test_stale_provider_resume_live.py: new live E2E — realsession.resume→_make_agent→AIAgentthroughhandle_requestagainst a real seededstate.dbin an isolated HERMES_HOME (no mocks).github/workflows/windows-staleprovider-e2e.yml: single-use Windows proof workflow[proof do-not-merge]— reverted before mergeValidation
resume failed: Unknown provider 'custom:deadbot'(live)custom:<name>identityLive repro: all three scenarios fired on origin/main via the real dispatch path (
Unknown provider 'custom:deadbot'verbatim) and pass on this branch; Windows CI proof leg runs the same live suite on windows-latest.Credit
Infographic