Skip to content

fix(desktop): sessions with deleted/renamed providers resume cleanly; Bot Chats follow current profile config (salvage #96111 + #90343) - #97008

Merged
teknium1 merged 7 commits into
mainfrom
salvage/stale-provider-botmode-cluster
Aug 28, 2026
Merged

fix(desktop): sessions with deleted/renamed providers resume cleanly; Bot Chats follow current profile config (salvage #96111 + #90343)#97008
teknium1 merged 7 commits into
mainfrom
salvage/stale-provider-botmode-cluster

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

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

Validation

Before (origin/main) After (this branch)
Resume w/ deleted provider resume failed: Unknown provider 'custom:deadbot' (live) falls back to configured default, agent builds
Resume w/ renamed provider same failure healed to new custom:<name> identity
Legacy Bot Chat (pre-marker) pinned to dead provider forever follows profile's current config
Live E2E suite (3 tests) 3/3 FAIL 3/3 pass
Targeted unit suites 51/51 pass (persistence + identity + db-ownership + live)
Desktop plugin tests 92/92 pass (group-chat.test.mjs)

Live 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

Stale Provider Sessions Heal on Resume

lorzl and others added 6 commits August 28, 2026 01:59
… 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.
@teknium1
teknium1 requested a review from a team August 28, 2026 09:02
@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 9c71216 — chore: remove single-use Windows live-E2E proof workflow (ru

⚠️ Warnings

OSV vulnerability scan · View job

6 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.


debug info

CI timings

CI timings · View report · View job

Wall time 4m14s vs 3m27s (+22.7%). 11 job(s) slower, 2 faster,

  • Python tests / Run tests: +44.0s
  • OS-specific tests / Windows-only tests: +23.0s
  • OS-specific tests / macOS-only tests: +8.0s
  • Python lints / ruff enforcement (blocking): +5.0s
  • Python lints / Windows footguns (blocking): +3.0s

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) comp/desktop Electron desktop app (apps/desktop/*) area/sessions Session lifecycle, resume, persistence, history sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 28, 2026
@teknium1
teknium1 merged commit 547f4c9 into main Aug 28, 2026
38 checks passed
@teknium1
teknium1 deleted the salvage/stale-provider-botmode-cluster branch August 28, 2026 09:59
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants