_custom_provider_model_matches() only compared the session model
against the entry's single 'model' field. A custom provider declaring
a multi-model catalog (providers.<name>.models mapping / models list)
whose default model differed from the session model silently failed to
match — dropping the entry's extra_body entirely. Real impact: an
OpenAI custom provider pinning service_tier=flex via extra_body ran
every request at STANDARD tier (~2.3x billing) with zero signal.
- Model matching now accepts the session model when it appears in the
entry's models catalog (dict keys or list), case-insensitive;
single-model 'model' field behavior unchanged; entries with neither
still match everything.
- Usage report ('hermes -z --usage-file') now carries service_tier
(the tier requested via request_overrides.extra_body) so batch
pipelines can audit the billed tier per run.
Validation: 8 new tests; live E2E via real 'hermes -p sweeper -z'
with httpx-level wire capture — service_tier=flex present in the
outgoing /v1/responses body and in the usage report.
#63113 rewrote native drive paths in ShellFileOperations, but init_session
/_wrap_command still embedded C:/... hermes-snap paths from get_temp_dir.
MSYS arg-converts those during bash -l and surfaces Directory \drivers\etc
— including for relative write_file targets, since the wrapper is the fault.
Add _bash_safe_path, override BaseEnvironment._quote_shell_path on
LocalEnvironment (no base→local import), and normalize mixed /c/Users\...
paths in file ops.
Co-authored-by: xxxigm <tuancanhnguyen706@gmail.com>
The web UI CONFIG_SCHEMA showed ['ask', 'yolo', 'deny'] for the
approvals.mode select field. These don't match any real config values
and 'smart' mode was entirely unreachable from the dashboard.
Correct the options to ['manual', 'smart', 'off'] which match the
values defined and documented in hermes_cli/config.py.
Adds a regression test to TestBuildSchemaFromConfig to pin the correct
option names and guard against future drift.
Fixes#31925
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.
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>
Add shared translators in hermes_constants (Windows drive → /mnt, `\\wsl(.localhost|$)\`
UNC → POSIX, gated on is_wsl) and apply them at the gateway session-cwd boundary
so a Windows-host UI can hand the WSL backend a path it can actually chdir into.
De-dups the ACP adapter's private `_win_path_to_wsl` onto the shared helper and
extends it to the UNC spelling.
Co-authored-by: Rage Lopez <VrtxOmega@pm.me>
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>
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).
GatewayRunner._prepare_inbound_message_text's "@" context-reference
block read self._model / self._base_url to resolve the model for
get_model_context_length_async. GatewayRunner never sets either
attribute (copy-pasted from HermesCLI in da44c196b, which does carry
self.model/self.base_url). Every message containing "@" raised
AttributeError inside the try block, silently swallowed by the
surrounding except Exception at debug level, so
preprocess_context_references_async never ran and @file:/@folder:/@diff/
etc. references passed through to the model unexpanded.
Fix: resolve model/provider/base_url via
self._resolve_session_agent_runtime(source=, session_key=,
user_config=), the same session-aware resolution the hygiene
compression block already uses a few hundred lines later in this file.
Also raise the swallow log from debug to warning (with exc_info at
debug) so a future regression here is visible instead of silent.