18 Commits

Author SHA1 Message Date
teknium1
41ead01d6d fix: custom-provider key_env in the CLI picker/catalog helpers reads through the profile secret scope
hermes_cli/models_local.py::_api_key_from_provider_config and
hermes_cli/model_setup_flows_custom.py::_model_flow_named_custom still read
the variable named by key_env with raw os.getenv / os.environ.get. Under the
multiplexed gateway one process serves many profiles from one os.environ, so
the picker's endpoint probe and the native Ollama catalog headers could carry
a stale or another profile's value, while a key present only in the profile's
.env was not found (#67935). Every other credential read on the custom
provider path (runtime_provider_custom, runtime_provider_backends) already
goes through agent.secret_scope.get_secret_str, which falls back to
os.environ for single-profile processes, so the CLI behaviour is unchanged.
2026-09-19 10:26:08 -07:00
teknium1
5bccc4e238 fix(model_switch): clear key_env only when the route changes; drop it on custom activation
`model.key_env` is not custom-only: the Desktop settings UI stores REGISTRY
provider keys there (e.g. HERMES_CUSTOM_LMSTUDIO_API_KEY with provider
lmstudio, #106336) and auth._model_level_key_env honours it. The previous
predicate (`not custom or route_changed`) therefore wiped that pointer on a
same-provider same-base_url model re-pick and silently broke the user's
credential. The pointer now clears ONLY when provider or base_url changed;
the inline api_key/api rule is unchanged.

Custom-endpoint activation (model_setup_flows_custom) popped base_url /
api_key but never key_env, so a stale pointer from a previous endpoint
outranked the credential it had just written — pop it alongside.

Tests: the same-route re-pick case now covers a registry provider (red on
the old predicate), and the two clear_model_endpoint_credentials tests are
folded into one invariant.
2026-09-15 03:39:45 -07:00
teknium1
e8016a18c1 fix(cli): report the auto-detected context length when a custom provider is saved without one
Leaving the context-length prompt blank in the custom-endpoint wizard said
"will auto-detect" and then went silent, so users could not tell whether their
endpoint runs on a detected window or the runtime's default fallback (which
shapes compression and prompt-cache behaviour). After the save prompt, run the
same resolver the runtime uses (with the endpoint's URL and key) and print
either "auto-detected N tokens" or "not detected — using the default N tokens".
Feedback only: the probe result is not persisted, and a failing probe never
blocks the save.

Fixes #2513. Approach from PR #2522 (@ygd58) and PR #85499 (@Luna161), both
written against the pre-decomposition wizard module.

Co-authored-by: Luna161 <268031236+Luna161@users.noreply.github.com>
2026-09-12 05:10:51 -07:00
Teknium
520e63661c fix: keep command-auth model discovery lazy across config and setup 2026-09-07 21:22:49 -07:00
Hayden Moulds
c111ede3e5 fix(picker): resolve key_cmd credentials for model discovery
`key_cmd` (#86891) authenticates a provider with a SHORT-LIVED bearer minted
by a command — SSO/OIDC brokers, cloud IAM, internal auth proxies. The
request path has honoured it since it landed, but the picker resolved probe
credentials from `api_key`/`key_env` ONLY, so a key_cmd provider probed
`/v1/models` with an EMPTY key.

Against an authenticated endpoint the probe 401s, discovery returns nothing,
and the provider falls back to its single configured default model. The
picker shows ONE model, indistinguishable from an endpoint that genuinely
serves one — while inference keeps working, because that path mints
correctly. Reproduced against a LiteLLM gateway behind Entra OIDC: 0 models
discovered with an empty key, 26 with the minted token.

Both picker probe sites already funnel through `_entry_credentials()`, so
the fix lands in one place: it now reports a `cmd:<key_cmd>` identity, and
each site falls back to `resolve_probe_token()` after api_key/key_env. An
explicit static key still wins, so existing configs are unaffected.

The identity is keyed on the COMMAND, never the minted token: the token
rotates on every refresh, so keying on its value would change the group
fingerprint constantly and force a re-probe on every open. Two entries on
one URL with different helpers still get distinct rows.

`resolve_probe_token()` lives in agent.command_token_source, which already
owns key_cmd minting, and shares the CommandTokenSource cache with the
request path — a cache read, not a fresh sign-in. Fail-closed: a helper
needing an interactive sign-in degrades to today's empty-key behaviour
rather than taking down every other provider's row.

`_model_flow_named_custom` (the `hermes model` setup flow) is the sibling
path — it builds its own `Authorization: Bearer` from the same incomplete
resolution — and is fixed the same way, with one ordering constraint: the
value persisted to config.yaml is computed BEFORE the mint, so a short-lived
bearer can never be written back to shadow the key_cmd meant to re-mint it.

Tests drive the real code paths and assert on the credential each probe
receives rather than on function source, so a semantics-preserving refactor
does not fail them. Verified they fail with the fix reverted.
2026-09-07 21:22:49 -07:00
Teknium
0e78694c72 simplify(compat): hermes_cli.main — drop 198 re-exports/aliases (155 eager + 45 lazy PEP 562 + _warn_stale_dashboard_processes alias + _time/_self/_LAZY_* machinery), repoint 17 source callers + 91 test files 2026-09-03 15:07:39 -07:00
Teknium
7a33369e81 simplify(compat): interrupt — drop _ThreadAwareEventProxy/_interrupt_event legacy alias, repoint 2 test files
No runtime consumer read the proxy (terminal_tool/environments call is_interrupted()/set_interrupt()
directly); its only users were tests patching tools.interrupt._interrupt_event, which had no effect on
the code under test. tools/terminal_tool.py's own re-export of the name is owned by another worker.
2026-09-03 14:00:59 -07:00
Teknium
c93ace77c2 simplify(compat): config/runtime_provider/plugins/commands/secrets_cli/kanban — drop 96 re-exports (incl. PEP 562 facades) + 3 aliases (get_pre_tool_call_directive/_block_message, get_telegram_handler_factories), repoint 56 callers + 50 test files 2026-09-03 14:00:17 -07:00
Teknium
7b8c11bcf7 simplify(compat): models — drop 52 re-exports from hermes_cli.models, repoint 16 callers + 41 test files 2026-09-03 13:48:49 -07:00
Teknium
1e6cfaa0d0 simplify(compat): setup/model_switch/nous_subscription — drop 34 re-exports, repoint 14 callers + 26 test files (~100 sites) 2026-09-03 13:33:10 -07:00
Teknium
e83816a4d1 review-fix(comments): restore lost #NNNN rationale comments across non-test source (mechanical sweep, condensed, code unchanged)
For each issue anchor present in BASE 63279301bc non-test .py and absent on HEAD, the BASE comment/docstring block was re-attached at the HEAD location of the code it explained (matched by the distinctive code line / enclosing def). Sentences already covered by an existing HEAD comment were deduped; the issue number always survives. Insert-only: no code lines changed.
2026-09-03 09:44:26 -07:00
Teknium
31bc234a66 refactor(hermes_cli): compact setup-flow docstrings/comments; fold env-ref identity indexing 2026-09-02 21:54:56 -07:00
Teknium
0f767135f8 refactor(hermes_cli): unify curses radiolist-or-fallback into _radiolist (3 sites) 2026-09-02 21:17:50 -07:00
Teknium
a6e3fe5f5b refactor(hermes_cli): try/except-pass -> contextlib.suppress in setup flows 2026-09-02 21:05:31 -07:00
Teknium
9f9401cf05 refactor(hermes_cli): azure flow — current-settings dataclass, transport-detection phase helper; squeeze import blanks 2026-09-02 20:49:52 -07:00
Teknium
f4996ddbee refactor(hermes_cli): main_platform_setup — WhatsApp phase helpers, sync subcommand dispatch, _say/_err banners 2026-09-02 20:43:00 -07:00
Teknium
b5f96ab3e5 refactor(hermes_cli): AST-neutral layout compaction of provider/platform setup flows 2026-09-02 20:03:18 -07:00
Teknium
72490fa6cd refactor(model_setup_flows): extract custom-endpoint, Azure Foundry and Bedrock wizards into concern modules
hermes_cli/model_setup_flows_custom.py (_model_flow_custom, _model_flow_named_custom + helpers),
model_setup_flows_azure.py (_model_flow_azure_foundry + Entra preflight / picker),
model_setup_flows_bedrock.py (BEDROCK_GEO_PREFIXES, routability predicates, both Bedrock flows).
Bodies moved verbatim (AST slices); every name re-exported from model_setup_flows (noqa: F401)
so hermes_cli.main and test imports keep resolving. Origin: 1975 -> 1159 lines.
Flow corpus (396 cases): 0 diffs; 34 test files / 364 tests green.
2026-09-02 16:39:35 -07:00