Commit graph

1904 commits

Author SHA1 Message Date
teknium1
46613071e4 fix(config): widen empty-section guard to _deep_merge in load_config
Sibling site of the load_cli_config fix (#58277): _deep_merge treated a
YAML-null section (terminal: with no value) as an override, replacing
the entire DEFAULT_CONFIG dict for that section with None. Every
downstream consumer expecting a mapping was a latent crash, and default
sub-keys were silently lost. A None override of a dict default is now
ignored, matching the CLI loader's behavior. Scalar-null overrides are
unchanged.
2026-07-09 18:23:18 -07:00
yungchentang
bdecf0ab94 fix(cli): ignore empty config sections 2026-07-09 18:23:18 -07:00
Teknium
b298fd5db1
test(cli): deflake --accept-hooks position test — one driver, one import (#61734)
test_accepted_at_every_position spawned 11 separate
'python -m hermes_cli.main' subprocesses, each cold-importing the full
CLI module tree under a 15s TimeoutExpired deadline. On a loaded CI
worker the import alone can exceed that (slice 2/8 flaked exactly here
on PR #61726's run, TimeoutExpired at subprocess.py:1253), failing PRs
that never touched the CLI.

Replace with ONE driver subprocess that imports hermes_cli.main once
and parses all 11 argvs in-process (catching SystemExit per argv),
reporting JSON results. Same assertions per argv, identical semantics
(verified the --help-before-unknown-flag exit behavior matches the old
method), ~11x less import work, and the 180s timeout only trips on a
genuine hang.
2026-07-09 17:56:50 -07:00
chenkun
501616e8e6 fix(cli): set HERMES_YOLO_MODE before plugin discovery at startup 2026-07-09 16:58:37 -07:00
Adolanium
1f57ed2a53 fix(export): escape tool-call name in HTML session export
The HTML session export interpolated the tool-call name into the page
without escaping, while every sibling field went through _escape_html. A
tool-call name is attacker-influenced, so a prompt-injected model can emit
a name containing HTML that executes when the export is opened in a browser.

Escape the tool-call name like the other fields.
2026-07-09 16:46:07 -07:00
Teknium
fe25806a6b
fix(config): retain last-known-good config when config.yaml fails to parse (#60591)
Port from openai/codex#31188: a parse failure in a policy-bearing config
file must not silently replace the effective policy with an empty/default
one. Codex's load_exec_policy_with_warning replaced the whole exec policy
with Policy::empty() when a .rules file failed to parse, silently dropping
managed prompt/forbidden rules; the fix preserves the managed policy while
still warning.

Hermes had the same bug shape in load_config(): a YAML parse error made
_load_config_impl() fall through to DEFAULT_CONFIG, dropping every user
override — including approvals.deny rules, which are documented to block
commands even under --yolo. In a long-running gateway, a user mid-editing
config.yaml into broken YAML silently disarmed their own deny rules on the
next load.

Now, when the process has a last successfully loaded config for that path
(_LAST_EXPANDED_CONFIG_BY_PATH), a parse failure keeps serving it (cached
under the corrupt file's signature so the broken file isn't re-parsed) and
the warning says edits are being ignored until the YAML is fixed. Fresh
processes with no last-known-good keep the existing DEFAULT_CONFIG
fallback and warning.

E2E-verified: deny rule 'curl*evil.com*' still blocks after mid-process
corruption; fixed file reloads normally; fresh-process fallback unchanged.
2026-07-09 16:36:03 -07:00
Brooklyn Nicholson
b06e2f846c fix(kanban): no-TTY gate in _wants_tui_early — the actual worker-crash fix
The earlier fix gated _resolve_use_tui, but the EARLY launcher
(_wants_tui_early) decides TUI from display.interface before cmd_chat
runs — so a `display.interface: tui` default still booted the Ink UI for
headless spawns (kanban workers), whose no-TTY bail-out exits 0 →
"protocol violation". Gate the early resolver on a real TTY: headless
stdio never boots the TUI regardless of config; explicit --tui still does.
2026-07-09 16:13:59 -05:00
Brooklyn Nicholson
77db9d6bf3 fix(kanban): explicit re-queue bypasses the recent-success respawn guard
Dragging a task done→ready did nothing: the respawn guard saw a run that
completed within the success window and deferred forever, unable to tell a
deliberate operator re-run from a status flap. Now a re-queue event
(status change, promote, unblock, reclaim) AFTER the completion bypasses
the recent_success guard, so an explicit done→ready runs again.
2026-07-09 16:13:59 -05:00
Brooklyn Nicholson
aea570db4e fix(kanban): clear failure/crash diagnostics while a retry is in flight
A retried task (→ running) kept showing "crashed Nx": the in-flight run
has no outcome yet, so the trailing crash scan skipped it and kept
counting the prior streak, and the consecutive_failures counter lingers.
Exempt `running` from both repeated_failures and repeated_crashes so a
fresh attempt clears the banner until it itself resolves (re-fires if the
new run also fails).
2026-07-09 16:13:59 -05:00
Brooklyn Nicholson
e87c495dc2 fix(kanban): spawn workers headless — TUI can never eat a worker run
An inherited HERMES_TUI=1 or a `display.interface: tui` config default sent
kanban workers into the Ink TUI, whose no-TTY bail-out exits 0 without doing
the task — every attempt ended in "protocol violation". Two layers:

- _default_spawn pins `--cli` (highest-precedence interface flag) and strips
  HERMES_TUI from the child env (covers older builds on PATH).
- _resolve_use_tui gates ambient TUI prefs (env/config) behind a real TTY;
  an explicit --tui still wins so the informative bail-out stays reachable.
2026-07-09 16:13:59 -05:00
Brooklyn Nicholson
5829fe1378 fix(kanban): failure diagnostics exempt done/archived tasks
A manual done (dashboard/desktop drag) runs complete_task but ends no
run, so a trailing crashed/crashed run history never gains the
'completed' outcome that breaks the repeated_crashes streak — the card
kept flagging "needs attention" forever after being finished.
repeated_failures had the same hole via a stale counter. Terminal
statuses are now exempt from both: done means done; the history stays
on the event log for audit. Regression test included.
2026-07-09 16:13:59 -05:00
Kshitij Kapoor
111544d544 test(codex-picker): raise max_models so count invariant survives catalog growth
Adding the 6 gpt-5.6 slugs to DEFAULT_CODEX_MODELS grew the curated codex
catalog to 11, above the test's max_models=10 cap. That truncated the
picker list to 10 while total_models reported 11, breaking the
total_models == len(models) assertion. The cap was an implicit
change-detector on catalog size; raise it to 100 so the list is never
truncated and the count-consistency invariant stays meaningful as new
gpt-5.x slugs land.
2026-07-10 00:47:51 +05:30
Kshitij Kapoor
4af484d3dd feat(openai): complete gpt-5.6 E2E — codex catalog + 272K compaction auto-raise
Close the remaining end-to-end gaps so the full gpt-5.6 family (sol/
terra/luna + their -pro high-effort modes, 6 slugs) works on every
surface a user can reach them through:

- agent/auxiliary_client.py: the Codex OAuth backend hard-caps context
  at 272K for gpt-5.6 exactly as it does for 5.4/5.5, but the default
  50% compaction trigger would summarize at ~136K and waste half the
  usable window. Extend the existing _is_codex_gpt54_or_gpt55 chokepoint
  (single enforced predicate feeding _compression_threshold_for_model)
  to match gpt-5.6* on the openai-codex route so those sessions get the
  same 0.85 auto-raise. Direct-API/OpenRouter routes (full 1.05M window)
  are unaffected; the historical codex_gpt55_autoraise opt-out still
  applies. The one-time notice banner is model-dynamic and already
  renders the correct slug/cap.
- hermes_cli/config.py, agent/agent_init.py: refresh the autoraise
  comments/notice to mention the 5.6 family.
- hermes_cli/codex_models.py: add the -pro variants to DEFAULT_CODEX_MODELS
  + forward-compat so ChatGPT-OAuth (openai-codex) Pro users see the full
  family in /model, not just the base tiers.

Supersedes the earlier commit's note that 5.6 was intentionally kept out
of the codex catalog: the slugs are confirmed routable (OpenRouter live
+ codex backend), so they belong there like every other codex-capable
gpt-5.x slug.

E2E verified across all 6 slugs: direct-API ctx 1.05M, codex ctx 272K,
pricing reachable from openai + openai-api routes, codex compaction
override 0.85 (and None on direct-API + when opted out), present in
openai-api picker + codex catalog, /model gpt resolves to sol on both
native routes. Guard tests added for the compaction route matrix.
2026-07-10 00:47:51 +05:30
Kshitij Kapoor
a3828a94d0 feat(openai): cover gpt-5.6 -pro variants (PR #61587 complement)
PR #61587 adds sol-pro/terra-pro/luna-pro to the aggregator lists.
Complete those on the native surfaces the same way this PR completes
the base tiers:

- hermes_cli/models.py: -pro variants in _PROVIDER_MODELS[openai-api].
- agent/usage_pricing.py: alias ("openai", "gpt-5.6-*-pro") onto the
  base-tier PricingEntry rows — the -pro high-effort modes bill at the
  SAME per-token rates (verified against OpenRouter live pricing
  2026-07-09: identical prompt/completion prices for base and -pro);
  they cost more per task by consuming more tokens, not a higher rate.
- Context lengths need no new entries: "gpt-5.6-sol" et al. are
  substrings of their -pro variants and both lookup tables match
  longest-key-first (verified: sol-pro -> 1.05M direct / 272K codex).
- model_switch sort: -pro variants parse as suffix "sol-pro" (rank 1),
  so /model gpt still defaults to base sol — pinned by test.
- Not added to DEFAULT_CODEX_MODELS: only confirmed routable via API/
  OpenRouter so far; codex live discovery will surface them if ChatGPT
  exposes them, same policy as other unconfirmed codex slugs.

Tests: invariant tests extended (pro aliases share base entries, base
sol outranks sol-pro); 191 targeted tests pass.
2026-07-10 00:47:51 +05:30
Kshitij Kapoor
db117af478 review fixes: openai-api pricing route normalization, GA pricing_version, invariant tests
Phase-2 review findings addressed:
- resolve_billing_route: normalize the "openai-api" picker slug to the
  "openai" billing provider — without this the ("openai", <model>)
  _OFFICIAL_DOCS_PRICING keys (incl. every pre-existing gpt-4o/gpt-4.1
  entry, not just 5.6) were unreachable when the provider is openai-api.
- pricing_version: drop the "preview" tag (GA 2026-07-09 at same rates).
- model_metadata comment: dict order is cosmetic — lookups length-sort
  keys at match time; the old comment implied a positional invariant.
- model_switch comment: note "sol" is a series codename, not a generic
  quality word.
- tests/hermes_cli/test_gpt56_registration.py: behavior contracts (no
  list snapshots) — sol > terra/luna > 5.5 sort invariant, pricing
  reachability from both openai and openai-api routes, cache-write
  1.25x / cache-read 0.10x input relation.
2026-07-10 00:47:51 +05:30
kshitij
3ed7c8a8da
Merge pull request #61388 from kshitijk4poor/fix/dashboard-validate-web-dist
fix(dashboard): validate HERMES_WEB_DIST before startup (#17845 follow-through)
2026-07-09 14:55:44 +05:30
kshitij
cb79518d4f
Merge pull request #61385 from kshitijk4poor/fix/dashboard-residual-cron-event-loop-io
fix(dashboard): run residual cron profile I/O off the event loop (#50948 follow-through)
2026-07-09 14:49:12 +05:30
kshitijk4poor
f5bc18f901 fix: write expanded HERMES_WEB_DIST back for web_server's raw read
Phase-2 review finding: the validation branch expanduser()s the path but
web_server.py reads os.environ['HERMES_WEB_DIST'] raw at import — a
'~/dist' value would validate here and still 404 there. Write the
expanded path back before the web_server import. Adds a regression test
asserting the env var holds the expanded path after cmd_dashboard.
2026-07-09 14:48:50 +05:30
Tranquil-Flow
4ed910c689 fix(cli): mock systemd preflight in gateway service tests for non-systemd environments (#15187)
Mock _preflight_user_systemd and _select_systemd_scope in
test_systemd_start_refreshes_outdated_unit and
test_systemd_restart_refreshes_outdated_unit. These tests target
unit-file refresh logic, not D-Bus reachability, so the preflight
check was causing spurious UserSystemdUnavailableError on macOS,
WSL, and Docker where systemd is unavailable.

(cherry picked from commit 34113300a1)
2026-07-09 14:35:09 +05:30
kshitijk4poor
d928017742 fix(dashboard): validate HERMES_WEB_DIST before startup
A custom HERMES_WEB_DIST without --skip-build skipped BOTH the web UI
build and any validation: cmd_dashboard fell through the build gate and
started the server against a dist that may not exist, serving 404s with
no obvious cause. This is the same failure mode issue #23817 fixed for
the --skip-build branch — the env-var branch was left unvalidated.

Add the missing else-branch: fail fast with actionable guidance when
HERMES_WEB_DIST has no index.html, proceed (still without building) when
it does.

Credit: @Caelier (#17845) originally proposed dist validation for the
dashboard startup path; the --skip-build half of that PR's scope has
since landed via the #23817 fix, this covers the remaining env-var path
on the rewritten cmd_dashboard surface.
2026-07-09 14:34:01 +05:30
kshitijk4poor
8cfada0df4 test(dashboard): pin cron fire + blueprint handlers off the event loop
Mutation-verified: both tests fail against main's inline-call version and
pass with the threadpool routing.
2026-07-09 14:30:16 +05:30
kshitijk4poor
d54a8f7079 refactor(gateway): funnel HERMES_HOME sync through a single chokepoint
Follow-up to HexLab98's fix. The sync-before-regenerate invariant was
enforced by convention across 6 callsites (~3 idempotent unit-file reads
per command). Consolidate it into the one function every compare/regenerate
path funnels through — systemd_unit_is_current — and drop the now-redundant
callsite pre-syncs in refresh_systemd_unit_if_needed / systemd_start /
systemd_restart / systemd_status.

Kept the systemd_install pre-sync: the --force path bypasses the
is_current gate and calls generate_systemd_unit() directly, so it needs
its own sync to avoid baking /root/.hermes under sudo.

Reworked the two callsite-ordering tests into a chokepoint-invariant guard
(test_is_current_syncs_before_reading_unit) + a delegation test proving
start/restart no longer pre-sync. Both fail if the chokepoint sync is
removed; the pre-existing behavior test still passes.
2026-07-09 13:04:47 +05:30
HexLab98
8f18f6c695 test(gateway): cover sudo system-unit refresh adopting HERMES_HOME 2026-07-09 13:04:47 +05:30
kshitijk4poor
473407174b test(dashboard): assert server still gates OAuth endpoints without cookie
PR #61281 removed the client-side X-Hermes-Session-Token requirement from the
dashboard OAuth mutation calls so cookie-authenticated hosted/mobile sessions
can start provider logins. That change is safe only because the server still
gates those endpoints (gated_auth_middleware cookie check + _require_token).
The PR's api.test.ts suite mocks fetch and only asserts client behavior, so a
re-break of the gated-mode cookie gate would pass CI unnoticed.

Add gated-mode TestClient tests asserting POST /api/env/reveal and the OAuth
mutation endpoints (disconnect/start/submit/cancel) return 401 with no session
cookie. Mutation-verified: neutering both the middleware gate and _require_token
flips all five to 200.
2026-07-09 12:21:16 +05:30
Shannon Sands
3e24b16f56 fix(dashboard): support mobile OAuth login 2026-07-09 12:21:16 +05:30
kshitijk4poor
31e39dec84 test: expect compact_rows in the read-only status-count fake
Rebase reconciliation with #60884: _count_status_active_sessions (from
#58238) now passes compact_rows=True (this branch's #47437 projection),
so the fake asserts both.
2026-07-09 01:36:46 +05:30
Omar Baradei
4df16c429b Refresh on upstream/main: resolve conflicts (no behavior change)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 1e70eaa49a)
2026-07-09 01:36:46 +05:30
kshitijk4poor
c95cf313c9 test: patch the cached PID probe name in profile-unification status tests
get_status now probes via get_running_pid_cached() (#53511 salvage);
these tests were added on main after that PR was cut and still patched
web_server.get_running_pid, so their fakes were bypassed and CI slice
5/8 failed. Patch the name the handler actually calls.
2026-07-09 01:19:07 +05:30
kshitijk4poor
664878b0ba fix: guard /api/status active count against missing state.db
Review finding: SessionDB(read_only=True) requires the DB file to exist
(its documented contract says callers guard on db_path.exists()); on a
fresh install every /api/status poll paid an OperationalError until the
first session was written. Short-circuit to 0 when state.db is absent.
Tests: fresh-install guard + existing read_only test adjusted.
2026-07-09 01:19:07 +05:30
kshitijk4poor
1e2ad17afd fix: descendant CTE must dedup (UNION) to survive parent-chain cycles
The #39140 CTE used UNION ALL, which recurses forever if a corrupted
parent chain loops (a -> b -> a) — reproduced: query never returns. The
old Python walk was cycle-safe via a seen-set. UNION dedups the working
set and terminates. Regression test added and mutation-verified (UNION
ALL hangs the test, UNION passes).
2026-07-09 01:19:07 +05:30
luyifan
492be28e50 fix(web): bound dashboard action log tails
(cherry picked from commit a4188a3e24)
2026-07-09 01:19:07 +05:30
0disoft
7d0ddbb2ff fix(dashboard): cache gateway PID status probes
(cherry picked from commit a2bbe564ad)
2026-07-09 01:19:07 +05:30
墨綠BG
49fa04a235 🐛 fix(dashboard): use managed cron threadpool
(cherry picked from commit 97fabc289c)
2026-07-09 01:19:07 +05:30
墨綠BG
346e5673de 🐛 fix(dashboard): offload cron profile scans
(cherry picked from commit 1fded709aa)
2026-07-09 01:19:07 +05:30
yoma
9a4341aa90 fix(dashboard): keep status responsive when session db locks
(cherry picked from commit a3454dd15d)
2026-07-09 01:19:07 +05:30
Grace
0cf2e39c41 feat(gateway): add webhook payload filters 2026-07-08 08:10:55 -07:00
Teknium
b64b802131
feat(models): swap curated Tencent Hy3 Preview for GA tencent/hy3, drop owl-alpha (#60943)
- OPENROUTER_MODELS: remove openrouter/owl-alpha (free) and
  tencent/hy3-preview{,:free}; add tencent/hy3 and tencent/hy3:free
- _PROVIDER_MODELS[nous]: tencent/hy3-preview -> tencent/hy3
- run_agent.py reasoning-prefix list: tencent/hy3-preview -> tencent/hy3
  (prefix match still covers -preview if pinned)
- model_metadata: register hy3 context length (262144) alongside hy3-preview
- regenerate website/static/api/model-catalog.json
- update tokenhub curated-list tests to the new IDs

The tencent-tokenhub direct provider still serves hy3-preview and is
intentionally unchanged.
2026-07-08 07:09:08 -07:00
ethernet
4d7f8ade3e
feat(install): warn pip/Homebrew installs are unsupported (CLI, TUI, desktop) (#57225)
* feat(install): warn pip/Homebrew installs are unsupported (CLI, TUI, desktop)

pip and Homebrew are now Unsupported install methods per
website/docs/getting-started/platform-support.md. Surface a
warn-don't-block deprecation notice everywhere the install method is
already shown, pointing at the platform-support docs and noting these
installs will not receive further updates. NixOS (Tier 2) is untouched.

- hermes_cli/config.py: shared is_unsupported_install_method() /
  format_unsupported_install_warning() helpers so the wording and docs
  link stay consistent across every surface.
- hermes_cli/banner.py: generalize the existing pip-only banner
  warning to also cover Homebrew.
- hermes_cli/main.py: hermes update and hermes update --check print
  the warning before proceeding (still update; warn, don't block).
- tui_gateway/server.py: session.info gains install_warning.
- ui-tui: SessionPanel renders install_warning alongside the existing
  'N commits behind' notice.
- apps/desktop: SessionRuntimeInfo/GatewayEventPayload gain
  install_warning; applyRuntimeInfo + the live session.info event fire
  a snoozable warning toast via a new reportInstallMethodWarning(),
  mirroring the existing backend-contract-skew toast pattern. i18n
  strings added for en/zh/zh-hant/ja.
- Tests: updated pip banner assertions for the new wording, added a
  Homebrew banner test, and two tui_gateway session_info tests
  (install_warning present for pip, absent for git).

* fix(nix): make `hermes` in developement environment actually work

install modules as editable overlay with uv

* feat: print install method when running --version

* fix: correct detect install method when running from a subtree
2026-07-07 21:13:19 -07:00
Shannon Sands
4f620a0bbc Add WhatsApp dashboard pairing flow 2026-07-07 17:45:51 -07:00
Shannon Sands
543f069093 Fix dashboard chat model profile scoping 2026-07-07 17:44:44 -07:00
teknium1
d9a4b5a5e5 fix: validate memory provider names before filesystem lookup and setup commands
Strict charset allowlist (alnum + - _, max 64) on the {name} path param of
the memory-provider config/setup endpoints. Prevents traversal-shaped names
from reaching find_provider_dir(), and setup now 404s when neither a
loadable provider nor a plugin manifest exists, so the command-running path
is only reachable for discoverable plugins. Adds regression tests.
2026-07-07 17:27:54 -07:00
Shannon Sands
4b184cbe54 Add dashboard memory provider switching 2026-07-07 17:27:54 -07:00
Ben Barclay
8d66e78844
feat(dashboard): expose profile names + gateway_mode on gated /api/status (#60585)
The profile+gateway topology added in #60537 sits entirely behind the
loopback/--insecure auth gate. But a hosted agent (Hermes Cloud) binds
non-loopback with OAuth, so should_require_auth is True, and NAS reads
/api/status over the network (fly-provider.ts getInstanceRuntimeStatus)
with no session token. On that gated path the whole topology block was
omitted, so the Portal could never render the profile list.

Split the topology readout by sensitivity:
- profile NAMES (profiles) + gateway_mode are low-sensitivity product
  surface and now ride the always-public status body, surviving the auth
  gate so NAS/the Portal can enumerate profiles.
- the per-gateway detail (gateways[], carrying host ports) is deployment
  recon and stays gated alongside hermes_home / config_path / env_path /
  gateway_pid / gateway_health_url.

The collector now runs unconditionally (still in the executor, off the
event loop). No new fields; only the gate placement changes.
2026-07-08 00:20:13 +00:00
Teknium
5633fa19b8
fix(dashboard): advertise truecolor to the embedded chat TUI (#60576)
Headless/hosted deploys run the dashboard server without COLORTERM in
the process environment, so chalk inside the PTY-spawned TUI child
downgraded every skin hex color to the xterm 256 palette — the default
skin's bronze banner border (#CD7F32) snapped to palette 173 (#D7875F,
salmon red) and the gold caduceus rendered red/yellow on fresh cloud
instances. Local launches never reproduced it because the operator's
interactive terminal leaks COLORTERM=truecolor into the server env.

xterm.js always renders 24-bit RGB, so the dashboard PTY child should
always advertise truecolor: backfill COLORTERM=truecolor in
_resolve_chat_argv via setdefault (an explicit operator value wins).

Verified with a clean-env PTY probe of the real TUI binary:
no COLORTERM -> 0 truecolor SGRs / 165 palette-256 (salmon 38;5;173);
with the backfill -> 166 truecolor SGRs, exact bronze 38;2;205;127;50.
2026-07-07 17:17:51 -07:00
Tranquil-Flow
8a726e91ba fix(tools): enable platform-native toolsets when their composite is explicitly configured (#35527)
When a user explicitly configures a platform with its native composite
(e.g. platform_toolsets.discord: [hermes-discord]), the discord and
discord_admin toolsets were silently stripped by _DEFAULT_OFF_TOOLSETS
even though the composite contains those tools. The strip could not tell
an explicit composite opt-in apart from the unconfigured default.

Track whether the platform was explicitly configured and, when it was,
exempt toolsets that are both default-off and platform-restricted to the
current platform from the strip. Only discord/discord_admin are affected
(the sole entries in both _DEFAULT_OFF_TOOLSETS and
_TOOLSET_PLATFORM_RESTRICTIONS). Unconfigured and empty-list platforms
keep the security default-off behaviour.
2026-07-07 16:27:28 -07:00
Teknium
b062083d0a
feat(dashboard): report profile + gateway topology in /api/status (#60537)
/api/status (loopback/insecure binds only) now includes:
- profiles: every profile on the host (default + named)
- gateway_mode: none | single | multiple | multiplex
- gateways: one entry per live gateway with the host ports its
  port-binding platforms listen on, plus served_profiles when the
  default gateway is multiplexing

Ports resolve from each profile's config.yaml (top-level platforms:
wins over gateway.platforms:, matching load_gateway_config precedence)
with adapter defaults as fallback. Topology enumeration runs in an
executor so the profile scan + process-table probes stay off the event
loop, and the whole block is gated behind the same loopback-only split
as hermes_home/gateway_pid so gated binds leak nothing new.
2026-07-07 16:13:03 -07:00
doncazper
36308f0667 feat(plugins): pass approve rule keys to approval gate 2026-07-07 15:14:30 -07:00
Teknium
f304f41266 fix: harden explicit-provider gate for stale env-seeded pool entries + non-desktop picker opt-ins
Follow-up on the #56966 salvage:

- is_provider_explicitly_configured(): an env-seeded credential-pool entry
  only counts as explicit while its env var still resolves to a usable
  secret. A stale auth.json entry left behind after the user deletes the
  var no longer keeps the provider in the picker forever (#55790).
- TUI modelPicker + dashboard ModelPickerDialog/api.getModelOptions pass
  include_unconfigured=true explicitly, preserving their full-universe
  setup-affordance behavior now that the backend defaults to the
  configured subset.
- desktop lib/model-options.ts routes explicit_only through the shared
  requestModelOptions() helper (added on main after the PR branched).
- regression tests for ambient (gh_cli) pool sources, explicit manual/
  device-code sources, and stale vs live env-seeded entries.
2026-07-07 15:12:54 -07:00
Ronald Reis
37a4cf9000 fix: limit desktop model pickers to explicit providers 2026-07-07 15:12:54 -07:00
Teknium
0e04d14209
feat(sessions): trace export + HF upload via 'sessions export --format trace' (#60507)
* feat(trace): upload sessions to HF Agent Trace Viewer

Salvage trace upload as a smaller CLI-first feature: deterministic Claude Code JSONL export, fail-closed redaction, lazy Hugging Face dependency, and no gateway slash-command wiring.

* chore(trace): drop external porting references from docstrings

Describe the trace-upload design in Hermes' own terms.

* feat(sessions): fold trace upload into 'sessions export --format trace'

Integrates the HF Agent Trace Viewer exporter (PR #36145) onto the
unified export surface instead of a separate 'hermes trace' subcommand:

- --format trace: Claude Code JSONL to stdout/file, or one
  <id>.trace.jsonl per session for filtered bulk export; defaults to
  the most recent session when no --session-id/filters given.
- --upload pushes to the user's private HF traces dataset (--public to
  opt out of private); reads HF_TOKEN with guided setup when missing.
- traces are secret-redacted by default (force mode); --no-redact opts
  out after review; redaction failure blocks export (fail closed).
- hermes_cli/trace.py + subcommands/trace.py removed; agent/trace_upload.py
  is the single engine. Docs EN + zh-Hans; 4 new CLI tests.
2026-07-07 15:12:49 -07:00