feat(auxiliary): per-task reasoning_effort for auxiliary models - #64597
Merged
Conversation
Every auxiliary task block (vision, web_extract, compression,
title_generation, curator, background_review, moa_reference, ...) now
accepts a reasoning_effort shorthand:
auxiliary:
compression:
reasoning_effort: low
vision:
reasoning_effort: none
_get_task_extra_body() folds it into extra_body.reasoning, which every
auxiliary wire already translates: chat.completions passes it through,
the Codex Responses adapter maps it to top-level reasoning/include, and
the Anthropic auxiliary adapter now forwards it into
build_anthropic_kwargs(reasoning_config=...) (previously hardcoded None).
An explicit extra_body.reasoning on the same task wins over the
shorthand. Invalid levels are ignored with a warning. Empty string
(the shipped default) is a no-op — zero behavior change.
Config: reasoning_effort added to all 16 auxiliary task blocks in
DEFAULT_CONFIG (no version bump — deep-merge handles new keys).
This was referenced Jul 14, 2026
Contributor
Author
|
Correction to this PR's description and infographic: the |
This was referenced Jul 15, 2026
Closed
exiao
added a commit
to exiao/hermes-agent
that referenced
this pull request
Jul 15, 2026
…n (+ upstream merge) (#117) * feat(desktop): layout-tree model + store + workspace geometry * feat(desktop): layout-tree renderer — splits, zones, pointer drag-session, tab strip * feat(desktop): shared UI — per-session prompt overlays, gateway overlays, tab primitives * feat(desktop): routes, nav, and command palette as contributions * feat(desktop): multi-session tiles — per-profile state, tile pane, pane mirror * feat(desktop): pointer session drag/drop + row/tab menus with close others/right/all * feat(desktop): ⌘W close-tab, ⌘⇧T reopen, ⌘T new tab, ⌘1-9 + ⌃Tab tab switching * feat(desktop): chat view — drop overlays, composer scoping, tile integration * feat(desktop): session hooks — open-in-tile, per-session actions, resilient resume * feat(desktop): focused-session-aware titlebar + statusbar * feat(desktop): contribution controller, surfaces, and wiring * feat(desktop): store + lib — layout/preview/session atoms, escape-layers, keybind helpers * feat(desktop): electron — openDir IPC + ⌘W menu bridge (tabs, not windows) * refactor(desktop): retire desktop-controller for the contribution shell; views as contributions * chore(desktop): i18n strings for tabs, zones, and session menus * docs(desktop): hermes-desktop-plugins skill + starter template * chore(desktop): build config — keep tsc emit out of src, gitignore artifacts * fix(desktop): render reasoning text in the Thinking widget The Thinking disclosure rendered blank for every reasoning-emitting model (Fable, DeepSeek, GPT-5.5, ...). Two causes: 1. ReasoningTextPart read a `text` prop that assistant-ui never populates — reasoning parts arrive via context, same as text parts — so it always got an empty string. Read the text via useMessagePartReasoning() instead, mirroring how MarkdownText uses useMessagePartText(). 2. The reasoning-only SmoothStreamingText / useSmoothReveal layer stalled at revealed="": the reasoning part stays isRunning for the whole message while the answer streams and thrashes re-renders, so the char-reveal never advanced past 0. Render reasoning through the same DeferStreamingText → surface path the assistant answer uses, and drop the dead smoothing code. * fix(cron): bound SessionDB init so a hang can't wedge cron forever run_job() constructs SessionDB() synchronously with no timeout of its own, unlike the agent's run_conversation call further down, which is already bounded by HERMES_CRON_TIMEOUT. A wedged sqlite3.connect (e.g. a stale flock from a crashed sibling process) hangs this call indefinitely. That hang is invisible to every existing cron safeguard because it happens before _submit_with_guard's future exists: the finally block that discards the job ID from _running_job_ids never runs. The job stays wedged "running" — every later tick logs "already running — skipping" — until the whole gateway process is restarted. Observed in production: a cron job's worker thread was confirmed via a live py-spy thread dump to be parked inside SessionDB.__init__'s sqlite3.connect for 3+ days, silently skipping every scheduled fire in between across a gateway process that otherwise stayed healthy. Bound the SessionDB() construction with its own timeout (HERMES_CRON_SESSION_DB_TIMEOUT, default 10s), following the same bounded-thread-pool pattern already used elsewhere in this file (the delivery retry path, and the agent inactivity watchdog just below). On timeout, log at ERROR and proceed with session_db=None instead of degrading silently to debug level, since an actual hang here is a new condition worth surfacing. Adds tests/cron/test_sessiondb_init_hang.py, including an end-to-end regression proving the dispatch guard is released and a subsequent tick can fire the same job again after a simulated hang. * fix(cron): resolve SessionDB timeout from config.yaml Salvage of #63935. The original fix read HERMES_CRON_SESSION_DB_TIMEOUT from a bare env var, but AGENTS.md requires non-secret behavioral settings to live in config.yaml with an env var bridge only for backward compatibility. Changes: - Add cron.session_db_timeout_seconds to DEFAULT_CONFIG (default 10s) - Resolution order: HERMES_CRON_SESSION_DB_TIMEOUT env override → cron.session_db_timeout_seconds in config.yaml → 10s default (mirrors the existing script_timeout_seconds pattern) - 0 = unlimited (opt-in for debugging, skips the bound) - Strengthen test: assert the warning is logged on invalid env value (caplog was taken but never asserted) - Add test: verify config.yaml resolution path works end-to-end Co-authored-by: LoicHmh <26006141+LoicHmh@users.noreply.github.com> * fix(cli): persist close transcript without history alias * fix(cli): preserve resumed history during close flush Retain a distinct CLI history baseline during the signal window before a turn's normal persistence flush. When CLI history aliases the live agent list, use marker-only persistence so a genuinely unflushed tail is written. * fix(cli): serialize close persistence handoff Preserve one durable staged input across terminal close and the worker's early turn flush, without duplicating resumed transcripts or creating a session with a null prompt. Fixes #63766. * fix(cli): preserve noted staged input on close * fix(session): preserve clean multimodal persistence override * fix(session): serialize direct persistence flushes * fix(session): preserve clean shortened close snapshots * fix(cli): clear stale persistence override before staging * fix(cli): snapshot close state under staging lock * fix(session): restore clean API-local turn content * test(session): type finalizer clean-history assertions * test(cli): cover noted multimodal persistence handoff * fix(deepinfra): restore provider-prefix aliases for model parsing The _PROVIDER_PREFIXES frozenset in agent/model_metadata.py is static and does not auto-extend from ProviderProfile. Removing deepinfra and deep-infra from it broke provider:model prefix stripping for DeepInfra. * perf(tools): text prefilter before AST parse in tool discovery `_module_registers_tools()` reads each `tools/*.py` file and fully AST-parses it to check for a top-level `registry.register()` call. 90 files are scanned on every process start — but only 32 actually register tools. Add a cheap text prefilter: after reading the file (which we need to do anyway for AST), check that both `"registry"` and `"register"` appear in the source before calling `ast.parse`. A file with a top-level `registry.register()` call must contain both strings, so this is a perfect superset — zero false negatives. 50 of 90 files skip the AST parse entirely. The `source=` parameter is not threaded through `discover_builtin_tools`; the prefilter lives entirely inside `_module_registers_tools`, keeping the public API unchanged. Benchmark (median of 10 runs, scanning 90 files): before (read + ast.parse all): 305.9ms after (text prefilter + ast): 187.8ms speedup: 1.6x (118ms saved) Identical module set: 32 modules, same names, same order. * fix: recalculate safe_out from current input on each output-cap retry (#55546) The retry loop computed safe_out from the error's available_tokens, which reflected the *previous* request. Between retries the agent appends tool results and error text, so the real input token count grows. Deriving safe_out from the stale budget meant every retry still exceeded the context ceiling by 1+ tokens, burning through the 3-attempt limit. Compute safe_out from estimate_messages_tokens_rough(messages) so the cap tracks the growing input on each retry attempt. * fix: use provider available_out + request estimate for output-cap retry cap The branch computed safe_out from estimate_messages_tokens_rough(messages), but the provider rejected the larger api_messages request (system prompt, injected context, tool schemas). When API-only content is large, safe_out could far exceed the provider's available_tokens. Compute safe_out from estimate_request_tokens_rough(api_messages, tools=...) and keep provider available_out as an upper bound. Do not alter context_length or trigger compression for output-cap errors. Add production-path run_conversation tests that assert the retry API call's max_tokens, including a case where a large system prompt makes messages-only estimation undercount the real request. Fixes #55546 * test: add output-cap retry with compression disabled + fix request-pressure test * fix: exempt output-cap errors from compression-disabled guard * chore: remove trailing blank lines from test_ctx_halving_fix.py * chore: restore test_ctx_halving_fix.py to main * fix(agent): exempt parseable vLLM/LM Studio output-cap errors from compression-disabled guard Salvage of #63862. is_output_cap_error() returns False for vLLM/LM Studio error messages that contain 'prompt contains ... input tokens' (treated as input-overflow signal). But parse_available_output_tokens_from_error() CAN extract a valid available_tokens from those same messages. The compression-disabled guard only checked is_output_cap_error(), so vLLM/LM Studio users with compression off still got a terminal failure instead of the max-tokens retry. Fix: also exempt when parse_available_output_tokens_from_error() returns a value — that function determines whether the retry path can actually handle the error, so it's the right predicate for the exemption. Added test: verify vLLM-format error with compression_disabled=False still triggers the max-tokens retry path. Co-authored-by: dmabry <dmabry@users.noreply.github.com> * fix(nemo-relay): align dynamic plugin configuration Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com> * fix(windows): put Git Bash coreutils on PATH for the non-login fallback #63955 made Hermes survive a broken `bash -l` (Ainz's `Directory \drivers\etc does not exist`) by falling back to non-login `bash -c`. But a non-login shell never sources /etc/profile, so it never gets `…\usr\bin` on PATH — and that dir holds every coreutil the file/terminal tools shell out to (cat, mktemp, mv, wc, head, stat, chmod, mkdir, find). Result: `write_file` returned bytes_written:0 with an EMPTY error (the failure text went to a missing binary's stderr) and terminal commands exited 127. The survive-broken-login-bash fix was only half-done: it stopped crashing but silently failed every write. Derive Git Bash's bin dirs (mingw64/bin, usr/bin, bin, …) from the resolved bash.exe and prepend them to the subprocess PATH on Windows, in /etc/profile precedence order so coreutils win over same-named System32 tools (find.exe, sort.exe) inside the shell. No-op off Windows and when a login snapshot is healthy (the snapshot re-exports the full PATH inside the shell), so this only bites on the broken-login fallback path. Adds _git_bash_bin_dirs() (derivation, cached) + _prepend_git_bash_dirs() (PATH merge), plus regression tests for PortableGit/MinGit layouts and the run-env injection ordering. * fix(desktop): render reasoning text in the Thinking widget (#63999) * refactor(desktop): tighten reasoning part typing, drop dead useRef Self-review nits on the Thinking-widget fix: - type ReasoningTextPart as ReasoningMessagePartComponent and read the typed useMessagePartReasoning() directly, dropping the ad-hoc cast (the hook already returns text/status). - remove useRef, now unused after deleting useSmoothReveal. - trim the autopsy comments; the PR body carries the narrative. * fix(dashboard): keep memory.provider in the config schema so Desktop's dropdown survives (#63886) The dashboard's dedicated memory-provider UI (4b184cbe5) excluded memory.provider from /api/config/schema server-side. Desktop's settings page builds its field list from that schema, so the Memory Provider dropdown silently vanished from Desktop after v0.18.1. - web_server.py: restore memory.provider as a select in _SCHEMA_OVERRIDES, with options built from plugins.memory discovery (was a stale hardcoded [builtin, honcho] list before the removal) - plugins/memory: add list_memory_provider_names() — directory-scan-only name listing, safe at module import time (no provider imports) - web ConfigPage: hide memory.provider client-side instead — the Plugins page owns the dedicated provider-switching UI there - tests: schema contract (select present, category memory, builtin sentinel) + invariant that every discoverable provider is selectable * fix(desktop): restore curated declared schema for the provider panel The desktop provider panel previously rendered the curated declarations from hermes_cli/memory_providers.py: five hindsight fields, and no panel at all for undeclared providers like honcho (OAuth connect only). The dashboard provider-switching rework re-pointed the shared config route at raw plugin schemas, so the desktop began dumping every internal field (35 for hindsight) and grew a bespoke honcho panel. Serve both surfaces from the same route: ?surface=declared returns the curated schema (empty for undeclared providers) with the original config-file + env-store write semantics; the dashboard keeps the raw plugin schema unchanged. The desktop client opts into declared. * feat(gemini): improve request context for support and compatibility Include the Hermes client name and version with Gemini inference, model and tier checks, and TTS requests. Add focused coverage for the request headers and keep the Gemini-specific context scoped to Google Gemini endpoints. * fix(gemini): restrict TTS client context to official host * fix(tests): patch catalog urlopen wrapper in gemini probe tests (#64318) test_probe_sends_client_context_to_gemini and test_probe_omits_gemini_client_context_for_other_providers (added in b8eb89f5c) patch hermes_cli.models.urllib.request.urlopen, but probe_api_models routes requests through the _urlopen_model_catalog_request wrapper (open_credentialed_url from the urllib_security hardening), so the mock is never invoked and mock_urlopen.call_args is None -> TypeError. Every CI run on main and every PR has been failing test slice 7/8 on these two tests. Point the patches at _urlopen_model_catalog_request, the same target every sibling test in TestProbeApiModelsUserAgent already uses. 89/89 tests in the file now pass. * fix(moa): flatten structured message content in the advisory view (#64319) Cache-decorated turns (apply_anthropic_cache_control converts string content to [{type: text, ..., cache_control}] lists — applied BEFORE the MoA facade since the #57675 cache-cold fix) and multimodal turns (text + image_url parts) flattened to empty strings in _reference_messages, which only read str content. On turn 1 of a provider:moa session with a Claude aggregator the references received a single EMPTY user message: Anthropic-side providers 400'd ('messages: at least one message is required') while tolerant models answered 'no user request is present' (live incident Jul 14 2026, preset 'closed'). Fixes, in totality: - _reference_messages: extract visible text via agent/message_content.flatten_message_text for user/assistant/tool turns (skips image parts, so no base64 leaks into the advisory view); decorated and undecorated transcripts now produce a byte-identical advisory view (advisor cache prefix stays stable). - image-only user turns get a placeholder instead of an empty message (Anthropic rejects empty text blocks) or a silently dropped turn (would break user/assistant alternation). - degenerate-case fallback flattens structured content too. - _attach_reference_guidance: a decorated/multimodal trailing user turn now receives the guidance as a NEW text part appended AFTER the cache_control-marked part (cached prefix byte-stable) instead of falling through to a second consecutive user message (strict providers reject user/user). - conversation_loop MoA injection: multimodal user turns get the MoA context appended as a trailing text part instead of being dropped; user_prompt for the one-shot path flattens content lists instead of str()-ing them (which leaked base64 payloads into the prompt). Live-verified on the 'closed' preset (real OpenRouter wire, 2 user turns, tool loop): all 4 reference calls carry the full document + rendered tool state, end on user, zero tool-role/tool_calls; advisor cache_write 7968 then cache_read 5909+; aggregator cache_read 14880-15237 on iterations 2+. Co-authored-by: bo.fu <bo.fu@meituan.com> * feat(codex): redeem banked usage-limit resets via /usage reset (#64280) OpenAI lets ChatGPT-plan Codex users bank rate-limit reset credits, but until now they could only be redeemed from the Codex CLI/app or the website. This wires the same backend API into Hermes: - /usage on the openai-codex provider now shows "You have N resets banked - use /usage reset to activate" (parsed from the rate_limit_reset_credits field the /usage endpoint already returns). - New /usage reset subcommand (CLI + gateway) redeems one banked credit via POST .../rate-limit-reset-credits/consume with a UUID idempotency key, mirroring codex-rs backend-client semantics (PathStyle /wham vs /api/codex, ChatGPT-Account-Id header, reset/nothing_to_reset/no_credit/already_redeemed outcomes). - Guard: redemption is refused while no rate-limit window is fully exhausted, since a banked reset restores the FULL 5h + weekly allowance and spending it early wastes it. /usage reset --force overrides. Zero banked credits and non-codex providers are refused with clear messages; nothing_to_reset reports the credit was NOT spent. - i18n: new gateway.usage.unknown_subcommand / reset_wrong_provider keys across all 16 locales; docs updated (cli.md, messaging index). Tested with unit tests plus a real-socket E2E against a local fake Codex backend exercising redeem/guard/force and the /usage hint. * fix(conversation): clear stale housekeeping fallback on substantive tool-only turns A cached _last_content_with_tools response from a housekeeping-only turn could survive a later substantive tool-only turn. When the model returned an empty response, Hermes incorrectly finalized the older housekeeping narration instead of invoking the post-tool empty-response nudge. Production impact: scheduled cron jobs could return early without completing their actual work (e.g., daily report job returning a housekeeping message instead of producing the report artifact). Root cause: The fallback state was only updated when a turn had both content AND tool_calls. A turn with tool_calls but empty visible content would skip state updates entirely, leaving stale fallback state intact. Fix: Classify tools in every tool-call turn (regardless of visible content). When any tool is substantive (non-housekeeping), clear the older fallback state before processing later empty responses. This prevents two-turn-old housekeeping narration from being treated as if it belonged to the immediately preceding substantive tool turn. Regression test added: tests/run_agent/test_conversation_fallback_state.py Fixes #63860 * fix(conversation): clear _mute_post_response on substantive tool-only turn Salvage of #63888. The original fix clears stale _last_content_with_tools on substantive tool-only turns but doesn't clear _mute_post_response, which a prior housekeeping turn may have set. This suppresses tool progress output via _vprint until the no-tool-call branch resets it at line ~4834 — after all tools have finished executing. Fix: also reset _mute_post_response = False when clearing stale fallback. Added test: verify pure housekeeping turns (content + only housekeeping tools) still set the fallback correctly — the original use case the fallback was designed for. Co-authored-by: liuhao1024 <sunsky.lau@gmail.com> * fix(kanban): nudge workers that exit without complete/block Add a bounded turn-end stop guard for kanban workers. When a worker tries to exit with finish_reason=stop without having called kanban_complete or kanban_block, inject up to two synthetic nudges so the conversation loop continues instead of exiting cleanly (which the dispatcher records as protocol_violation). Mirrors the existing verify-on-stop pattern: same ephemeral scaffolding flag (_kanban_stop_synthetic), same role-alternation contract, same _pending_verification_response fallback for budget exhaustion. Disabled by default (gated on HERMES_KANBAN_TASK env var set by the dispatcher); kill switch via HERMES_KANBAN_STOP_NUDGE=0. Salvaged from #62262 by @mdc2122. The original branch was 272 commits behind main with ~538 files of stale-base reversions; this salvage applies only the 4 substantive files (agent/kanban_stop.py, conversation_loop.py insertion, run_agent.py _EPHEMERAL_SCAFFOLDING_FLAGS, tests/agent/test_kanban_stop.py). * fix(state): enforce synchronous=FULL on macOS to prevent btree corruption On Darwin, the default synchronous=NORMAL only calls fsync(), which Apple explicitly states does not guarantee data-on-platter or write-ordering. During a WAL checkpoint race with process termination (e.g., launchd shutdown), this can leave the main DB with half-written btree pages, resulting in btreeInitPage error 11 corruption. WAL mode's durability guarantee assumes the OS honors fsync barriers; macOS does not unless we explicitly set synchronous=FULL (which issues fsync() and F_FULLFSYNC via checkpoint_fullfsync=1). Previously, apply_wal_with_fallback() skipped setting synchronous=FULL when the DB was already in WAL mode, leaving connections at the unsafe synchronous=NORMAL default. This commit adds _enforce_macos_synchronous_full() to always enforce synchronous=FULL on macOS after any WAL activation. Fixes #63531 * docs(state): fix _enforce_macos_synchronous_full docstring synchronous=FULL issues plain fsync(), not F_FULLFSYNC. The F_FULLFSYNC barrier comes from checkpoint_fullfsync=1, set by the separate _apply_macos_checkpoint_barrier(). The original docstring conflated the two PRAGMAs. * fix(skills): guard skill slash commands against core-command and slug collisions scan_skill_commands() had two collision bugs in the same loop body: 1. Core-command collision: a skill whose normalized slug matches a core Hermes command name or alias (e.g. "skills", "learn", "bg") would get an auto-generated /command that shadows the core command in the gateway dispatch path (skill map is consulted before built-in handlers). The skill command silently overrode the core command. 2. Inter-skill slug collision: the seen_names set deduped on the raw frontmatter name, but the command map was keyed by the normalized slug. Two distinct names collapsing to the same slug (e.g. "git_helper" vs "git-helper") both passed the dedup, and the second silently clobbered the first. Fix: add two guards in scan_skill_commands() after slug normalization: - resolve_command(cmd_name) check skips skills colliding with any core CommandDef (name or alias), logging a warning. Uses the existing resolve_command() API so aliases and case variants are covered without a separate cache. The skill remains loadable via /skill. - cmd_key in _skill_commands check dedups on the resolved slug, first-wins (preserving local-before-external precedence), logging a warning naming the shadowed skill. Combines and supersedes #31204 (@cyrkstudios), #53450 (@Gridzilla), #50304 (@petrichor-op), and #63305 (@Vissirexa). Co-authored-by: cyrkstudios <cyrkstudios@users.noreply.github.com> Co-authored-by: Gridzilla <Gridzilla@users.noreply.github.com> Co-authored-by: petrichor-op <petrichor-op@users.noreply.github.com> Co-authored-by: Vissirexa <Vissirexa@users.noreply.github.com> * fix(kanban_db): bounded retry for clean-exit protocol violations A worker that exits 0 without calling kanban_complete/kanban_block (model stops early, transient tool wedge) tripped the failure breaker on FIRST occurrence and the task was blocked. These are overwhelmingly transient: with a bounded retry (limit 3, tracked via a violation fingerprint) ~96%% of them complete on respawn. Genuine repeat offenders still trip the breaker at the limit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * review follow-up: violation-only retry streak with defined max_retries precedence Address the hermes-sweeper review of #61233: the bounded retry budget is now a clean-exit-specific streak, not a share of the unified consecutive_failures counter. - detect_crashed_workers stamps a protocol_violation marker into the violation run's metadata (via the event payload _end_run copies); _protocol_violation_streak derives the streak from run history: consecutive most-recent violation runs, rate_limited runs neutral (mirroring their unified-counter treatment), any other closed run resets it. Mixed failure kinds can neither consume nor extend the budget. - Below-budget violations no longer call _record_task_failure at all: the task returns to ready with last_failure_error stamped directly (including the corrective retry guidance wording adopted from #61817, which build_worker_context surfaces to the retry worker) and the unified counter is untouched, keeping the two budgets independent. - At the bound the trip funnels through _record_task_failure with a new keyword-only force_trip=True: the reaper has already resolved the per-task max_retries override against the violation streak itself, so the threshold comparison is skipped rather than double-applied. max_retries keeps its documented top precedence in both directions: max_retries=1 blocks on the first violation, max_retries=5 blocks on the fifth consecutive one, unset uses the default bound of 3. - Replace the first-violation-blocks regression test with five tests: first occurrence retries (ready + guidance stamped + no gave_up + unified counter untouched); streak trips exactly at the bound with protocol_violations/protocol_violation_limit in the gave_up payload and the auto-blocked side channel set; a prior nonzero crash does not consume the violation budget; a non-violation failure between violations resets the streak; max_retries precedence both directions. All five fail against the previously reviewed diff and pass with this follow-up. The test harness resolves hermes_cli.kanban_db fresh and uses that single module object for the exit registry, liveness patch, and reaper — earlier suite tests reload the module, and the old mixed-object harness made _classify_worker_exit return unknown (the reason the old test failed in full-suite runs on main). Kanban suite: zero introduced failures vs upstream/main tip (62 vs 63 pre-existing environmental failures — the one no longer failing is the old violation test this replaces; 662 passed vs 657). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(kanban): worker-lifecycle + events table reflect the bounded protocol-violation retry Fold in kevinb361's suggested lifecycle wording (#61817 conceded in favor of this PR) and update the second stale site his sweep didn't cover: the task-events table still said the dispatcher 'auto-blocks immediately instead of retrying'. Both now describe the violation-only streak: protocol_violation fires on every violation (its payload marker feeds the budget), below-budget runs return the task to ready, and gave_up + auto-block happen only when the consecutive streak reaches _PROTOCOL_VIOLATION_FAILURE_LIMIT (default 3, per-task max_retries overriding). * follow-up: integrate agent nudge + dispatcher retry docs and tests - Nudge text now warns that repeated protocol violations will block the task and require manual intervention, so the model understands the consequence of ignoring the nudge. - Kanban docs restructured to clearly separate the two defense layers: agent-side prevention (nudge, from #64350) and dispatcher-side recovery (bounded retry, from this PR). - Two new integration tests verifying the nudge mentions blocking and that the agent-side and dispatcher-side budgets are independent. * fix(agent): validate credential pool after provider auto-detection (#63425) Provider auto-detection (URL-based inference for Anthropic, OpenAI Codex, and xAI endpoints) runs before credential-pool validation in AIAgent init, but #63048 placed the pool validation before auto-detection. When the agent is constructed with provider=None and a recognized endpoint URL, the pool is validated against an empty provider identity and discarded, even though auto-detection correctly resolves the provider moments later. Fix: move the credential-pool validation block to after the URL-based auto-detection chain. The pool is stored on the agent before auto-detection; validation now checks the resolved provider and only nullifies agent._credential_pool when the pool's scoped provider genuinely doesn't match. Regression test covers all three auto-detection paths: - Anthropic (api.anthropic.com) - OpenAI Codex (chatgpt.com/backend-api/codex) - xAI (api.x.ai) Fixes #63425. * fix(cron): keep live one-shots when running-set check fails * fix(gateway): fail closed on compression state probe errors * perf(cli): skip npm install during update when lockfile is unchanged (#17268) (cherry picked from commit 8fb6d5e910b6fd89bdc698c477cc5f039a0deabd) (cherry picked from commit 27474007b9463d7ce19d981a24aeeac552e79f48) * fix: derive skip-key manifests from npm workspaces config Review round 2 from @ethernet8023 on #61580: 1. The manifest list was a hardcoded root/ui-tui/web trio — desktop and any future workspace escaped the skip key even though step 1's root install hoists deps for every workspace. The list is now expanded from the root package.json 'workspaces' globs (npm's own source of truth): on the real repo that yields all 8 manifests incl. apps/desktop, apps/bootstrap-installer, apps/shared, and the nested ui-tui/packages/hermes-ink. Unreadable package.json falls back to root manifests only (never skips more than main would install). 2. --prefer-offline dropped entirely (this branch no longer carries #39399): local 3-run benchmarks on the repo's real manifests show the flag is noise on npm ci with a warm cache (root: 0.90s vs 0.84s avg; ws: 4.02s vs 4.00s avg) — npm ci does no resolution and the content-addressed cache already serves tarballs locally. It also carried the stale-resolution risk on the npm install fallback the reviewer flagged. All the real win is the skip itself (0s vs ~5s+). Tests: workspace-glob edit (desktop), literal-listed edit, and new-workspace-under-glob all defeat the skip; verified against the real repo's workspace config (8 manifests picked up). * test(update): document shared npm cache scope * fix(gateway): allow ws_orphan_reap rows in session recovery (#63207) Whitelist ws_orphan_reap alongside agent_close in find_latest_gateway_session_for_peer so gateway stale-routing self-heal can reopen wrongly-reaped messaging sessions instead of minting empty replacements. Layer A prevention already landed in #60609. * test(gateway): cover ws_orphan_reap session recovery (#63207) Regression tests for find_latest_gateway_session_for_peer and SessionStore stale-routing self-heal when end_reason is ws_orphan_reap. Pin manual approval mode in blocking E2E tests so smart aux-LLM resolution does not flake CI. * fix(telegram): classify and dedup post-reconnect probe failures (#63243) * docs: clarify write safety, HERMES_WRITE_SAFE_ROOT, and file-mutation verifier Document that safe-root violations are hard-blocked (not approval-gated), add a security guide section for write_file/patch guards, and link cron and verifier docs so users trust the footer over agent summaries. * fix(file-safety): distinguish safe-root write denial from credential blocks Return actionable errors when HERMES_WRITE_SAFE_ROOT blocks a path instead of labeling every denial as a protected credential file. Wire the helper through write_file, patch, delete/move, and the Copilot ACP shim; sync docs examples. * test(file-safety): add integration tests for safe-root denial messages Exercises the actual ShellFileOperations.write_file and patch_replace code paths (not just the helper in isolation) to verify that safe-root denials surface 'outside HERMES_WRITE_SAFE_ROOT' and credential-path denials surface 'protected system/credential file'. Adapted from PR #55615 by @liuhao1024. * fix(telegram): diagnose blocked-loop init hangs, unbind DoH from system DNS The #63309 hang class — gateway stuck at 'Connecting to Telegram (attempt 1/8)' with no retry, no timeout, for minutes — can only occur when the event loop thread itself is blocked in a synchronous call: _await_with_thread_deadline's timer fires off-loop, but its expiry hand-off (call_soon_threadsafe) still needs the loop to run, and the gateway's outer wait_for is a pure loop timer. When the loop is pinned, every layer goes silent simultaneously and the process wedges with no evidence of where. Two changes: 1. Loop-blocked watchdog in _await_with_thread_deadline: a second daemon timer fires one grace period (5s) after the deadline; if the loop still hasn't processed the expiry, it logs a WARNING from the timer thread and faulthandler-dumps all thread stacks to stderr — converting the silent hang into a trace that names the exact blocking frame. A threading.Event set by the expiry callback (and on normal exit) keeps completed awaits from ever being misreported. 2. discover_fallback_ips: the system-resolver leg runs socket.getaddrinfo in a worker thread with no timeout, and asyncio.gather waited on it unboundedly — a wedged OS resolver stalled discovery for minutes between the two startup log lines. Its result only feeds a log message, so it no longer gates discovery: DoH legs (already client-bounded) are gathered alone and the system leg is awaited with a _DOH_TIMEOUT cap, best-effort. Refs #63309 Tests: 3 watchdog regressions (blocked-loop dump fires; responsive-loop timeout does not; completed await does not) + 2 hung-resolver regressions (DoH results returned promptly; worst-case seed fallback stays bounded). * fix(cron): prevent long-running scheduled scripts from running twice * fix(gateway): never prune sessions when active-process check fails prune_old_entries' active-process guard failed open: when has_active_processes_fn raised, the except block logged at debug and fell through to the age check, so sessions with live background processes attached could still be pruned — violating the documented invariant that such sessions are never dropped. Add a continue so an exception in the safety check fails safe (the entry is kept). Commit 6b408e131 fixed the session_key/session_id mismatch in this same guard but left the exception path failing open. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(background_review): inherit parent's reasoning_config to preserve Anthropic cache namespace PR #17276 painstakingly pinned `_cached_system_prompt`, `session_start`, `session_id`, and the toolset config on the background-review fork so its outbound request body would byte-match the parent's and hit Anthropic's exact-prefix cache. The contributor measured a ~26% end-to-end cost reduction on Sonnet 4.5. That optimization is currently being silently undone by a missing `reasoning_config` kwarg. The fork's `AIAgent(...)` call omits it, so the fork's `reasoning_config` defaults to `None`. `anthropic_adapter.build_anthropic_kwargs` (line ~2165) then short-circuits the `thinking` / `output_config` block, and the fork's request body lands in a DIFFERENT Anthropic cache namespace from the parent's. Result on the wire: 0 `cache_read_input_tokens`, full `cache_creation_input_tokens` of the entire parent prefix — every single background review. 7 days of midagent.db traffic from one host running stock Hermes against Anthropic Sonnet: ``` Background-review FIRST calls (the moment a review fork is born): count = 68 cache_write tokens = 7,004,297 cache_read tokens = 1,016,335 Cost on Sonnet ($3.75/M write vs $0.30/M read): Spent on these writes: $26.27 Cost if they had hit parent cache instead: $2.10 WASTED: $24.16 / week / user ``` That is from one user. Multiply by Hermes's installed base for the full impact. Tested against api.anthropic.com directly (see refs/api-tests/ in the attached investigation repo if needed): | pair | cache_r | cache_w | |---------------------------------------------|---------|---------| | parent fresh | 0 | 24,047 | | parent same again | 24,047 | 0 | | fork: appends 2 new tail msgs, thinking ON | 24,047 | 22 | | fork: appends 2 new tail msgs, thinking OFF | 0 | 24,047 | Same fork-shape request, only difference is `thinking`. With the fix, the fork hits the parent's full prefix and only writes the delta (the `Review the conversation above…` prompt block, ~3-5K tokens). One line in `agent/background_review.py`: pass `reasoning_config=getattr(agent, "reasoning_config", None)` to the `AIAgent(...)` constructor of the review fork. A short comment block above it explains why so the next person who reads this code doesn't re-introduce the regression. `tests/run_agent/test_background_review_cache_parity.py` already covers the system-prompt / session-id / toolset-config parity contracts that PR #17276 introduced. I added: * a `reasoning_config` attribute to `_make_agent_stub` so the stub has a non-None parent value the test can verify is propagated. * `test_review_fork_inherits_parent_reasoning_config()` — asserts the fork's `AIAgent(...)` kwargs carry the parent's `reasoning_config`. Pre-fix this test fails with `None vs expected {'enabled': True, 'effort': 'medium'}`; post-fix all 4 tests in the file pass. ``` $ python -m pytest tests/run_agent/test_background_review_cache_parity.py -v test_review_fork_inherits_parent_cached_system_prompt PASSED test_review_fork_pins_session_start_and_session_id PASSED test_review_fork_inherits_parent_toolset_config PASSED test_review_fork_inherits_parent_reasoning_config PASSED ← new ``` Also runs against the broader background-review test suite: `test_background_review.py` (4), `test_background_review_summary.py` (8), `test_background_review_toolset_restriction.py` (3) — 19/19 pass. `agent/curator.py:1691` has the same omission for the umbrella-curation fork, but curator's prompt is "curate all skills" — it shares no prefix with any user conversation, so cache-parity is a non-issue there. Worth auditing if the curator ever takes a parent conversation as input, but not part of this PR. The `agent/auxiliary_client.py:1006` `reasoning_config=None` hardcode is intentional (title/summary one-shots on short prompts — per-call cost of namespace flip is negligible) and is also out of scope. * fix(background_review): gate reasoning_config inheritance on not-routed + dedupe recorder stubs Review follow-up to the reasoning_config cache-parity fix: - Only inherit the parent's reasoning_config when the fork runs on the parent's model (not routed). On the routed aux path (auxiliary.background_review.{provider,model}) the cache is cold regardless, so parity buys nothing, and the parent's effort vocabulary can be invalid for the routed model/provider: OpenRouter extra_body.reasoning.effort is forwarded unclamped (chat_completions.py) and codex_responses only maps max/ultra for gpt-5.6 — an exotic parent effort routed to a strict provider could 400 the review. Mirrors the existing 'not _routed' gate on _cached_system_prompt / session_start three lines below. - Add a routed-path regression test asserting reasoning_config is omitted from the fork kwargs when _resolve_review_runtime returns routed=True. - Extract the four copy-pasted recorder stubs in test_background_review_cache_parity.py into a single _make_recorder_class() factory so a new fork attribute needs one stub edit, not four. * test(telegram): define polling progress contract * fix(telegram): gate polling health on getUpdates progress * chore(release): map @Roseyco-management in AUTHOR_MAP For PR #63581 salvage (telegram: require getUpdates progress before polling is healthy). SilentKnight87 uses a noreply GitHub email which auto-skips. * test(telegram): guard PTB integration tests with importorskip CI test slices don't install python-telegram-bot (optional dep), causing a ModuleNotFoundError on collection. Add pytest.importorskip('telegram') before the PTB imports. * chore(release): map arnispiekus in AUTHOR_MAP For PR #63581 salvage (telegram: require getUpdates progress before polling is healthy). * fix(desktop): clear stale compaction status across session switches (#64127) * fix(desktop): clear stale compaction status Clear the compaction phase when a turn resumes with model or tool activity, and key response timers by session and turn so switching chats preserves elapsed time.\n\nSupersedes #48115 by porting its resumed-content approach to the current split stream hook and covering tool-first resumptions.\n\nCo-authored-by: liuhao1024 <sunsky.lau@gmail.com> * fix(desktop): resume after thinking activity * fix(desktop): clear turn timer on stop * test: remove flaky test_crashed_runner_produces_error_completion (#64431) Flaked 3 times today across 3 unrelated PRs (#64321, #64319, #64409), on two different CI shards (slice 1 and slice 8), while passing deterministically on local runs of the same SHAs. The test polls process_registry.completion_queue for 5s waiting for a daemon-thread completion event; since the durable completion delivery work (67f4e1b4a, d0e9a42ce) the crashed-runner path also writes through the sqlite-backed persistence layer, and on slow CI runners the in-memory enqueue can lose the 5s race. Coverage note: the durable-delivery suite in this file covers the completed-runner and submit-failure paths through persistence, but not a runner that raises mid-flight — that specific path loses its direct test with this removal. A deterministic (non-racing) replacement can follow separately if wanted. * fix(telegram): respect rich_messages config for pipe table routing Remove the pipe-table bypass from _rich_delivery_enabled() so that rich_messages: false is fully honoured. Previously, pipe tables were auto-routed to sendRichMessage regardless of the config flag, breaking delivery on clients without Bot API 10.1 support (AyuGram, Telegram Web, some desktop clients). Fixes #53824 * fix(agent): gate Telegram rich-Markdown hint on rich_messages config The platform hint in PLATFORM_HINTS['telegram'] always encouraged rich Markdown constructs (tables, task lists, math, collapsible details) even when rich_messages: false (the default). This caused the agent to produce formatting that MarkdownV2 cannot render, especially broken on Telegram Web. Split the hint into a base hint (MarkdownV2-compatible) and a TELEGRAM_RICH_MESSAGES_HINT extension. The extension is conditionally appended in system_prompt.py only when platforms.telegram.extra.rich_messages is true. Fixes #57122 * refactor(telegram): drop dead _content_is_pipe_table_primary helper After #53825's fix removed the auto-rich table bypass from _rich_delivery_enabled(), _content_is_pipe_table_primary() had zero callers. Remove it and simplify _rich_delivery_enabled() to the bare rich_messages opt-in check (content param no longer used). * fix: drop empty user turns from MoA advisory view (strict-provider 400) MoA's _reference_messages() unconditionally appended every user-role message to the advisory view sent to reference models, even when the message content was an empty string or a non-string/multimodal payload that the text-extraction step flattens to "". Strict providers (Kimi/Moonshot, and others that enforce non-empty user content) reject such a message with: 400 Invalid request: the message at position N with role 'user' must not be empty Lenient providers (DeepSeek) accept it, so an identical rendered view passes on one reference and 400s on another within the same fan-out — the user sees "kimi doesn't support MoA" when the real cause is an empty user turn leaking into the advisory transcript. Skip empty user turns, mirroring the existing behavior for empty assistant turns (which are already dropped when they carry no parts). The end-on-user invariant is preserved: the synthetic advisory-request user turn is still appended when the view would otherwise end on an assistant turn. Adds a regression test asserting the advisory view contains no empty user turn and still ends on a user turn. * fix(moa): scope the non-text placeholder to structured content only Follow-up to the cherry-picked empty-user-turn drop: the placeholder introduced in 8582f35d9 fired for whitespace-only STRING turns too (content=' ' flattens to non-stripping text but isn't in the (None, '', []) exclusion set), fabricating an attachment note for a turn that carried nothing. Gate the placeholder on isinstance(content, list) so only genuinely structured (e.g. image-only) turns get it; empty and whitespace-only string turns now fall through to the drop path. Edge cases verified: trailing empty user turn still ends the view on the synthetic advisory marker; an all-empty transcript degenerates to []. * chore: add neo-claw-bot to AUTHOR_MAP (PR #58465 salvage) * fix(auth): route session refresh with provider hint cookie * fix(auth): preserve provider fallback during refresh * chore(release): map unsupportedpastels in AUTHOR_MAP * feat(agent): add Upstage Solar as a model provider Adds Upstage Solar as a bundled model-provider plugin. Solar exposes an OpenAI-compatible chat-completions endpoint at https://api.upstage.ai/v1, so the generic chat_completions transport handles request/response/streaming/tool calls — the profile is the core integration. Provider registration (Upstage isn't in models.dev, so each registry that does not auto-wire from the plugin layer needs an explicit entry — same pattern as nvidia/gmi): - plugins/model-providers/upstage/: UpstageProfile + plugin.yaml. Picker default and offline catalog list only the agentic Solar Pro models, led by `solar-pro` (rolling alias for the latest Pro). default_aux_model empty so aux tasks use the main model. `solar` alias. UPSTAGE_BASE_URL overrides the host. - hermes_cli/providers.py: HERMES_OVERLAYS + label + `solar` alias, so resolve_provider_full('upstage') resolves (without this, an explicit `provider: upstage` in config was dropped and fell through to auto-detect). - hermes_cli/auth.py: PROVIDER_REGISTRY entry + `solar` alias, so `hermes doctor` / resolve_provider recognise upstage (the static-registry path the lazy profile-extension doesn't reliably cover at validation time). - hermes_cli/models.py: CANONICAL_PROVIDERS entry places Upstage Solar in the curated picker order (above the auto-appended `custom`). - agent/model_metadata.py: context-window fallbacks (/v1/models omits context_length); `solar-pro` carries the 128K Pro context as the catch-all. Reasoning: UpstageProfile.build_api_kwargs_extras wires Solar's top-level `reasoning_effort` (low|medium|high; xhigh/max→high). Reasoning-capable families are solar-pro* and solar-open*; solar-mini/syn-pro never receive it. Defaults ON at medium when unset (matches the /reasoning "medium (default)" label); `/reasoning none` disables; explicit/saved settings are honored. No reasoning_content echo handling needed (unlike DeepSeek/Kimi). Web dashboard: - web/src/pages/EnvPage.tsx: add an "Upstage Solar" provider group so UPSTAGE_API_KEY / UPSTAGE_BASE_URL appear under LLM Providers (not "Other"). Docs/tests: - .env.example: documents UPSTAGE_API_KEY / UPSTAGE_BASE_URL. - tests: profile wiring, reasoning_effort mapping (pro/open/mini, efforts, disabled, default-on), provider-resolver regression (resolve_provider_full / get_provider / solar alias / overlay), `solar-pro` default. Testing: pytest tests/providers tests/plugins/model_providers tests/hermes_cli/test_upstage_provider.py tests/run_agent/test_provider_parity.py tests/hermes_cli/test_api_key_providers.py; ruff clean. Verified end-to-end: `hermes doctor` shows "Upstage Solar", and live chat works via both `--provider upstage` and `--provider solar`. Reasoning wire format per https://console.upstage.ai/api/docs/for-agents/raw. Platforms tested: macOS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(agent): treat unknown Solar models as reasoning-capable Invert the reasoning-support check from an allow-list (solar-pro, solar-open) to a deny-list of the known non-reasoning families (solar-mini, syn-pro). Newly released Solar models now get reasoning_effort by default instead of having it silently dropped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(agent): register Upstage keys in the env-var catalog `UPSTAGE_API_KEY` / `UPSTAGE_BASE_URL` were wired through the provider resolver, auth registry, and the EnvPage grouping, but never added to `OPTIONAL_ENV_VARS` in hermes_cli/config.py. The dashboard/desktop Providers page builds its list from that catalog (`/api/env` iterates `OPTIONAL_ENV_VARS`), so with no entry the keys were never emitted and "Upstage Solar" never rendered — the EnvPage prefix group stayed empty. Add both keys under `category: "provider"` (matching gmi/minimax) so they show up in `hermes dashboard` / `hermes desktop` under "Upstage Solar". Adds a regression test asserting the catalog contains them, mirroring the existing GMI coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(agent): drop solar-open2-preview from Solar context fallbacks Remove the `solar-open2-preview` context-window entry; `solar-open2` covers the Open 2 family at the same 256K window. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(agent): drop the solar-pro rolling alias, default to solar-pro3 Pin the Upstage default to the concrete solar-pro3 instead of the solar-pro rolling alias: - plugin fallback_models is now ("solar-pro3",); entry [0] is the setup default - drop the "solar-pro" context-window fallback entry (solar-pro3 covers it) - update the reasoning default-on docstring and profile tests accordingly Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(upstage): drop manual auth/models registrations covered by profile auto-extend PROVIDER_REGISTRY, its alias map, and CANONICAL_PROVIDERS all auto-extend from registered ProviderProfiles since the provider-modules refactor (20a4f79ed). Verified with real imports: registry entry, 'solar' alias resolution via resolve_provider(), and the picker entry are identical with the manual entries removed. The hermes_cli/providers.py overlay stays (models.dev has a stale /v1/solar base URL and no UPSTAGE_BASE_URL var), and the manual OPTIONAL_ENV_VARS entries stay (non-advanced key + curated prompt text, matching the fireworks convention). * chore: add changhyun.min@gmail.com to AUTHOR_MAP (minchang, PR #42231) * fix(upstage): map 'ultra' reasoning effort to Solar's high Main added max/ultra effort levels (#62650) after this PR branched; without the mapping 'ultra' silently fell through to the medium default. Matches the xhigh/max collapse-to-strongest convention used by other profiles. * docs: add Upstage Solar to provider docs (env vars, fallback table, --provider list) * fix(upstage): collapse unknown future efforts to high; behavior-contract tests Review findings from the 4-angle pass: - Unknown-but-enabled effort levels now collapse to Solar's strongest (high) instead of silently downgrading to the medium default — guards against the next #62650-style vocabulary addition. Explicit-empty effort keeps the medium default. - fallback_models test now asserts the behavior contract (non-empty, no denied families) instead of freezing the exact model tuple (change-detector, AGENTS.md reject reason). - Drop unused pytest import in test_upstage_provider.py. * feat(config): support per-model reasoning_effort overrides Add agent.reasoning_overrides dict to config.yaml. Users can now set a reasoning_effort per model, overriding the global agent.reasoning_effort. Example: agent: reasoning_effort: "medium" # global default reasoning_overrides: "openrouter/anthropic/claude-opus-4.5": "xhigh" "openai/gpt-5": "low" "claude-sonnet-4.6": "high" # bare model name also works The helper is spelling-tolerant: override keys match regardless of provider prefix or dots-vs-dashes normalization, so users can write keys in any sensible form and they'll match. Resolution priority: 1. Session-scoped /reasoning --session override (gateway only; unchanged) 2. Per-model override from agent.reasoning_overrides (spelling-tolerant) 3. Global agent.reasoning_effort (existing) 4. Provider default (unchanged) Wired into: - CLI startup (cli.py) - Messaging gateway agent construction (gateway/run.py) - Desktop/TUI _load_reasoning_config (tui_gateway/server.py) - Cron job scheduler (cron/scheduler.py) - /model mid-session switch (agent/agent_runtime_helpers.py) + _primary_runtime now tracks reasoning_config for correct fallback recovery - Fallback activation (agent/chat_completion_helpers.py::try_activate_fallback) + Re-resolves reasoning_config for the fallback model (best-effort) Closes #21256 (per-model reasoning_effort defaults). Note: no hermes config set agent.reasoning_overrides.<model> support; users edit the YAML directly. _set_nested splits on "." and would corrupt model keys containing version dots. * refactor(reasoning): unify per-model reasoning resolution behind a single chokepoint Collapse the six per-surface copies of override-then-global resolution (CLI startup, gateway, TUI, cron, /model switch, fallback activation) onto one shared resolve_reasoning_config() in hermes_constants. Also fixes the gateway resolving reasoning against config model.default instead of the session's effective model: after a session-only /model switch, the switched model's override now applies (gateway message paths pass the resolved session model through _resolve_session_reasoning_config; /reasoning status reads the session model override). Cleanup: drop docs/PER_MODEL_REASONING.md (duplicates the website docs page), drop the change-detector _config_version test (no bump needed — deep-merge handles new keys), remove a stale plan-reference comment. Adds chokepoint contract tests (13) and gateway session-effective-model regression tests (2). * test: update stale _load_reasoning_config mocks for new model parameter Two test mocks stubbed the old zero-arg signature; the chokepoint refactor added an optional model param that call sites now pass. Swept the full test tree for other stale stubs of the changed functions — the rest use MagicMock/patch(return_value=...), which tolerate the new arg. * perf(agent): segment mixed tool batches to recover lost concurrency (#64460) A model response containing several parallel-safe reads plus one unsafe tool used to lose ALL concurrency: _should_parallelize_tool_batch was all-or-nothing, so a single barrier call (terminal, clarify, unknown tool, malformed args) forced the entire batch onto the sequential path. _plan_tool_batch_segments now splits the batch into ordered segments: maximal contiguous runs of parallel-safe calls execute on the existing concurrent path, barrier calls on the sequential path, strictly in the model's emission order. Invariants preserved: - one tool result per call, appended in emission order (segments are contiguous, so no result reordering across a barrier) - side-effect boundaries: no call starts before an earlier barrier ends - overlapping file targets split into separate ordered parallel runs - turn-end budget enforcement + /steer injection run exactly once per batch (segment executors run with finalize=False; the segmented dispatcher owns the whole-turn finalize) - interrupt during segment k drains segments k+1..n with cancelled results, keeping one result per tool_call_id Homogeneous batches keep their original single-path dispatch (zero behavior delta); _should_parallelize_tool_batch remains as a thin view over the planner for existing callers and tests. * fix(terminal): ignore stale env.cwd from a different session's cd The terminal environment is shared process-globally (collapsed to the default key), so env.cwd tracks the LAST session that ran a command. _resolve_command_cwd() trusted env.cwd unconditionally — no ownership check — so when session A left env.cwd pointing at A's checkout, session B's first terminal command inherited A's stale cwd and ran in the wrong workspace. The file tools already solved this exact shared-env problem with _live_cwd_if_owned() checking env.cwd_owner. The terminal tool never got the same guard. Fix: capture env.cwd_owner BEFORE the current session claims it, and pass it as prev_owner to _resolve_command_cwd. When the previous owner was a different session, env.cwd is stale — fall through to default_cwd (the config/override cwd for this session) instead. Once the session has claimed the env, subsequent calls in the same session still trust env.cwd so in-session state survives. * fix(desktop): clear the transcript on every cold resume so sessions can't share one resumeSession hand-rolls $messages (it paints before a runtime id is bound), and only cleared the old transcript on the cold path at entry. But a warm-cache hit can bail down to the full resume — an empty-transcript drop, or the cache being purged during the profile-swap await — without ever clearing, so the previous session's array leaked into the next one. Symptom: switching sessions kept showing the same messages (deterministic once tiling pre-warms the cache on boot). Clear $messages at the single point every cold/bail path converges, so carryover is structurally impossible; the warm fast-path still repaints in place. * feat(desktop): add a chat backdrop on/off toggle The faint statue backdrop behind the transcript was only switchable via the DEV-only leva panel. Add a persisted Appearance toggle (default on) so users can hide it; the Backdrop simply skips rendering when off. * fix(dashboard): pass backup output with -o * feat(slack): support agent view manifests * feat(slack): cover agent view assistant APIs * fix(slack): complete agent view workspace routing * fix(slack): scope Agent View workspace state * fix(slack): clear uniquely scoped assistant status * fix(slack): gate feedback buttons behind rich_blocks as documented The docs state feedback_buttons requires rich_blocks: true, but _maybe_blocks rendered full Block Kit whenever feedback_buttons alone was enabled — implicitly turning on rich-block rendering the user never opted into. Align the code with the documented contract and add a regression test. * chore(slack): bump slack-bolt to 1.29.0 and slack-sdk to 3.43.0 Slack's June 30 Agent messaging experience changelog lists Bolt Python 1.29.0 / Python SDK 3.43.0 as the Agent View minimums. Bump the messaging/slack extras and the platform.slack lazy-install pins to match, and regenerate uv.lock. All adapter API surfaces verified present against the new versions in a clean venv. * fix(mcp): materialize ResourceLink/EmbeddedResource/Audio blocks instead of dropping them MCP tool results with non-image binary resources (PDFs, archives, office docs) were silently dropped: the success path only handled TextContent and ImageContent, so a PDF-returning MCP tool appeared to return metadata only. - EmbeddedResource blob contents are decoded (50MB cap), materialized into the Hermes document cache via cache_document_from_bytes (sanitized filename, traversal-safe), and surfaced as a local-path marker the agent can read with file/terminal tools. - EmbeddedResource text contents are inlined directly. - ResourceLink blocks preserve the URI and point the agent at the server's read_resource tool; no arbitrary network fetch outside the MCP session. - AudioContent blocks are cached via cache_audio_from_bytes as MEDIA: tags. - read_resource blob contents are materialized the same way instead of returning '[binary data, N bytes]'. - Unsupported blocks are logged instead of silently discarded. - Existing ImageContent MEDIA: behavior unchanged. Reported by an enterprise customer; reproduced against an HTTP MCP server returning application/pdf resources. * fix(mcp): use real wire name in ResourceLink marker + surface resource text in isError path Follow-ups on top of #64061's salvage: - ResourceLink markers now point at mcp__<server>__read_resource (the actual registered tool name via mcp_prefixed_tool_name) instead of a nonexistent <server>_read_resource the agent could hallucinate-call. - The isError path now surfaces EmbeddedResource .resource.text blocks instead of dropping them, so error payloads carried in resources no longer collapse to a bare 'MCP tool returned an error'. (Same-class fix flagged in #64061 and independently addressed in #63576 by @alauer.) - 3 new error-path tests + updated ResourceLink wire-name assertion. * refactor(desktop): trim backdrop store to match tool-view style * feat(auxiliary): per-task reasoning_effort for auxiliary models (#64597) Every auxiliary task block (vision, web_extract, compression, title_generation, curator, background_review, moa_reference, ...) now accepts a reasoning_effort shorthand: auxiliary: compression: reasoning_effort: low vision: reasoning_effort: none _get_task_extra_body() folds it into extra_body.reasoning, which every auxiliary wire already translates: chat.completions passes it through, the Codex Responses adapter maps it to top-level reasoning/include, and the Anthropic auxiliary adapter now forwards it into build_anthropic_kwargs(reasoning_config=...) (previously hardcoded None). An explicit extra_body.reasoning on the same task wins over the shorthand. Invalid levels are ignored with a warning. Empty string (the shipped default) is a no-op — zero behavior change. Config: reasoning_effort added to all 16 auxiliary task blocks in DEFAULT_CONFIG (no version bump — deep-merge handles new keys). * fix(desktop): full-reset the thread runtime on a disjoint transcript swap The incremental external-store runtime reconciles message repositories in place (addOrUpdateMessage + prune-non-incoming). On a session switch the incoming transcript shares no ids with the current one, and grafting the new chain onto the old tree before pruning can strand a stale head/branch — the thread keeps showing the previous session. When nothing carries over there's nothing to preserve, so clear the tree first (leaves→root) then rebuild clean. Belt-and- suspenders alongside the $messages-carryover fix. * fix(cron/chronos): cache PyJWKClient across fires to stop JWKS fetch storm (#64641) The inbound cron-fire verifier constructed a fresh PyJWKClient on every fire, discarding the client's key cache and forcing a synchronous JWKS HTTP GET to the portal on each fire. Under a burst of concurrent fires (a hosted instance with several cron jobs firing in the same window) this fanned out into N simultaneous JWKS fetches that the portal rate-limited (HTTP 403 -> verification fails -> agent 401), or that blocked the event loop long enough that the fire webhook could not return its 202 before the relay's 30s timeout (observed in prod as relay 504s concentrated on high-job-count instances). Cache one PyJWKClient per JWKS URL at module scope (double-checked lock) so the signing keys are reused across fires; NAS keys rotate rarely, so the steady state is zero JWKS fetches per fire. Regression test proves 5 fires -> 1 client construction (was 5). * feat(relay): consume channel context from the connector (#64649) Phase 3 of relay-channel-context (gateway/agent side, single PR). The connector (gateway-gateway #122/#123/#124) now attaches read-only surrounding channel/group context to an addressed relay turn; this wires the gateway to consume it. - descriptor.py: additive optional supports_context (default False) on CapabilityDescriptor. from_json already filters unknown keys, so this is back-compat both directions within contract_version 1. - ws_transport.py: _event_from_wire maps the connector's read-only context[] array into the EXISTING MessageEvent.channel_context field via a new _render_relay_context() helper — reusing the same read-only injection path history-backfill uses (run.py prepends channel_context ahead of the trigger message). Never raises; absent/empty/malformed -> channel_context unset (byte-identical to today). - docs/relay-connector-contract.md: document supports_context in the §2 descriptor table (fixes the contract-doc conformance test) + the context/context_error inbound fields in §3. - tests: descriptor default/round-trip/forward-compat; _render_relay_context rendering + malformed-safe; …
1 task
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
…Research#64597) Every auxiliary task block (vision, web_extract, compression, title_generation, curator, background_review, moa_reference, ...) now accepts a reasoning_effort shorthand: auxiliary: compression: reasoning_effort: low vision: reasoning_effort: none _get_task_extra_body() folds it into extra_body.reasoning, which every auxiliary wire already translates: chat.completions passes it through, the Codex Responses adapter maps it to top-level reasoning/include, and the Anthropic auxiliary adapter now forwards it into build_anthropic_kwargs(reasoning_config=...) (previously hardcoded None). An explicit extra_body.reasoning on the same task wins over the shorthand. Invalid levels are ignored with a warning. Empty string (the shipped default) is a no-op — zero behavior change. Config: reasoning_effort added to all 16 auxiliary task blocks in DEFAULT_CONFIG (no version bump — deep-merge handles new keys).
gabrielcosi
pushed a commit
to gabrielcosi/home-ops
that referenced
this pull request
Jul 21, 2026
…7.7 ➔ v2026.7.20) (#10)
This PR contains the following updates:
| Package | Update | Change |
|---|---|---|
| [ghcr.io/gabrielcosi/hermes-agent](https://github.com/NousResearch/hermes-agent) | patch | `v2026.7.7` → `v2026.7.20` |
---
### Release Notes
<details>
<summary>NousResearch/hermes-agent (ghcr.io/gabrielcosi/hermes-agent)</summary>
### [`v2026.7.20`](https://github.com/NousResearch/hermes-agent/releases/tag/v2026.7.20): Hermes Agent v0.19.0 (2026.7.20) — The Quicksilver Release
[Compare Source](https://github.com/NousResearch/hermes-agent/compare/v2026.7.7...v2026.7.20)
##### Hermes Agent v0.19.0 (v2026.7.20)
**Release Date:** July 20, 2026
**Since v0.18.0:** \~2,245 commits · \~1,065 merged PRs · \~2,465 files changed · \~300,000 insertions · \~36,000 deletions · **\~3,300 issues closed** · **450+ community contributors**
> **The Quicksilver Release.** Hermes is the messenger god, and this window we made him move like it. First-turn time-to-first-token dropped **\~80% on every platform**, reasoning streams live by default, the desktop app got a \~20-PR speed overhaul (14× faster streaming markdown, virtualized diffs, snappy session switching), and the TUI renders markdown incrementally. Around that speed spine: you can now **manage your Nous subscription without leaving the terminal**, plug **Bitwarden and 1Password** straight into Hermes, let **smart approvals** judge flagged commands for you by default, **watch your subagents work live**, and trust that a finished response **survives a gateway crash** thanks to a durable delivery ledger. This release also rolls up everything from the v0.18.1 and v0.18.2 infrastructure patch tags — those windows are fully documented here.
***
##### ✨ Highlights
- **Hermes got dramatically faster — first token in a fraction of the time** — Cold-start "Initializing agent..." used to eat \~4.3 seconds before your first turn even reached the model; it's now \~0.9s, an \~80% cut that applies to the CLI, gateway, TUI, desktop, and cron alike. Round 2 attacked what you *see* while waiting: reasoning models now stream their thinking live by default (no more staring at a spinner for 30 seconds), and the response box paints per token instead of per line. If Hermes ever felt like it took a deep breath before answering, that breath is gone. ([#​59332](https://github.com/NousResearch/hermes-agent/pull/59332), [#​59389](https://github.com/NousResearch/hermes-agent/pull/59389) — [@​teknium1](https://github.com/teknium1))
- **The desktop app speed wave — 20+ targeted perf PRs** — Long replies used to cost 14× more CPU in the markdown splitter than they do now; giant diffs froze the review pane until we virtualized it; switching sessions thrashes layout no more. Streaming no longer re-renders the sidebar and every tool row per token, profile backends pre-warm on hover intent, and boot-hidden panes mount at idle instead of on the cold-start critical path. The net effect: the desktop app feels like a native app under load, even with huge transcripts and busy agents. ([#​67154](https://github.com/NousResearch/hermes-agent/pull/67154), [#​67818](https://github.com/NousResearch/hermes-agent/pull/67818), [#​65898](https://github.com/NousResearch/hermes-agent/pull/65898), [#​66033](https://github.com/NousResearch/hermes-agent/pull/66033), [#​66747](https://github.com/NousResearch/hermes-agent/pull/66747), [#​67742](https://github.com/NousResearch/hermes-agent/pull/67742) and more — [@​OutThisLife](https://github.com/OutThisLife))
- **Manage your Nous plan from the terminal — `/subscription` and `/topup`** — Changing your subscription used to mean a trip to the billing website. Now `/subscription` opens a full flow right in the TUI or classic CLI: see your plan and remaining allowance, preview exactly what an upgrade costs ("Pay $46.30 & upgrade now") or when a downgrade takes effect, and apply it — with scheduled-change banners and undo. The desktop app got a matching billing settings tab. Your wallet never has to leave the keyboard. ([#​51639](https://github.com/NousResearch/hermes-agent/pull/51639), [#​61054](https://github.com/NousResearch/hermes-agent/pull/61054), [#​61067](https://github.com/NousResearch/hermes-agent/pull/61067) — [@​alt-glitch](https://github.com/alt-glitch))
- **Smart approvals are now the default** — When Hermes wants to run a flagged command, an LLM reviewer now assesses it independently instead of asking you to approve every single one — and each verdict covers only that exact command, so a later command matching the same pattern gets its own review. Combined with the new **user-defined deny rules** (which block commands even under yolo mode) and `/deny <reason>` (which tells the agent *why* you refused so it course-corrects), day-to-day approval fatigue drops sharply without giving up control. ([#​62661](https://github.com/NousResearch/hermes-agent/pull/62661), [#​59164](https://github.com/NousResearch/hermes-agent/pull/59164), [#​54518](https://github.com/NousResearch/hermes-agent/pull/54518) — [@​teknium1](https://github.com/teknium1))
- **Plug your password manager into Hermes — Bitwarden & 1Password secret sources** — API keys no longer have to live in a plaintext `.env`. A new pluggable `SecretSource` interface lets Hermes fetch secrets from Bitwarden and 1Password (`op://` references) at load time, with multiple vaults enabled simultaneously, deterministic precedence, conflict warnings, and per-variable provenance. This consolidated eleven competing community PRs into one orchestrated interface — future vault providers drop in as plugins. ([#​59498](https://github.com/NousResearch/hermes-agent/pull/59498) — [@​teknium1](https://github.com/teknium1), 1Password provider salvaged from [@​hwrdprkns](https://github.com/hwrdprkns))
- **Watch your subagents work — live transcripts + durable background delegation** — `delegate_task` dispatches now return live transcript files you can `tail -f` the moment the subagents launch: every tool call, result, and streamed reply, one human-readable log per child. And background delegation completions are now **durable** — if the process restarts mid-run, results are restored and delivered through an ownership-checked ledger instead of vanishing. Fan out a fleet, watch any worker live, and never lose the results. ([#​67479](https://github.com/NousResearch/hermes-agent/pull/67479), [#​63494](https://github.com/NousResearch/hermes-agent/pull/63494) — [@​teknium1](https://github.com/teknium1))
- **A finished answer can no longer be lost — the delivery-obligation ledger** — If the gateway died between generating your response and confirming the platform actually delivered it, that answer used to be silently gone (and you'd paid for the turn). Final responses are now recorded in a durable ledger in `state.db` around the platform send and **redelivered on the next boot** — closing a P1 silent-loss window for Telegram, Discord, Slack, and every other channel. ([#​67181](https://github.com/NousResearch/hermes-agent/pull/67181) — [@​teknium1](https://github.com/teknium1))
- **One gateway, many profiles — profile-based message routing** — A single multiplexed gateway sharing one bot token can now route specific guilds, channels, or threads to different profiles — each with fully isolated config, skills, memory, and secrets. Point your work Discord server at the `work` profile and your hobby server at `personal`, from one bot. A second multiplex hardening wave means one misconfigured profile can no longer take down the whole gateway. ([#​64835](https://github.com/NousResearch/hermes-agent/pull/64835) salvaging [@​Burgunthy](https://github.com/Burgunthy), [#​65700](https://github.com/NousResearch/hermes-agent/pull/65700), [#​60589](https://github.com/NousResearch/hermes-agent/pull/60589) — [@​teknium1](https://github.com/teknium1), [@​benbarclay](https://github.com/benbarclay) + six salvaged contributors)
- **New providers and the newest frontier models** — Fireworks AI and DeepInfra land as first-class providers (Fireworks with cost estimation and a [#​2](https://github.com/NousResearch/hermes-agent/issues/2) slot in the provider picker), Upstage Solar joins via salvage, and the model catalogs picked up **GPT-5.6 (Sol/Terra/Luna + Pro variants, wired end-to-end across every route)**, **grok-4.5 (GA)**, **moonshotai/kimi-k3**, **claude-fable-5 / claude-sonnet-5**, and GA **tencent/hy3** — plus LM Studio JIT model loading for local setups. ([#​62593](https://github.com/NousResearch/hermes-agent/pull/62593), [#​63969](https://github.com/NousResearch/hermes-agent/pull/63969), [#​61616](https://github.com/NousResearch/hermes-agent/pull/61616) — [@​kshitijk4poor](https://github.com/kshitijk4poor) completing [@​rob-maron](https://github.com/rob-maron)'s [#​61578](https://github.com/NousResearch/hermes-agent/issues/61578), [#​60887](https://github.com/NousResearch/hermes-agent/pull/60887), [#​65913](https://github.com/NousResearch/hermes-agent/pull/65913), [#​64541](https://github.com/NousResearch/hermes-agent/pull/64541), [#​65472](https://github.com/NousResearch/hermes-agent/pull/65472))
- **Crank the thinking to max — new reasoning effort tiers and per-model control** — Reasoning effort gained `max` and `ultra` levels (GPT-5.6 and Codex's top tiers), selectable everywhere from the CLI to the desktop, with sane clamping on providers with smaller scales. You can now also pin **per-model reasoning-effort overrides** in config, set **per-slot effort in MoA presets** (your advisors think hard, your synthesizer stays fast), and per-task effort for auxiliary models. Thinking depth is now a dial, not a global switch. ([#​62650](https://github.com/NousResearch/hermes-agent/pull/62650), [#​64458](https://github.com/NousResearch/hermes-agent/pull/64458), [#​64631](https://github.com/NousResearch/hermes-agent/pull/64631), [#​64597](https://github.com/NousResearch/hermes-agent/pull/64597) — [@​teknium1](https://github.com/teknium1))
- **Your sessions, your data — export everything** — `hermes sessions export` now writes Markdown, Quarto, HTML, prompt-only, and even Hugging Face-ready trace formats, with the full filter surface (age, workspace, platform), an opt-in `--redact` secret-scrubbing pass, and compacted-session lineage stitched into one logical export. Pair with the new prune filters and bulk archive to keep your session store tidy. Your conversation history is a real dataset now, not a black box. ([#​60186](https://github.com/NousResearch/hermes-agent/pull/60186) salvaging [@​web3blind](https://github.com/web3blind), [#​60492](https://github.com/NousResearch/hermes-agent/pull/60492), [#​60507](https://github.com/NousResearch/hermes-agent/pull/60507), [#​59327](https://github.com/NousResearch/hermes-agent/pull/59327) — [@​teknium1](https://github.com/teknium1))
- **Security hardening round** — This window closed a long list of credential-surface gaps: Vertex credentials scoped away from subprocess env and through profile secret scopes, media/vision/image-gen local-file reads routed through one shared credential-read guard, a webhook body-size-cap sweep across every aiohttp server, bot-token redaction in Telegram transport errors, Fireworks token prefixes added to the redactor, six P1 browser/MEDIA/.env hardening PRs salvaged in one pass, and CI hardened against untrusted-ref interpolation. ([#​57660](https://github.com/NousResearch/hermes-agent/pull/57660), [#​58709](https://github.com/NousResearch/hermes-agent/pull/58709), [#​59215](https://github.com/NousResearch/hermes-agent/pull/59215), [#​56582](https://github.com/NousResearch/hermes-agent/pull/56582), [#​57842](https://github.com/NousResearch/hermes-agent/pull/57842) — [@​teknium1](https://github.com/teknium1), [@​srojk34](https://github.com/srojk34), [@​kshitijk4poor](https://github.com/kshitijk4poor), [@​jquesnelle](https://github.com/jquesnelle))
***
##### ⚡ Performance — the speed spine
##### First-turn latency (all platforms)
- **\~80% TTFT cut** — Discord capability detection off the critical path (token-keyed 24h disk cache + background refresh), Ollama probe skipped for known non-Ollama providers, agent-init blocking work removed; cold submit→dispatch \~4.3s → \~0.9s ([#​59332](https://github.com/NousResearch/hermes-agent/pull/59332) — [@​teknium1](https://github.com/teknium1))
- **Perceived-latency round 2** — `display.show_reasoning` default ON (watch the model think instead of a spinner), per-token response-box painting with width-aware force-flush, prompt-build caching, mtime-cached timezone resolution ([#​59389](https://github.com/NousResearch/hermes-agent/pull/59389) — [@​teknium1](https://github.com/teknium1))
- Segment mixed tool batches to recover lost concurrency; drop per-call base64 re-serialization from request-size estimates ([#​64460](https://github.com/NousResearch/hermes-agent/pull/64460), [#​67788](https://github.com/NousResearch/hermes-agent/pull/67788) — [@​teknium1](https://github.com/teknium1), [@​OutThisLife](https://github.com/OutThisLife))
##### Desktop speed wave
- 14× less splitter CPU via incremental block lexing for streaming markdown; virtualized review-pane diffs (no more full-Shiki freeze); snappy session switching on large transcripts; killed the layout-thrash cascade on session switch ([#​67154](https://github.com/NousResearch/hermes-agent/pull/67154), [#​67818](https://github.com/NousResearch/hermes-agent/pull/67818), [#​65898](https://github.com/NousResearch/hermes-agent/pull/65898), [#​66033](https://github.com/NousResearch/hermes-agent/pull/66033) — [@​OutThisLife](https://github.com/OutThisLife))
- Cut startup serialization + per-turn REST amplification; pre-warm profile backends and gateway sockets on hover intent; idle-mount boot-hidden panes; fast model picker + dialogs ([#​66747](https://github.com/NousResearch/hermes-agent/pull/66747), [#​66347](https://github.com/NousResearch/hermes-agent/pull/66347), [#​67857](https://github.com/NousResearch/hermes-agent/pull/67857), [#​66470](https://github.com/NousResearch/hermes-agent/pull/66470) — [@​OutThisLife](https://github.com/OutThisLife))
- Stop per-token sidebar + tool-row re-renders during streaming; stop eager JSON.stringify of every tool's args/result; scope tool-diff subscriptions; batch sidebar session slices into one profile-DB pass; targeted file-tree revalidation; rAF-coalesced sash resizes ([#​67742](https://github.com/NousResearch/hermes-agent/pull/67742), [#​67842](https://github.com/NousResearch/hermes-agent/pull/67842), [#​67195](https://github.com/NousResearch/hermes-agent/pull/67195), [#​67245](https://github.com/NousResearch/hermes-agent/pull/67245), [#​67824](https://github.com/NousResearch/hermes-agent/pull/67824), [#​67838](https://github.com/NousResearch/hermes-agent/pull/67838), [#​67844](https://github.com/NousResearch/hermes-agent/pull/67844) — [@​OutThisLife](https://github.com/OutThisLife))
- Systematized perf benchmark harness with trustworthy cold-start + first-token measurement, replacing 12 one-off scripts ([#​67466](https://github.com/NousResearch/hermes-agent/pull/67466), [#​67697](https://github.com/NousResearch/hermes-agent/pull/67697) — [@​OutThisLife](https://github.com/OutThisLife))
##### Everywhere else
- TUI renders streamed markdown incrementally per block ([#​67236](https://github.com/NousResearch/hermes-agent/pull/67236) — [@​OutThisLife](https://github.com/OutThisLife))
- Skill discovery cached by scan signature; snapshot manifest builds \~5× faster; text prefilter before AST parse in tool discovery ([#​61414](https://github.com/NousResearch/hermes-agent/pull/61414), [#​61131](https://github.com/NousResearch/hermes-agent/pull/61131), [#​63941](https://github.com/NousResearch/hermes-agent/pull/63941) — [@​kshitijk4poor](https://github.com/kshitijk4poor), [@​ethernet8023](https://github.com/ethernet8023))
- Copy-on-write message prep instead of full deepcopy; model-metadata probe-cache cluster; gateway `session.resume` model + display history from one SELECT ([#​61133](https://github.com/NousResearch/hermes-agent/pull/61133), [#​61368](https://github.com/NousResearch/hermes-agent/pull/61368), [#​67247](https://github.com/NousResearch/hermes-agent/pull/67247) — [@​kshitijk4poor](https://github.com/kshitijk4poor), [@​OutThisLife](https://github.com/OutThisLife))
- `hermes update` skips npm install when Node manifests are unchanged; dashboard session-list payloads trimmed + messages paginated ([#​61580](https://github.com/NousResearch/hermes-agent/pull/61580), [#​60883](https://github.com/NousResearch/hermes-agent/pull/60883) — [@​kshitijk4poor](https://github.com/kshitijk4poor))
- Byte-stable gateway system prompts — pinned session-context render keeps the prompt cache alive across turns ([#​67403](https://github.com/NousResearch/hermes-agent/pull/67403) — [@​kshitijk4poor](https://github.com/kshitijk4poor))
##### 🏗️ Core Agent & Architecture
##### Providers & models
- **Fireworks AI provider** with cost estimation + cached picker price columns, promoted to [#​2](https://github.com/NousResearch/hermes-agent/issues/2) in provider pickers ([#​62593](https://github.com/NousResearch/hermes-agent/pull/62593), [#​65476](https://github.com/NousResearch/hermes-agent/pull/65476), [#​65214](https://github.com/NousResearch/hermes-agent/pull/65214) — [@​teknium1](https://github.com/teknium1))
- **DeepInfra** hardened integration; **Upstage Solar** provider ([#​42231](https://github.com/NousResearch/hermes-agent/issues/42231) salvage) ([#​63969](https://github.com/NousResearch/hermes-agent/pull/63969), [#​64541](https://github.com/NousResearch/hermes-agent/pull/64541) — [@​kshitijk4poor](https://github.com/kshitijk4poor))
- **GPT-5.6 (Sol/Terra/Luna + Pro) end-to-end** — context lengths, native/Codex catalogs, pricing, compaction caps across every route ([#​61616](https://github.com/NousResearch/hermes-agent/pull/61616) — [@​kshitijk4poor](https://github.com/kshitijk4poor), building on [@​rob-maron](https://github.com/rob-maron))
- grok-4.5 (GA) catalog + reasoning allowlist; kimi-k3 on Nous Portal + OpenRouter (kimi-k2.x retired) + K3 discovery on the Kimi Coding endpoint; claude-fable-5 / claude-sonnet-5 / fugu-ultra curated; GA tencent/hy3 ([#​60887](https://github.com/NousResearch/hermes-agent/pull/60887), [#​65913](https://github.com/NousResearch/hermes-agent/pull/65913), [#​65922](https://github.com/NousResearch/hermes-agent/pull/65922), [#​56617](https://github.com/NousResearch/hermes-agent/pull/56617), [#​60943](https://github.com/NousResearch/hermes-agent/pull/60943) — [@​teknium1](https://github.com/teknium1))
- Catalog-labeled silent default (GLM-5.2) + bare-provider `/model` cost-safe routing; LM Studio JIT load mode; adaptive thinking for Kimi-family Anthropic endpoints ([#​64771](https://github.com/NousResearch/hermes-agent/pull/64771), [#​65472](https://github.com/NousResearch/hermes-agent/pull/65472), [#​67606](https://github.com/NousResearch/hermes-agent/pull/67606) — [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor))
- GLM-5.2 native reasoning\_effort controls; Gemini request-context improvements; extra HTTP headers for LLM API calls; per-client model routing on the API server ([#​58884](https://github.com/NousResearch/hermes-agent/pull/58884), [#​61873](https://github.com/NousResearch/hermes-agent/pull/61873) — [@​vishal-dharm](https://github.com/vishal-dharm), [#​57038](https://github.com/NousResearch/hermes-agent/pull/57038), [#​57028](https://github.com/NousResearch/hermes-agent/pull/57028) — [@​teknium1](https://github.com/teknium1))
- **Claude Sonnet 5 fully wired** — curated lists, intro pricing, and metadata across every route ([#​67932](https://github.com/NousResearch/hermes-agent/pull/67932) — [@​teknium1](https://github.com/teknium1))
- **Hide providers you don't use** — `enabled: false` per-provider flag + `excluded_providers` config scrub unwanted providers from `/model` pickers and built-in resolution ([#​67971](https://github.com/NousResearch/hermes-agent/pull/67971) — [@​teknium1](https://github.com/teknium1))
- Bedrock catalog wave: real context-window probing from the live endpoint, 1M-context rows for current-gen Claude + Fable, geo-prefix parity, versioned profile-ID pricing, Opus 4.8/4.7 rows ([#​68007](https://github.com/NousResearch/hermes-agent/pull/68007), [#​67977](https://github.com/NousResearch/hermes-agent/pull/67977), [#​68005](https://github.com/NousResearch/hermes-agent/pull/68005), [#​67976](https://github.com/NousResearch/hermes-agent/pull/67976) — [@​teknium1](https://github.com/teknium1))
- kimi-k3 rollout completed across Kimi-direct catalog surfaces with 1M context on canonical Kimi Coding endpoints ([#​68108](https://github.com/NousResearch/hermes-agent/pull/68108) — [@​teknium1](https://github.com/teknium1))
- Provider pickers: Qwen providers folded into one group row; collapsible provider groups in the desktop model picker; friendlier TUI model display grouping same-endpoint providers ([#​67758](https://github.com/NousResearch/hermes-agent/pull/67758), [#​67904](https://github.com/NousResearch/hermes-agent/pull/67904), [#​67908](https://github.com/NousResearch/hermes-agent/pull/67908) — [@​teknium1](https://github.com/teknium1))
##### Reasoning & MoA
- `max` + `ultra` effort levels across every surface and route ([#​62650](https://github.com/NousResearch/hermes-agent/pull/62650) — [@​teknium1](https://github.com/teknium1))
- Per-model reasoning\_effort overrides via a unified resolution chokepoint; per-task auxiliary effort; per-slot MoA preset effort; session-scoped `/reasoning` in the CLI ([#​64458](https://github.com/NousResearch/hermes-agent/pull/64458), [#​64597](https://github.com/NousResearch/hermes-agent/pull/64597), [#​64631](https://github.com/NousResearch/hermes-agent/pull/64631), [#​67946](https://github.com/NousResearch/hermes-agent/pull/67946) — [@​teknium1](https://github.com/teknium1))
- MoA: `reference_max_tokens` to cap advisor output and cut latency; per-preset fanout cadence (`user_turn` runs advisors once per user turn); stale presets surfaced without retries; half-filled preset saves rejected at the API boundary; aggregator resolves reasoning like an acting model ([#​56756](https://github.com/NousResearch/hermes-agent/pull/56756), [#​57591](https://github.com/NousResearch/hermes-agent/pull/57591), [#​64756](https://github.com/NousResearch/hermes-agent/pull/64756) — [@​teknium1](https://github.com/teknium1))
##### Delegation, approvals & the agent loop
- Live subagent transcripts + durable background completions (see Highlights) ([#​67479](https://github.com/NousResearch/hermes-agent/pull/67479), [#​63494](https://github.com/NousResearch/hermes-agent/pull/63494) — [@​teknium1](https://github.com/teknium1))
- Smart approvals default; user-defined deny rules (block even under yolo); `/deny <reason>` relays the denial reason; plugin `pre_tool_call` approve action escalates to a human gate (re-landed with rule keys) ([#​62661](https://github.com/NousResearch/hermes-agent/pull/62661), [#​59164](https://github.com/NousResearch/hermes-agent/pull/59164), [#​54518](https://github.com/NousResearch/hermes-agent/pull/54518), [#​60504](https://github.com/NousResearch/hermes-agent/pull/60504) — [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor))
- Unified delegation concurrency caps (`max_async_children` deprecated); explain long provider waits on the live status line; deterministic tool-output risk exposure ([#​56955](https://github.com/NousResearch/hermes-agent/pull/56955), [#​64775](https://github.com/NousResearch/hermes-agent/pull/64775), [#​61793](https://github.com/NousResearch/hermes-agent/pull/61793) — [@​teknium1](https://github.com/teknium1))
- Codex: live TUI/desktop tool cards for the app-server runtime, commentary streamed as visible interim messages, compaction routed through `thread/compact/start`, max-output truncation recovery, oversized message ids dropped on replay, banked usage-limit resets via `/usage reset` ([#​66514](https://github.com/NousResearch/hermes-agent/pull/66514), [#​66115](https://github.com/NousResearch/hermes-agent/pull/66115), [#​60114](https://github.com/NousResearch/hermes-agent/pull/60114), [#​58155](https://github.com/NousResearch/hermes-agent/pull/58155), [#​62225](https://github.com/NousResearch/hermes-agent/pull/62225) — [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor), [@​JoaoMarcos44](https://github.com/JoaoMarcos44), [#​64280](https://github.com/NousResearch/hermes-agent/pull/64280) — [@​teknium1](https://github.com/teknium1))
- Hooks: oversized hook-injected context spills to disk ([#​20468](https://github.com/NousResearch/hermes-agent/pull/20468) — [@​teknium1](https://github.com/teknium1))
- Vibe reactions — floating hearts on affection across CLI/TUI/desktop, token-free core detection ([#​62016](https://github.com/NousResearch/hermes-agent/pull/62016) — [@​OutThisLife](https://github.com/OutThisLife))
##### Secrets & config
- Pluggable `SecretSource` interface + Bitwarden & 1Password providers (see Highlights) ([#​59498](https://github.com/NousResearch/hermes-agent/pull/59498) — [@​teknium1](https://github.com/teknium1), [@​hwrdprkns](https://github.com/hwrdprkns))
- `hermes config get` / `unset`; warn on unknown root config keys + doctor deprecated-key reporting; `display.timestamp_format` ([#​65540](https://github.com/NousResearch/hermes-agent/pull/65540), [#​67370](https://github.com/NousResearch/hermes-agent/pull/67370), [#​40622](https://github.com/NousResearch/hermes-agent/pull/40622) — [@​teknium1](https://github.com/teknium1))
- Auxiliary model usage recorded per task in session accounting; conversation-scoped Nous Portal usage tags across aux/MoA/delegate calls; `--usage-file` JSON report for `hermes -z` ([#​65537](https://github.com/NousResearch/hermes-agent/pull/65537), [#​65468](https://github.com/NousResearch/hermes-agent/pull/65468), [#​59615](https://github.com/NousResearch/hermes-agent/pull/59615) — [@​teknium1](https://github.com/teknium1))
##### Sessions & compression
- Sessions export: Markdown/QMD/HTML/prompt-only/trace formats, HF upload, `--redact`, unified filters; full prune filter surface + bulk archive; CLI workspace filter + restore-cwd-on-resume ([#​60186](https://github.com/NousResearch/hermes-agent/pull/60186), [#​60492](https://github.com/NousResearch/hermes-agent/pull/60492), [#​60507](https://github.com/NousResearch/hermes-agent/pull/60507), [#​59327](https://github.com/NousResearch/hermes-agent/pull/59327), [#​63091](https://github.com/NousResearch/hermes-agent/pull/63091) — [@​teknium1](https://github.com/teknium1), [@​web3blind](https://github.com/web3blind))
- Compression: preserve human intent and durable handoffs; retain prompt cache when memory is unchanged; flatten multimodal content for the summarizer keeping image handles; gateway compression routing integrity ([#​67275](https://github.com/NousResearch/hermes-agent/pull/67275), [#​67916](https://github.com/NousResearch/hermes-agent/pull/67916), [#​65046](https://github.com/NousResearch/hermes-agent/pull/65046), [#​56868](https://github.com/NousResearch/hermes-agent/pull/56868) — [@​kshitijk4poor](https://github.com/kshitijk4poor), [@​teknium1](https://github.com/teknium1))
- Gateway session metadata consolidated into state.db; routing index moved to state.db (sessions.json now an optional legacy mirror); exact API bytes persisted in an `api_content` sidecar ([#​58899](https://github.com/NousResearch/hermes-agent/pull/58899), [#​59203](https://github.com/NousResearch/hermes-agent/pull/59203), [#​67274](https://github.com/NousResearch/hermes-agent/pull/67274) — [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor))
##### 🌐 Gateway, Fleet & Relay
- **Durable delivery-obligation ledger** for final responses (see Highlights) ([#​67181](https://github.com/NousResearch/hermes-agent/pull/67181) — [@​teknium1](https://github.com/teknium1))
- **Profile-based routing for inbound messages** + multiplex hardening wave 2 + `GATEWAY_MULTIPLEX_PROFILES` override (see Highlights) ([#​64835](https://github.com/NousResearch/hermes-agent/pull/64835), [#​65700](https://github.com/NousResearch/hermes-agent/pull/65700), [#​60589](https://github.com/NousResearch/hermes-agent/pull/60589) — [@​teknium1](https://github.com/teknium1), [@​benbarclay](https://github.com/benbarclay) + salvaged contributors)
- Per-session turn lease + conversation-scope funnel; unified session reset boundaries (reset sessions stay reset); truthful runtime readiness checks; per-channel model and system prompt overrides; per-session `/model` overrides persist across restarts ([#​67401](https://github.com/NousResearch/hermes-agent/pull/67401), [#​65783](https://github.com/NousResearch/hermes-agent/pull/65783), [#​62645](https://github.com/NousResearch/hermes-agent/pull/62645), [#​56967](https://github.com/NousResearch/hermes-agent/pull/56967), [#​57030](https://github.com/NousResearch/hermes-agent/pull/57030) — [@​teknium1](https://github.com/teknium1))
- Session auto-reset default off; `/sessions search <query>`; webhook payload filters + route scripts; platform HTTP event callback routing; configurable long-running status phrases ([#​60194](https://github.com/NousResearch/hermes-agent/pull/60194), [#​57685](https://github.com/NousResearch/hermes-agent/pull/57685), [#​60944](https://github.com/NousResearch/hermes-agent/pull/60944), [#​65702](https://github.com/NousResearch/hermes-agent/pull/65702), [#​58872](https://github.com/NousResearch/hermes-agent/pull/58872) — [@​teknium1](https://github.com/teknium1))
- Relay: generic OIDC client-credentials provisioning (NAS-free), routed profile carried from the connector wire source, channel context consumed from the connector; Nous auth forensics + `nous_session_valid` on `/api/status` for hosted self-heal; Docker re-seeds a terminally-dead Nous bootstrap session on boot ([#​60730](https://github.com/NousResearch/hermes-agent/pull/60730), [#​60586](https://github.com/NousResearch/hermes-agent/pull/60586), [#​64649](https://github.com/NousResearch/hermes-agent/pull/64649), [#​59976](https://github.com/NousResearch/hermes-agent/pull/59976), [#​59969](https://github.com/NousResearch/hermes-agent/pull/59969), [#​59983](https://github.com/NousResearch/hermes-agent/pull/59983) — [@​benbarclay](https://github.com/benbarclay))
##### 📱 Messaging Platforms
- **Inline choice pickers** for `/reasoning` and `/fast` on Telegram, Discord, and Matrix — one-tap native buttons instead of typing ([#​65799](https://github.com/NousResearch/hermes-agent/pull/65799) — [@​teknium1](https://github.com/teknium1))
- WhatsApp: native Baileys polls (clarify renders as a poll), locations, rich inbound metadata; dashboard pairing flow ([#​58865](https://github.com/NousResearch/hermes-agent/pull/58865), [#​60571](https://github.com/NousResearch/hermes-agent/pull/60571) — [@​teknium1](https://github.com/teknium1))
- Discord: recover messages missed during reconnect; auto-created threads renamed to generated session titles; configurable interactive view timeout; opt-in owner mentions on exec-approval prompts; optional admin-only gate for approval buttons ([#​66149](https://github.com/NousResearch/hermes-agent/pull/66149), [#​60187](https://github.com/NousResearch/hermes-agent/pull/60187), [#​60230](https://github.com/NousResearch/hermes-agent/pull/60230), [#​60493](https://github.com/NousResearch/hermes-agent/pull/60493), [#​51751](https://github.com/NousResearch/hermes-agent/pull/51751) — [@​teknium1](https://github.com/teknium1))
- Slack: live per-tool status line ([#​67080](https://github.com/NousResearch/hermes-agent/pull/67080) — [@​teknium1](https://github.com/teknium1), salvaging [#​62007](https://github.com/NousResearch/hermes-agent/issues/62007))
- Telegram: per-topic free-response allowlist; Google Chat clarify prompts rendered as cards ([#​65543](https://github.com/NousResearch/hermes-agent/pull/65543), [#​65546](https://github.com/NousResearch/hermes-agent/pull/65546) — [@​teknium1](https://github.com/teknium1))
- Voice: `stt.echo_transcripts` toggle; MEDIA: captions attached to the media bubble on standalone sends; `display.tool_progress: log` option ([#​58859](https://github.com/NousResearch/hermes-agent/pull/58859), [#​61415](https://github.com/NousResearch/hermes-agent/pull/61415), [#​57014](https://github.com/NousResearch/hermes-agent/pull/57014) — [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor))
##### 🖥️ Hermes Desktop App
- **Contribution-driven shell on a layout-tree model** — panes, zones, and layouts as data; plugin-scoped i18n locale bundles followed ([#​60638](https://github.com/NousResearch/hermes-agent/pull/60638), [#​67303](https://github.com/NousResearch/hermes-agent/pull/67303) — [@​OutThisLife](https://github.com/OutThisLife))
- **Capabilities page** — Skills/Tools/MCP + Hub in one place, with responsive overlay nav; CLI/dashboard parity for skills hub, MCP test/toggle/catalog, maintenance ops, log filters; five UX fixes from live testing ([#​57590](https://github.com/NousResearch/hermes-agent/pull/57590), [#​57441](https://github.com/NousResearch/hermes-agent/pull/57441), [#​67482](https://github.com/NousResearch/hermes-agent/pull/67482) — [@​OutThisLife](https://github.com/OutThisLife), [@​teknium1](https://github.com/teknium1))
- **Hermes Cloud connection mode** (salvage of [#​55402](https://github.com/NousResearch/hermes-agent/issues/55402)); soft gateway switch + gateway-settings polish; terminal execution backend picker with health probes ([#​61912](https://github.com/NousResearch/hermes-agent/pull/61912), [#​61916](https://github.com/NousResearch/hermes-agent/pull/61916), [#​67203](https://github.com/NousResearch/hermes-agent/pull/67203) — [@​OutThisLife](https://github.com/OutThisLife), [@​teknium1](https://github.com/teknium1))
- Keybind hint tooltips + keybinds settings tab + unified worktree dialog; base-branch picker for new worktrees; green unread dot for background-finished sessions; background-task sidebar indicators; grouped tool calls across text-less messages; auto-scrolling window for long tool-call runs ([#​65204](https://github.com/NousResearch/hermes-agent/pull/65204), [#​62243](https://github.com/NousResearch/hermes-agent/pull/62243), [#​65109](https://github.com/NousResearch/hermes-agent/pull/65109), [#​65174](https://github.com/NousResearch/hermes-agent/pull/65174), [#​61147](https://github.com/NousResearch/hermes-agent/pull/61147), [#​57913](https://github.com/NousResearch/hermes-agent/pull/57913) — [@​ethernet8023](https://github.com/ethernet8023), [@​OutThisLife](https://github.com/OutThisLife))
- Session + project color system (inherit from project, per-session override, shared across sidebar/tabs); unified active-project identity in chat status; workspace path status action ([#​67469](https://github.com/NousResearch/hermes-agent/pull/67469), [#​67681](https://github.com/NousResearch/hermes-agent/pull/67681), [#​67282](https://github.com/NousResearch/hermes-agent/pull/67282), [#​63086](https://github.com/NousResearch/hermes-agent/pull/63086) — [@​OutThisLife](https://github.com/OutThisLife))
- Declarative memory-provider panel + full-config modal; config-defined TTS/STT providers + xAI TTS params; custom endpoint settings; per-job cron model picker; profile-aware approval mode control; UI scale setting; Ctrl/Cmd+wheel zoom; chat backdrop toggle; `/journey` opens the memory graph overlay ([#​67206](https://github.com/NousResearch/hermes-agent/pull/67206) salvaging [@​erosika](https://github.com/erosika), [#​67209](https://github.com/NousResearch/hermes-agent/pull/67209), [#​67759](https://github.com/NousResearch/hermes-agent/pull/67759) — [@​austinpickett](https://github.com/austinpickett), [#​67472](https://github.com/NousResearch/hermes-agent/pull/67472), [#​63520](https://github.com/NousResearch/hermes-agent/pull/63520), [#​60457](https://github.com/NousResearch/hermes-agent/pull/60457), [#​67029](https://github.com/NousResearch/hermes-agent/pull/67029), [#​64598](https://github.com/NousResearch/hermes-agent/pull/64598), [#​57267](https://github.com/NousResearch/hermes-agent/pull/57267) — [@​teknium1](https://github.com/teknium1), [@​OutThisLife](https://github.com/OutThisLife))
- Full TypeScript conversion of the desktop tree ([#​57855](https://github.com/NousResearch/hermes-agent/pull/57855) — [@​ethernet8023](https://github.com/ethernet8023))
##### 📊 Web Dashboard
- Memory provider switching; safe session import flow; WhatsApp pairing; Discord-specific toolsets editable from the web UI; clarified manual Telegram bot setup ([#​60569](https://github.com/NousResearch/hermes-agent/pull/60569), [#​63699](https://github.com/NousResearch/hermes-agent/pull/63699), [#​60571](https://github.com/NousResearch/hermes-agent/pull/60571), [#​65361](https://github.com/NousResearch/hermes-agent/pull/65361), [#​64636](https://github.com/NousResearch/hermes-agent/pull/64636) — [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor), [@​shannonsands](https://github.com/shannonsands))
- Terminal keep-alive + reattach for dashboard chat sessions; heavy turns isolated in a compute host; paste/drop images into Chat; `browser.headed` schema toggle; profile + gateway topology on `/api/status`; mobile/hosted OpenAI OAuth login ([#​60515](https://github.com/NousResearch/hermes-agent/pull/60515), [#​65895](https://github.com/NousResearch/hermes-agent/pull/65895), [#​61929](https://github.com/NousResearch/hermes-agent/pull/61929), [#​67046](https://github.com/NousResearch/hermes-agent/pull/67046), [#​60537](https://github.com/NousResearch/hermes-agent/pull/60537), [#​61330](https://github.com/NousResearch/hermes-agent/pull/61330) — [@​teknium1](https://github.com/teknium1), [@​OutThisLife](https://github.com/OutThisLife), [@​benbarclay](https://github.com/benbarclay))
- `hermes serve` is a true headless backend (no web UI build/mount) ([#​55923](https://github.com/NousResearch/hermes-agent/pull/55923) — [@​OutThisLife](https://github.com/OutThisLife))
##### 🧰 CLI & TUI
- `/subscription` + `/topup` terminal billing (see Highlights) ([#​51639](https://github.com/NousResearch/hermes-agent/pull/51639) — [@​alt-glitch](https://github.com/alt-glitch))
- **`/model --once`** — one-turn model override that reverts automatically ([#​67113](https://github.com/NousResearch/hermes-agent/pull/67113) — [@​teknium1](https://github.com/teknium1), salvaging [#​29923](https://github.com/NousResearch/hermes-agent/issues/29923))
- **Stacked slash-skill invocations** — `/skill-a /skill-b do XYZ` loads both skills in order (Claude Code port), with autocomplete + ghost text ([#​57987](https://github.com/NousResearch/hermes-agent/pull/57987), [#​58763](https://github.com/NousResearch/hermes-agent/pull/58763) — [@​teknium1](https://github.com/teknium1))
- `--safe-mode` troubleshooting flag; uninstall dry-run; TLS failures fail fast with fix hints; `/compact` alias + preview flags; pip/Homebrew installs warned unsupported ([#​45300](https://github.com/NousResearch/hermes-agent/pull/45300), [#​60111](https://github.com/NousResearch/hermes-agent/pull/60111), [#​57992](https://github.com/NousResearch/hermes-agent/pull/57992), [#​57029](https://github.com/NousResearch/hermes-agent/pull/57029), [#​57225](https://github.com/NousResearch/hermes-agent/pull/57225) — [@​teknium1](https://github.com/teknium1), [@​ethernet8023](https://github.com/ethernet8023))
- TUI: model picker refresh support; custom skill bundles dispatched as agent turns; banner sizes skills display to terminal width ([#​59782](https://github.com/NousResearch/hermes-agent/pull/59782) — [@​helix4u](https://github.com/helix4u), [#​62859](https://github.com/NousResearch/hermes-agent/pull/62859) — [@​Adolanium](https://github.com/Adolanium), [#​40624](https://github.com/NousResearch/hermes-agent/pull/40624) — [@​teknium1](https://github.com/teknium1))
- Hermes Console REPL + perf follow-ups; `hermes curator usage` all-skills view; entry-point plugins surfaced in `hermes plugins list` ([#​57781](https://github.com/NousResearch/hermes-agent/pull/57781) — [@​kshitijk4poor](https://github.com/kshitijk4poor), [#​36727](https://github.com/NousResearch/hermes-agent/pull/36727), [#​40623](https://github.com/NousResearch/hermes-agent/pull/40623) — [@​teknium1](https://github.com/teknium1))
##### 🔧 Tool System, Skills & MCP
- MCP: `mcp__server__tool` naming convention; server log notifications surfaced in agent.log; hosted OAuth completed across Dashboard + Desktop; configurable `redirect_uri`/`redirect_host` for proxied/WAF setups; OAuth callback port races closed; Blender added to the MCP catalog with a curated 4-tool default ([#​52750](https://github.com/NousResearch/hermes-agent/pull/52750), [#​57416](https://github.com/NousResearch/hermes-agent/pull/57416), [#​66151](https://github.com/NousResearch/hermes-agent/pull/66151), [#​65610](https://github.com/NousResearch/hermes-agent/pull/65610), [#​65622](https://github.com/NousResearch/hermes-agent/pull/65622), [#​64463](https://github.com/NousResearch/hermes-agent/pull/64463) — [@​teknium1](https://github.com/teknium1), [@​benbarclay](https://github.com/benbarclay))
- Skills: `security/unbroker` (autonomous data-broker removal) + blind opt-out hardening; `unreal-mcp` companion skill; blender-mcp reworked around the catalog entry; humanizer pattern expansion; `mcp-oauth-remote-gateway` optional skill ([#​57438](https://github.com/NousResearch/hermes-agent/pull/57438), [#​57902](https://github.com/NousResearch/hermes-agent/pull/57902), [#​65989](https://github.com/NousResearch/hermes-agent/pull/65989), [#​64715](https://github.com/NousResearch/hermes-agent/pull/64715) — [@​SHL0MS](https://github.com/SHL0MS), [#​65066](https://github.com/NousResearch/hermes-agent/pull/65066), [#​65486](https://github.com/NousResearch/hermes-agent/pull/65486) — [@​teknium1](https://github.com/teknium1))
- Browser: full snapshots stored on truncation, eval denylist opt-in; computer\_use follows cua-driver's verify→escalate ladder ([#​65923](https://github.com/NousResearch/hermes-agent/pull/65923), [#​67123](https://github.com/NousResearch/hermes-agent/pull/67123) — [@​teknium1](https://github.com/teknium1))
- Kanban: modal create-task dialog + editable board project directory; Done-card results made obvious; grab-to-pan board scrolling; attachment toolset + CLI with SSRF-guarded URL fetch; project directory captured at board creation ([#​66333](https://github.com/NousResearch/hermes-agent/pull/66333), [#​63638](https://github.com/NousResearch/hermes-agent/pull/63638), [#​60226](https://github.com/NousResearch/hermes-agent/pull/60226), [#​65698](https://github.com/NousResearch/hermes-agent/pull/65698), [#​63249](https://github.com/NousResearch/hermes-agent/pull/63249) — [@​teknium1](https://github.com/teknium1))
- Cron: durable execution audit history; one-shot stale-removal race fixed; run-claim TTL derived from HERMES\_CRON\_TIMEOUT ([#​61791](https://github.com/NousResearch/hermes-agent/pull/61791) — [@​teknium1](https://github.com/teknium1), [#​62014](https://github.com/NousResearch/hermes-agent/pull/62014) — [@​PRATHAMESH75](https://github.com/PRATHAMESH75), [#​59567](https://github.com/NousResearch/hermes-agent/pull/59567))
- mem0: self-hosted dashboard backend + recall tuning + setup-wizard mode ([#​56943](https://github.com/NousResearch/hermes-agent/pull/56943), [#​60494](https://github.com/NousResearch/hermes-agent/pull/60494) — [@​kshitijk4poor](https://github.com/kshitijk4poor), [@​teknium1](https://github.com/teknium1))
- Image gen: Codex image inputs; unsupported Codex image accounts classified; tool args recursively normalized by schema (cline port) ([#​57017](https://github.com/NousResearch/hermes-agent/pull/57017), [#​63627](https://github.com/NousResearch/hermes-agent/pull/63627), [#​52220](https://github.com/NousResearch/hermes-agent/pull/52220) — [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor))
##### 🔒 Security & Reliability
- Vertex: credential/project/region resolution through the profile secret scope; `VERTEX_CREDENTIALS_PATH`/`GOOGLE_APPLICATION_CREDENTIALS` stripped from subprocess env ([#​56680](https://github.com/NousResearch/hermes-agent/pull/56680), [#​56582](https://github.com/NousResearch/hermes-agent/pull/56582) — [@​srojk34](https://github.com/srojk34))
- Six P1 hardening PRs salvaged in one pass — browser guards, MEDIA anchoring, .env lockdown, delegate ACP transport ([#​57660](https://github.com/NousResearch/hermes-agent/pull/57660) — [@​teknium1](https://github.com/teknium1))
- Media/vision/image-gen local-file reads routed through the shared credential-read guard; native image routing guarded by file-safety policy; unified image-source resolver + terminal-backend confinement ([#​58709](https://github.com/NousResearch/hermes-agent/pull/58709), [#​58752](https://github.com/NousResearch/hermes-agent/pull/58752), [#​57890](https://github.com/NousResearch/hermes-agent/pull/57890) — [@​teknium1](https://github.com/teknium1))
- Webhook body-cap sweep: explicit `client_max_size` on 3 uncapped aiohttp servers + completion sweep; Raft chunked-request body limit; timestamp-bound V2 webhook signatures ([#​59180](https://github.com/NousResearch/hermes-agent/pull/59180), [#​59215](https://github.com/NousResearch/hermes-agent/pull/59215), [#​58902](https://github.com/NousResearch/hermes-agent/pull/58902), [#​58508](https://github.com/NousResearch/hermes-agent/pull/58508) — [@​teknium1](https://github.com/teknium1), [@​srojk34](https://github.com/srojk34))
- Redaction: Fireworks token prefixes + Telegram transport errors; env-lookup false positives fixed for KEY=value and JSON/YAML config fields; bot tokens scrubbed from Telegram connect/send errors ([#​58501](https://github.com/NousResearch/hermes-agent/pull/58501), [#​58534](https://github.com/NousResearch/hermes-agent/pull/58534), [#​58915](https://github.com/NousResearch/hermes-agent/pull/58915), [#​58893](https://github.com/NousResearch/hermes-agent/pull/58893) — [@​teknium1](https://github.com/teknium1))
- computer-use: subprocess env sanitized across all five cua-driver spawn sites ([#​58889](https://github.com/NousResearch/hermes-agent/pull/58889), [#​59165](https://github.com/NousResearch/hermes-agent/pull/59165) — [@​teknium1](https://github.com/teknium1))
- Dashboard: managed-files credential guard widened past .env + dir-tree gap closed; OAuth token TOCTOU closed with atomic 0o600 writes; stale dashboards can't recreate deleted profiles ([#​58222](https://github.com/NousResearch/hermes-agent/pull/58222) — [@​kshitijk4poor](https://github.com/kshitijk4poor), [#​60236](https://github.com/NousResearch/hermes-agent/pull/60236) — [@​teknium1](https://github.com/teknium1), [#​49435](https://github.com/NousResearch/hermes-agent/pull/49435) — [@​LeonSGP43](https://github.com/LeonSGP43))
- CI: untrusted refs passed through env, not `run:` interpolation; JS/TS tests wired into CI with source-regex tests banned; js-autofix pushes via PR instead of direct-to-main ([#​57842](https://github.com/NousResearch/hermes-agent/pull/57842) — [@​jquesnelle](https://github.com/jquesnelle), [#​60707](https://github.com/NousResearch/hermes-agent/pull/60707), [#​65186](https://github.com/NousResearch/hermes-agent/pull/65186) — [@​ethernet8023](https://github.com/ethernet8023))
- Docker: terminal network toggle with full-path coverage; Git Bash Mandatory-ASLR install failures detected; Windows updater console hidden during handoff ([#​59149](https://github.com/NousResearch/hermes-agent/pull/59149) — [@​teknium1](https://github.com/teknium1), [#​64651](https://github.com/NousResearch/hermes-agent/pull/64651), [#​66040](https://github.com/NousResearch/hermes-agent/pull/66040) — [@​helix4u](https://github.com/helix4u))
- Anthropic: request-local clients so the stale/interrupt watchdog never corrupts SQLite; per-profile OAuth file; OAuth login 429 fixed (UA must not be claude-code/) ([#​67238](https://github.com/NousResearch/hermes-agent/pull/67238) — [@​OutThisLife](https://github.com/OutThisLife), [#​59339](https://github.com/NousResearch/hermes-agent/pull/59339), [#​58178](https://github.com/NousResearch/hermes-agent/pull/58178) — [@​teknium1](https://github.com/teknium1))
- Gateway/agent: tool\_call\_id deduplicated across pre-API sanitizers; background review inherits parent reasoning\_config for Anthropic cache parity; `/new` memory extraction moved off the command path ([#​58350](https://github.com/NousResearch/hermes-agent/pull/58350), [#​64379](https://github.com/NousResearch/hermes-agent/pull/64379), [#​61139](https://github.com/NousResearch/hermes-agent/pull/61139) — [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor))
##### 🔁 Reverted in this window (for the record)
- iron-proxy credential-injection egress firewall ([#​30179](https://github.com/NousResearch/hermes-agent/issues/30179) → reverted in [#​58489](https://github.com/NousResearch/hermes-agent/pull/58489)) — not shipping in this release
- dynamic-workflow orchestration skill (landed, then reverted) — not shipping
- memory provider-actions extension point (landed, then reverted) — not shipping
- Note: the plugin `pre_tool_call` approve escalation was reverted mid-window but **re-landed** in [#​60504](https://github.com/NousResearch/hermes-agent/pull/60504) and ships in this release.
##### 👥 Contributors
**450+ people** contributed to this release (via commits, co-author trailers, and salvaged PRs) — the biggest contributor window yet. Thank you, all of you.
##### Core team
- [@​teknium1](https://github.com/teknium1) — release lead; TTFT perf wave, delivery + delegation durability, smart approvals, SecretSource, gateway multiplex + profile routing, sessions export, security round, and a \~290-PR community salvage burn
- [@​OutThisLife](https://github.com/OutThisLife) — desktop app (the speed wave, layout-tree shell, Capabilities page, session colors, vibe reactions, TUI incremental markdown, perf harness)
- [@​kshitijk4poor](https://github.com/kshitijk4poor) — GPT-5.6 end-to-end, DeepInfra + Upstage Solar providers, perf cluster, compression integrity, mem0, dashboard guards
- [@​ethernet8023](https://github.com/ethernet8023) — CI overhaul (JS/TS tests wired in, autofix-via-PR, python speedups), desktop keybinds/worktrees/status indicators, full desktop TypeScript conversion
- [@​benbarclay](https://github.com/benbarclay) — relay OIDC provisioning, gateway multiplex override, Nous auth self-heal, hosted MCP OAuth groundwork
- [@​alt-glitch](https://github.com/alt-glitch) — terminal billing (`/subscription`, `/topup`), desktop billing tab
- [@​helix4u](https://github.com/helix4u) — desktop provider/model UX, TUI model picker refresh, Windows install/updater hardening
- [@​austinpickett](https://github.com/austinpickett) — desktop custom endpoint settings
- [@​SHL0MS](https://github.com/SHL0MS) — unbroker + unreal-mcp skills, humanizer expansion
##### Top community contributors
- [@​srojk34](https://github.com/srojk34) — security hardening: Vertex credential/project/region scoping through the profile secret scope, subprocess env stripping, Raft chunked-request body limits
- [@​HexLab98](https://github.com/HexLab98) — 11 fixes across MCP capability gating, Windows installer PATH, desktop cron editing, gateway systemd warnings
- [@​UnathiCodex](https://github.com/UnathiCodex) — desktop stability: zoom across display moves, LaTeX rendering, resume-stall and runtime-readiness fixes
- [@​xxxigm](https://github.com/xxxigm) — `<think>` leak fix after thinking-only retry flush, dashboard auth/theme/PTY fixes
- [@​erosika](https://github.com/erosika) — desktop declarative memory-provider panel + honcho recall/timeout correctness
- [@​Frowtek](https://github.com/Frowtek) — credential security: master stores never mounted into skill sandboxes, live-transcript redaction, dashboard api\_key precedence
- [@​necoweb3](https://github.com/necoweb3) — browser private-page CDP guard, cron one-shot liveness, gateway compression fail-closed
- [@​DavidMetcalfe](https://github.com/DavidMetcalfe) — desktop updater version pill, Local/custom endpoint exposure, sidebar collapse behavior
- [@​shannonsands](https://github.com/shannonsands) — dashboard: mobile channel setup, Discord toolsets from web UI, Telegram setup clarity
- [@​vishal-dharm](https://github.com/vishal-dharm) — Gemini request-context improvements
- [@​PRATHAMESH75](https://github.com/PRATHAMESH75) — cron one-shot stale-removal race, dashboard multiplex port-binding guard
- [@​alelpoan](https://github.com/alelpoan), [@​embwl0x](https://github.com/embwl0x), [@​Adolanium](https://github.com/Adolanium), [@​giggling-ginger](https://github.com/giggling-ginger), [@​Drexuxux](https://github.com/Drexuxux), [@​frizikk](https://github.com/frizikk), [@​JoaoMarcos44](https://github.com/JoaoMarcos44), [@​wesleysimplici](https://github.com/wesleysimplici), [@​LeonSGP43](https://github.com/LeonSGP43), [@​pierrenode](https://github.com/pierrenode), [@​simpolism](https://github.com/simpolism), [@​MorAlekss](https://github.com/MorAlekss), [@​r266-tech](https://github.com/r266-tech), [@​WadydX](https://github.com/WadydX), [@​nv-kasikritc](https://github.com/nv-kasikritc) — targeted fixes across desktop, TUI, gateway, cron, webhook, nix, and browser surfaces
- Salvaged-work authors whose PRs were cherry-picked with credit this window: [@​Burgunthy](https://github.com/Burgunthy) (profile routing), [@​web3blind](https://github.com/web3blind) (sessions export), [@​hwrdprkns](https://github.com/hwrdprkns) (1Password), [@​Christopher-Schulze](https://github.com/Christopher-Schulze), [@​Ahmett101](https://github.com/Ahmett101), [@​sjiangtao2024](https://github.com/sjiangtao2024), and many more — see the salvage PR bodies for full attribution
##### All contributors
[@​0-CYBERDYNE-SYSTEMS-0](https://github.com/0-CYBERDYNE-SYSTEMS-0), [@​0disoft](https://github.com/0disoft), [@​0xbyt4](https://github.com/0xbyt4), [@​100yenadmin](https://github.com/100yenadmin), [@​17324393074](https://github.com/17324393074), [@​2751738943](https://github.com/2751738943), [@​8294](https://github.com/8294), [@​abhibansal-sg](https://github.com/abhibansal-sg),
[@​adambiggs](https://github.com/adambiggs), [@​Adolanium](https://github.com/Adolanium), [@​aeyeopsdev](https://github.com/aeyeopsdev), [@​aguung](https://github.com/aguung), [@​AhmetArif0](https://github.com/AhmetArif0), [@​Ahmett101](https://github.com/Ahmett101), [@​ai-ag2026](https://github.com/ai-ag2026), [@​AIalliAI](https://github.com/AIalliAI), [@​ajzrva-sys](https://github.com/ajzrva-sys),
[@​alastraz](https://github.com/alastraz), [@​alelpoan](https://github.com/alelpoan), [@​alex-fireworks](https://github.com/alex-fireworks), [@​alex-heritier](https://github.com/alex-heritier), [@​alex107ivanov](https://github.com/alex107ivanov), [@​AlexFucuson9](https://github.com/AlexFucuson9), [@​Alix-007](https://github.com/Alix-007),
[@​allenliang2022](https://github.com/allenliang2022), [@​Almurat123](https://github.com/Almurat123), [@​AlsayedHoota](https://github.com/AlsayedHoota), [@​alt-glitch](https://github.com/alt-glitch), [@​alvarosanchez](https://github.com/alvarosanchez), [@​amanning3390](https://github.com/amanning3390), [@​AmAzing129](https://github.com/AmAzing129),
[@​AndreasHiltner](https://github.com/AndreasHiltner), [@​andrewhomeyer](https://github.com/andrewhomeyer), [@​annguyenNous](https://github.com/annguyenNous), [@​ansel-f](https://github.com/ansel-f), [@​antydizajn](https://github.com/antydizajn), [@​arminanton](https://github.com/arminanton), [@​arnispiekus](https://github.com/arnispiekus), [@​asimons81](https://github.com/asimons81),
[@​asscan](https://github.com/asscan), [@​ats3v](https://github.com/ats3v), [@​austinlaw076](https://github.com/austinlaw076), [@​austinpickett](https://github.com/austinpickett), [@​avifenesh](https://github.com/avifenesh), [@​aydnOktay](https://github.com/aydnOktay), [@​Bartok9](https://github.com/Bartok9), [@​bautrey](https://github.com/bautrey), [@​bbednarski9](https://github.com/bbednarski9),
[@​bbopen](https://github.com/bbopen), [@​benbarclay](https://github.com/benbarclay), [@​bigstar0920](https://github.com/bigstar0920), [@​binhnt92](https://github.com/binhnt92), [@​bird](https://github.com/bird), [@​Black0Fox0](https://github.com/Black0Fox0), [@​BlackishGreen33](https://github.com/BlackishGreen33), [@​bo](https://github.com/bo).fu, [@​brendandebeasi](https://github.com/brendandebeasi),
[@​briandevans](https://github.com/briandevans), [@​BROCCOLO1D](https://github.com/BROCCOLO1D), [@​Bruce-anle](https://github.com/Bruce-anle), [@​brunz-me](https://github.com/brunz-me), [@​Burgunthy](https://github.com/Burgunthy), [@​bytesnail](https://github.com/bytesnail), [@​catbearlove1-lang](https://github.com/catbearlove1-lang), [@​Cdddo](https://github.com/Cdddo),
[@​cgarwood82](https://github.com/cgarwood82), [@​CharmingGroot](https://github.com/CharmingGroot), [@​chouqin](https://github.com/chouqin), [@​Christopher-Schulze](https://github.com/Christopher-Schulze), [@​claudlos](https://github.com/claudlos), [@​CocaKova](https://github.com/CocaKova), [@​Code-suphub](https://github.com/Code-suphub), [@​CodeForgeNet](https://github.com/CodeForgeNet),
[@​craigdfrench](https://github.com/craigdfrench), [@​CrazyBoyM](https://github.com/CrazyBoyM), [@​crazywriter1](https://github.com/crazywriter1), [@​cresslank](https://github.com/cresslank), [@​cruzanstx](https://github.com/cruzanstx), [@​cyrkstudios](https://github.com/cyrkstudios), [@​danilofalcao](https://github.com/danilofalcao),
[@​datachainsystems](https://github.com/datachainsystems), [@​DatTheMaster](https://github.com/DatTheMaster), [@​davidb73-hub](https://github.com/davidb73-hub), [@​davidgut1982](https://github.com/davidgut1982), [@​DavidMetcalfe](https://github.com/DavidMetcalfe), [@​davidrobertson](https://github.com/davidrobertson),
[@​deacon-botdoctor](https://github.com/deacon-botdoctor), [@​DECK6](https://github.com/DECK6), [@​deepujain](https://github.com/deepujain), [@​derek2000139](https://github.com/derek2000139), [@​designnotdrum](https://github.com/designnotdrum), [@​deusyu](https://github.com/deusyu), [@​devatnull](https://github.com/devatnull), [@​devorun](https://github.com/devorun),
[@​dexhunter](https://github.com/dexhunter), [@​dfein38347g](https://github.com/dfein38347g), [@​Dhravya](https://github.com/Dhravya), [@​DictatorBacon](https://github.com/DictatorBacon), [@​digitalbase](https://github.com/digitalbase), [@​dlkakbs](https://github.com/dlkakbs), [@​dmabry](https://github.com/dmabry), [@​DNAlec](https://github.com/DNAlec), [@​dodo-reach](https://github.com/dodo-reach),
[@​doncazper](https://github.com/doncazper), [@​dorokuma](https://github.com/dorokuma), [@​doxe0x](https://github.com/doxe0x), [@​Drexuxux](https://github.com/Drexuxux), [@​dschnurbusch](https://github.com/dschnurbusch), [@​Dusk1e](https://github.com/Dusk1e), [@​EdderTalmor](https://github.com/EdderTalmor), [@​egilewski](https://github.com/egilewski), [@​elashera](https://github.com/elashera),
[@​Elektrofussel](https://github.com/Elektrofussel), [@​eliteworkstation94-ai](https://github.com/eliteworkstation94-ai), [@​embwl0x](https://github.com/embwl0x), [@​emo-eth](https://github.com/emo-eth), [@​emozilla](https://github.com/emozilla), [@​enzo-adami](https://github.com/enzo-adami), [@​Epoxidex](https://github.com/Epoxidex), [@​ErnestHysa](https://github.com/ErnestHysa),
[@​Erosika](https://github.com/Erosika), [@​esthonjr](https://github.com/esthonjr), [@​ethernet8023](https://github.com/ethernet8023), [@​evefromwayback](https://github.com/evefromwayback), [@​evelynburger](https://github.com/evelynburger), [@​F4TB0Yz](https://github.com/F4TB0Yz), [@​falkoro](https://github.com/falkoro), [@​fanyangCS](https://github.com/fanyangCS), [@​firefly](https://github.com/firefly),
[@​fjlaowan1983](https://github.com/fjlaowan1983), [@​flewe](https://github.com/flewe), [@​flo1t](https://github.com/flo1t), [@​flow-digital-ny](https://github.com/flow-digital-ny), [@​floze-the-genius](https://github.com/floze-the-genius), [@​frizikk](https://github.com/frizikk), [@​Frowtek](https://github.com/Frowtek), [@​FuryMartin](https://github.com/FuryMartin),
[@​fyzanshaik](https://github.com/fyzanshaik), [@​gauravsaxena1997](https://github.com/gauravsaxena1997), [@​geoffreybutler94](https://github.com/geoffreybutler94), [@​georgedrury](https://github.com/georgedrury), [@​gigakun3030](https://github.com/gigakun3030), [@​giggling-ginger](https://github.com/giggling-ginger),
[@​Git-on-my-level](https://github.com/Git-on-my-level), [@​gitcommit90](https://github.com/gitcommit90), [@​githubespresso407](https://github.com/githubespresso407), [@​gnodet](https://github.com/gnodet), [@​GottZ](https://github.com/G…
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
…Research#64597) Every auxiliary task block (vision, web_extract, compression, title_generation, curator, background_review, moa_reference, ...) now accepts a reasoning_effort shorthand: auxiliary: compression: reasoning_effort: low vision: reasoning_effort: none _get_task_extra_body() folds it into extra_body.reasoning, which every auxiliary wire already translates: chat.completions passes it through, the Codex Responses adapter maps it to top-level reasoning/include, and the Anthropic auxiliary adapter now forwards it into build_anthropic_kwargs(reasoning_config=...) (previously hardcoded None). An explicit extra_body.reasoning on the same task wins over the shorthand. Invalid levels are ignored with a warning. Empty string (the shipped default) is a no-op — zero behavior change. Config: reasoning_effort added to all 16 auxiliary task blocks in DEFAULT_CONFIG (no version bump — deep-merge handles new keys).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Every auxiliary task can now set its own reasoning effort — a
reasoning_effortshorthand on eachauxiliary.<task>config block, so side tasks (compression, vision, title generation, curator, background review, ...) stop inheriting expensive thinking from the main model.This complements
automain-model-first aux routing: keep side tasks on your main (reasoning) model for quality, but dial their thinking down for latency/cost — without touching the main chat'sagent.reasoning_effort.How
One resolution point:
_get_task_extra_body()foldsreasoning_effortintoextra_body.reasoning, which every auxiliary wire already translates:extra_body.reasoningthrough (existing)reasoning+include(existing)build_anthropic_kwargs(reasoning_config=...)— previously hardcodedNone(fixed here)Rules: explicit
extra_body.reasoningon the same task wins over the shorthand; invalid levels are ignored with a warning; empty string (the shipped default in all 16 task blocks) is a no-op. No_config_versionbump — deep-merge handles new keys.Changes
agent/auxiliary_client.py: shorthand folding in_get_task_extra_body(); Anthropic auxiliary adapter forwardsextra_body.reasoninghermes_cli/config.py:reasoning_effort: ""on all 16 auxiliary task blockscli-config.yaml.example,website/docs/user-guide/configuration.md: documentednonedisables, explicit-wins, invalid-warns, empty-noop, Anthropic forwarding)Validation
tests/agent/test_auxiliary_client.pyreasoning, Codex translation, Anthropic kwargshermes chat -qsessions, logging proxy → real OpenRouter)title_generation.reasoning_effort: none→reasoning: {enabled: false}on the title-gen request; flipped toxhigh→{effort: "xhigh"}; main chat request unaffectedInfographic