Unify the named-provider fixes from #52506, #57185, #60337, and #60901 at the main-model normalization chokepoint.
Co-authored-by: izumi0uu <izumi0uu@gmail.com>
Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
Co-authored-by: Paulo Henrique <paulohenrique_789@hotmail.com>
Preserve the root cause and precedence direction from #43538 while applying the merge before truncation and covering all declared model shapes.
Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
list_authenticated_providers() emits picker rows for every slug in
PROVIDER_TO_MODELS_DEV that has any credential env-var set. Several of
those slugs (notably 'mistral') have no PROVIDER_REGISTRY entry, so
resolve_provider() rejects them as 'Unknown provider' once the user
selects a model — leaving the picker showing rows that cannot actually
be selected.
Add a resolve-gate in section 1: if PROVIDER_REGISTRY.get(hermes_id)
is None, skip the slug. The picker now only lists providers that can
actually be switched to at runtime.
This automatically resolves the duplicate-Mistral dedup symptom too:
once the broken-from-models.dev row is filtered, the conflict between
PROVIDER_TO_MODELS_DEV['mistral'] and a custom_providers 'Mistral' row
is moot.
Composes with #50289 (which promotes mistral to first-class via the
provider-plugin path): when that lands, PROVIDER_REGISTRY gains a
'mistral' entry and the gate becomes a no-op for it. No conflict.
Tests (regression suite):
- tests/hermes_cli/test_model_switch_filter_unresolved.py (new, 4 tests):
Picker excludes 'mistral' when MISTRAL_API_KEY is set; 'deepseek' and
'xai' (PROVIDER_REGISTRY-backed) still appear; 'mistral' stays
excluded when no key is set. Confirmed by reverting the fix and
seeing the test fail with 'mistral leaked into /model picker'.
Cross-checked against the existing 51 test_model_switch_* and
test_custom_provider_* cases — 55/55 PASS, no regressions.
MoA was internally inconsistent: preset-level ops (set default / add /
delete) persisted on click, but reference-model and aggregator slot edits
sat behind a manual Save button. Debounce-persist slot/aggregator edits
like the rest of settings and drop the redundant button, so MoA is
uniformly autosave.
Retain the provider-boundary core of #52799 while reusing the pool reload and handoff paths already landed in #53591 and #62417.
Co-authored-by: Flownium <157689911+itsflownium@users.noreply.github.com>
An aggregator whose pooled credentials are all exhausted/dead still counted as
an authenticated provider during no-provider /model resolution. It then won the
model-name match, was set as the sticky session provider, and poisoned every
later switch with "empty API key" errors while still routing through the dead
aggregator.
list_authenticated_providers now requires a pool to have at least one available
entry (has_available, not has_credentials / bare key presence) at all three
credential-pool gates. Simple token-style entries that don't parse into
exhaustion-tracked entries keep the prior behaviour, so providers whose creds
live only in the auth-store credential_pool still appear.
Fixes#45759
ShellFileOperations builds bash commands (wc/head/sed/cat/tee ...) with the
target path as an argument. On a Windows/Git-Bash host a native `C:\...` path
has its backslashes eaten by bash (and mangled by the msys runtime even when
single-quoted) — the "Directory \drivers\etc does not exist; exiting — update
your msys package" class of failures. Rewrite a native drive path to forward
slashes in `_escape_shell_arg`, reusing the env layer's `_windows_to_msys_path`.
Both `C:/...` and `/c/...` fix the backslash bug (the MSYS coreutils resolve
either via the POSIX API). We emit `/c/...` purely for consistency: it's the
same form `_windows_to_msys_path` already produces for the terminal `cd`
(LocalEnvironment._quote_cwd_for_cd), so shell file ops and `cd` share one
helper and one path form.
Scoped from #55481, which also patched BaseEnvironment._quote_cwd_for_cd — but
LocalEnvironment already overrides that through `_windows_to_msys_path`, so on a
real Windows host the base branch never ran (the cwd is already `/c/...`).
Co-authored-by: konsisumer <der@konsi.org>
Capture durable Desktop engineering principles from recent sessions —
state by authority, workspace-switch shapes, resolver ladders, optimistic
UI — and point root AGENTS.md at the scoped guide with current filenames.
Answered clarifies were collapsing into a generic tool row, hiding the
choice. Settle into a Q&A panel instead, and route freeform input through
the shared Textarea chrome.
Resuming a session cd's back into its recorded working directory, so it resumes
in the repo it belonged to. `--no-restore-cwd` opts out; skipped under
--worktree (that path owns its dir); best-effort — a missing dir warns and stays
put rather than failing the resume.
Co-authored-by: Cary Palmer <palmer@dugoutfantasy.com>
`hermes sessions list --workspace <needle>` filters to one workspace (git repo
root or project dir, matched by path substring or basename) and adds a
Workspace column. The column only appears once at least one listed session
carries a workspace, so all-unbound listings render exactly as before.
Co-authored-by: Cary Palmer <palmer@dugoutfantasy.com>
A session's coarse workspace identity: its git repo root when known, else its
cwd (branch excluded, so switching branches doesn't fragment history). Pure
helper over fields sessions already record — no new columns, no git shelling.
Co-authored-by: Cary Palmer <palmer@dugoutfantasy.com>
Align the workspace status-bar dropdown with the rest of the status bar: drop
the per-item icons (they mixed lucide size-4 with a Codicon 1rem glyph and were
the only status-bar menu carrying item icons), leaving text-only items on the
shared DropdownMenuItem primitive with default typography. The status-bar
trigger keeps its FolderOpen glyph, consistent with sibling items.
Also map true@supersynergy.de → Supersynergy in AUTHOR_MAP.
Squashed salvage of #45744 (@harjothkhara), rebased onto current main and
resolved against #58241 (which swapped the new-session cwd fallback to the
project-aware resolveNewSessionCwd).
An explicitly clicked sidebar workspace stays authoritative until session.create:
a one-shot $newChatWorkspaceTarget (null → detached, string → that folder) plus a
generation counter so a stale async `config.get project` normalization can't
overwrite a newer draft target. The start-workspace-session action is extracted
out of desktop-controller.tsx into a testable workspace-session-target module.
Integrated with #58241: the no-explicit-target branch now falls through to the
project-aware resolveNewSessionCwd() instead of the old workspaceCwdForNewSession.
Co-authored-by: harjoth <harjoth.khara@gmail.com>
Addresses @teknium1's review of #61950:
- The desktop live overlay (workspace-groups.ts) matched cwd membership
case-sensitively, so a fresh mixed-case/separator Windows session missed
its explicit/auto project until the next backend tree refresh. Mirror the
backend identity (isWindowsPath/comparisonSegments/pathKey) in isPathUnder,
liveSessionProjectId, and overlayRepoLanes lane matching. Comparison-only —
emitted ids/labels keep their spelling. POSIX stays case-sensitive.
- Backend _is_windows_path missed root-relative `\wsl.localhost\...` (single
leading backslash), leaving that historical spelling case-sensitive. Classify
any backslash-rooted path as Windows.
Tests: WSL-spelling collapse + explicit-project precedence (project_tree),
Windows/WSL live-overlay membership + POSIX case-sensitivity (workspace-groups).
Sign out of the dedicated OAuth partition before opening the login window so a
stale gateway/identity-provider cookie can't silently bounce an expired session
straight back into failure. Relabel the action "Sign out & sign in" and spell
out the sign-out step in the hint.
Co-authored-by: Tony Antunez <57689194+smtony@users.noreply.github.com>
Add isRemoteReauthError so an auth-shaped boot error counts as a remote-reauth
failure even when the session indicator still reads connected (a stale refresh
cookie / failed ws-ticket mint). Wire the boot error into the overlay's reauth
check so those sessions route to Sign in instead of the local-only recovery
buttons.
Co-authored-by: Tony Antunez <57689194+smtony@users.noreply.github.com>
A remote/VPS backend that failed to boot trapped the user on the recovery
screen — Retry/Repair/Use-local only target the local backend, so the only fix
was hand-editing connection.json. Add an in-place "Gateway settings" view (the
real GatewaySettings panel embedded via `embedded`, lazy-loaded) reached from
the recovery card, and shape the recovery actions by failure kind: Sign in for a
lapsed remote session, Gateway settings for any other remote failure (Retry
drops to secondary; Repair is dropped — it can't revive a remote), Retry for a
local backend. Use-local is scoped to remote failures.
Add an `embedded` flag to GatewaySettings (and a `bare` variant to
SettingsContent) that drops the page title/intro, Diagnostics row,
"Save for next restart", and the page gutters — so the same panel can be reused
inside a tighter surface without a second connection form to maintain. No change
to the standalone Settings → Gateway page (defaults off).
Factor the "remote/cloud with a URL" check out of isRemoteReauthFailure into a
shared isRemoteConfig helper so the boot-failure overlay can tell any remote
failure apart from a local one.
Remove the DEV-gated "Dev · soft switch" ListRow and its previewGatewaySwitch
helper. It was a temporary review affordance for exercising the soft-switch
reconnect; dead-stripped from production, but it doesn't belong in the tree.
wipeSessionListsForGatewaySwitch (the real path) and $gatewaySwitching stay.