Skip to content

Terminal-billing client hardening: shared wire types, wire-layer tests, dead RPC removal - #61067

Merged
alt-glitch merged 7 commits into
mainfrom
sid/tui-billing-hardening
Jul 18, 2026
Merged

Terminal-billing client hardening: shared wire types, wire-layer tests, dead RPC removal#61067
alt-glitch merged 7 commits into
mainfrom
sid/tui-billing-hardening

Conversation

@alt-glitch

@alt-glitch alt-glitch commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Hardening pass over the terminal-billing client layer that #51639 built. The theme: the billing wire contract used to live in three places (a TUI-local types file, a hand-copied desktop mirror, and everyone's heads). After this PR it lives once, in @hermes/shared, together with the behavior tables that used to be duplicated switch statements.

Originally stacked on #51639; now that it merged, this targets main directly.

Where things moved

flowchart TD
    GW["tui_gateway/server.py<br/>(serializes billing/subscription RPCs)"] -->|"JSON over the gateway socket"| BT

    subgraph shared ["apps/shared (new home)"]
        BT["billing-types.ts<br/>wire shapes + Known* unions<br/>(moved from ui-tui/src/gatewayTypes.ts)"]
        BP["billing-policy.ts<br/>refusal code &rarr; recovery table"]
        CS["charge-settlement.ts<br/>settlement poll state machine"]
    end

    BT --> TUI["ui-tui<br/>(gatewayTypes.ts re-exports, zero call-site churn)"]
    BT -.-> DESK["apps/desktop billing settings<br/>(adopts in #61054)"]
    BP --> TUI
    BP -.-> DESK
    CS --> TUI
    CS -.-> DESK
Loading

Solid arrows are wired up in this PR. Dotted ones land in #61054, which swaps the desktop's hand-copied type mirror for these modules.

The changes

Wire types move to @hermes/shared/billing. The billing and subscription response shapes (plus the usage-bar types they reference) move verbatim out of ui-tui/src/gatewayTypes.ts into apps/shared/src/billing-types.ts. gatewayTypes.ts re-exports every moved name, so no ui-tui call site changes. The new ./billing subpath exists so the DOM-less TUI does not pull in the root barrel's DOM-typed WebSocket helpers.

Error codes become real types. Two literal unions describe what the wire can carry: BillingRefusalCode for typed refusals and ChargeFailureReason for terminal charge failures. Each has a closed Known* half and an open (string & {}) arm. The closed half is the useful part: an exhaustive Record<KnownBillingRefusalCode, ...> now fails to compile when someone adds a code without deciding what it means, while the open arm lets unknown future codes (new card-health states, for example) flow through to the fallback branch instead of breaking anything. The reason union includes subscription_payment_intent_requires_action, the raw Stripe code that older server builds report for an SCA-gated upgrade, so the client renders "verify your card" either way.

One policy table instead of three switches. billing-policy.ts holds a single exhaustive map from refusal code to behavior: which recovery to offer (retry, portal, step-up, reconnect, login, none), which codes make a mid-poll outcome ambiguous, and which allow reusing an idempotency key on retry. The copy stays per-surface; the decisions no longer drift. This came out of review, which found the TUI and desktop already disagreeing on two of these decisions after a week.

One settlement machine. charge-settlement.ts is the charge-status polling loop (2s cadence, 5-minute cap, bounded retry-after backoff, revocation-means-ambiguous) as a pure function over injected deps. The TUI's /topup poller is now a thin renderer over it. Its output is byte-identical; the existing poller tests pass without modification, which was the acceptance bar.

Wire-layer tests. hermes_cli/nous_billing.py's HTTP error handling had no coverage through _request. About 30 tests now pin the 401-refresh-retry machinery, the 403 family, 429/503 retry-after handling, non-JSON bodies, and URLError normalization. Writing them surfaced two real bugs, fixed here: a bare read-phase TimeoutError escaped the typed error contract (now network_error), and BillingMutationResponse.payload claimed success payloads were error-shaped.

Verification

apps/shared, ui-tui, and apps/desktop typecheck clean. ui-tui vitest: 977 passed. Wire-layer suite: 30 passed. Billing-adjacent Python suites: 119 passed. The TUI poller rewrite passed the pre-existing topupCommand tests (28) unchanged.

@alt-glitch alt-glitch added type/refactor Code restructuring, no behavior change comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) area/billing Account usage, credit usage, billing (cross-cutting) P3 Low — cosmetic, nice to have labels Jul 8, 2026
@alt-glitch
alt-glitch force-pushed the sid/tui-billing-hardening branch from 7c6dd05 to 7ae52f3 Compare July 17, 2026 16:51
@alt-glitch
alt-glitch force-pushed the sid/tui-billing-hardening branch from 7ae52f3 to d2d1f23 Compare July 17, 2026 19:58
Base automatically changed from sid/tui-billing to main July 18, 2026 09:00
@alt-glitch
alt-glitch marked this pull request as ready for review July 18, 2026 09:00
@alt-glitch
alt-glitch requested a review from a team July 18, 2026 09:00
@alt-glitch
alt-glitch force-pushed the sid/tui-billing-hardening branch 2 times, most recently from a510dc3 to 7a66e57 Compare July 18, 2026 09:19
@alt-glitch
alt-glitch enabled auto-merge (squash) July 18, 2026 09:35
@alt-glitch
alt-glitch force-pushed the sid/tui-billing-hardening branch from 7a66e57 to 7004be9 Compare July 18, 2026 13:47
The billing/subscription wire shapes (plus UsageBarData/UsageModelData,
which they reference) move verbatim from ui-tui/src/gatewayTypes.ts into
apps/shared/src/billing-types.ts so the desktop app can share the same
gateway contract. gatewayTypes.ts re-exports every moved name from the
new @hermes/shared/billing subpath, so no ui-tui consumer changes.

The subpath export keeps DOM-less ui-tui from pulling the barrel (whose
WebSocket helpers need the DOM lib). ui-tui also now declares its
@hermes/shared dependency explicitly instead of relying on workspace
hoisting.
The HTTP layer's error handling had zero coverage through _request:
only 2xx parsing and request shaping were tested, and the mapping cases
in test_remote_spending_gate_contract.py hit _raise_for_error directly.

Adds 19 tests driving _request via a monkeypatched urlopen: the
401-refresh-retry path (success, terminal plain/session_revoked,
idempotency-key preservation, base re-resolution), 403 variants through
the wire, 429/503 retry-after, non-JSON error bodies, 404/502
fallbacks, and URLError normalization.

Two behaviors are pinned as findings rather than fixed: a JSON-body
retryAfter hint is ignored unless the Retry-After header is present,
and a bare socket.timeout propagates uncaught (real urllib wraps
timeouts in URLError before this layer).
urlopen wraps connect-phase timeouts in URLError (already mapped to
network_error), but a timeout during resp.read() raises a bare
TimeoutError that escaped the typed-BillingError contract and reached
callers as an unhandled exception. Catch it narrowly and normalize.
The boundary test now asserts normalization instead of documenting the
leak.
BillingMutationResponse.payload was declared BillingErrorPayload, but on
ok:true the gateway passes through the raw NAS success body (rail,
changeType, cancelAtPeriodEnd, ...). The TUI never reads it so nothing
broke, but the shared contract now feeds the desktop app too — widen the
field deliberately and document both shapes.
- BillingRefusalCode covers every code the gateway serializes today, with a
  (string & {}) arm so unknown future codes (the NAS W3 card-health family)
  stay assignable — consumers keep their unknown-code fallback.
- ChargeFailureReason models the four NAS terminal reasons plus the raw
  subscription_payment_intent_requires_action code NAS leaks pre-#711.
- billing.state now carries the server-derived can_change_plan the gateway
  emits; capability comments updated (canChangePlan is capability-based, not
  an OWNER/ADMIN role gate).
… unions

- KnownBillingRefusalCode / KnownChargeFailureReason are closed literal sets,
  so classification tables, copy maps and tests can be Record-exhaustive and
  break at compile time when a code is added but not mapped. The wire types
  keep the (string & {}) open arm for unknown future codes.
- Add network_error (client-originated transport code the gateway already
  serializes) to the known set.
- Export the union types from the root barrel alongside the other billing
  names.
…driver

- billing-policy.ts: one exhaustive Record<KnownBillingRefusalCode,
  BillingRefusalPolicy> classifying every known code (recovery kind,
  mid-poll ambiguity, idempotency-key reuse) with a documented unknown-code
  fallback. Surfaces keep their own copy; the behavior classification now
  has a single home that breaks the build when a new code goes unmapped.
- charge-settlement.ts: the settlement poll state machine (2s cadence,
  5-minute cap, bounded retry-after backoff, ambiguous-on-revocation) as a
  pure dependency-injected driver returning a discriminated outcome.
- The TUI's pollCharge becomes a thin renderer over the shared driver —
  byte-identical output, and the desktop poller can now share the same
  machine instead of a drifting copy.
@alt-glitch
alt-glitch force-pushed the sid/tui-billing-hardening branch from 7004be9 to dccb13c Compare July 18, 2026 13:48
@alt-glitch
alt-glitch merged commit 7668d28 into main Jul 18, 2026
40 checks passed
@alt-glitch
alt-glitch deleted the sid/tui-billing-hardening branch July 18, 2026 14:06
@teknium1 teknium1 added the area/usage-cost Token accounting, usage reporting, billing, cost tracking label Jul 19, 2026
eiritsu pushed a commit to eiritsu/hermes-agent that referenced this pull request Jul 20, 2026
…s, dead RPC removal (NousResearch#61067)

* refactor(shared): move terminal-billing wire types to @hermes/shared

The billing/subscription wire shapes (plus UsageBarData/UsageModelData,
which they reference) move verbatim from ui-tui/src/gatewayTypes.ts into
apps/shared/src/billing-types.ts so the desktop app can share the same
gateway contract. gatewayTypes.ts re-exports every moved name from the
new @hermes/shared/billing subpath, so no ui-tui consumer changes.

The subpath export keeps DOM-less ui-tui from pulling the barrel (whose
WebSocket helpers need the DOM lib). ui-tui also now declares its
@hermes/shared dependency explicitly instead of relying on workspace
hoisting.

* test(cli): pin nous_billing wire-layer status-to-exception mapping

The HTTP layer's error handling had zero coverage through _request:
only 2xx parsing and request shaping were tested, and the mapping cases
in test_remote_spending_gate_contract.py hit _raise_for_error directly.

Adds 19 tests driving _request via a monkeypatched urlopen: the
401-refresh-retry path (success, terminal plain/session_revoked,
idempotency-key preservation, base re-resolution), 403 variants through
the wire, 429/503 retry-after, non-JSON error bodies, 404/502
fallbacks, and URLError normalization.

Two behaviors are pinned as findings rather than fixed: a JSON-body
retryAfter hint is ignored unless the Retry-After header is present,
and a bare socket.timeout propagates uncaught (real urllib wraps
timeouts in URLError before this layer).

* fix(cli): normalize read-phase timeouts to the typed billing error

urlopen wraps connect-phase timeouts in URLError (already mapped to
network_error), but a timeout during resp.read() raises a bare
TimeoutError that escaped the typed-BillingError contract and reached
callers as an unhandled exception. Catch it narrowly and normalize.
The boundary test now asserts normalization instead of documenting the
leak.

* fix(shared): stop typing mutation success payloads as error payloads

BillingMutationResponse.payload was declared BillingErrorPayload, but on
ok:true the gateway passes through the raw NAS success body (rail,
changeType, cancelAtPeriodEnd, ...). The TUI never reads it so nothing
broke, but the shared contract now feeds the desktop app too — widen the
field deliberately and document both shapes.

* feat(shared): typed billing refusal and charge-failure unions

- BillingRefusalCode covers every code the gateway serializes today, with a
  (string & {}) arm so unknown future codes (the NAS W3 card-health family)
  stay assignable — consumers keep their unknown-code fallback.
- ChargeFailureReason models the four NAS terminal reasons plus the raw
  subscription_payment_intent_requires_action code NAS leaks pre-NousResearch#711.
- billing.state now carries the server-derived can_change_plan the gateway
  emits; capability comments updated (canChangePlan is capability-based, not
  an OWNER/ADMIN role gate).

* feat(shared): closed Known* halves for the refusal and charge-failure unions

- KnownBillingRefusalCode / KnownChargeFailureReason are closed literal sets,
  so classification tables, copy maps and tests can be Record-exhaustive and
  break at compile time when a code is added but not mapped. The wire types
  keep the (string & {}) open arm for unknown future codes.
- Add network_error (client-originated transport code the gateway already
  serializes) to the known set.
- Export the union types from the root barrel alongside the other billing
  names.

* feat(shared): canonical billing refusal policy and charge-settlement driver

- billing-policy.ts: one exhaustive Record<KnownBillingRefusalCode,
  BillingRefusalPolicy> classifying every known code (recovery kind,
  mid-poll ambiguity, idempotency-key reuse) with a documented unknown-code
  fallback. Surfaces keep their own copy; the behavior classification now
  has a single home that breaks the build when a new code goes unmapped.
- charge-settlement.ts: the settlement poll state machine (2s cadence,
  5-minute cap, bounded retry-after backoff, ambiguous-on-revocation) as a
  pure dependency-injected driver returning a discriminated outcome.
- The TUI's pollCharge becomes a thin renderer over the shared driver —
  byte-identical output, and the desktop poller can now share the same
  machine instead of a drifting copy.
ethernet8023 added a commit that referenced this pull request Jul 20, 2026
#61067
added a @hermes/shared dep for `ui-tui`. added it to the nix deps to fix
builds
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. ([#&#8203;59332](https://github.com/NousResearch/hermes-agent/pull/59332), [#&#8203;59389](https://github.com/NousResearch/hermes-agent/pull/59389) — [@&#8203;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. ([#&#8203;67154](https://github.com/NousResearch/hermes-agent/pull/67154), [#&#8203;67818](https://github.com/NousResearch/hermes-agent/pull/67818), [#&#8203;65898](https://github.com/NousResearch/hermes-agent/pull/65898), [#&#8203;66033](https://github.com/NousResearch/hermes-agent/pull/66033), [#&#8203;66747](https://github.com/NousResearch/hermes-agent/pull/66747), [#&#8203;67742](https://github.com/NousResearch/hermes-agent/pull/67742) and more — [@&#8203;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. ([#&#8203;51639](https://github.com/NousResearch/hermes-agent/pull/51639), [#&#8203;61054](https://github.com/NousResearch/hermes-agent/pull/61054), [#&#8203;61067](https://github.com/NousResearch/hermes-agent/pull/61067) — [@&#8203;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. ([#&#8203;62661](https://github.com/NousResearch/hermes-agent/pull/62661), [#&#8203;59164](https://github.com/NousResearch/hermes-agent/pull/59164), [#&#8203;54518](https://github.com/NousResearch/hermes-agent/pull/54518) — [@&#8203;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. ([#&#8203;59498](https://github.com/NousResearch/hermes-agent/pull/59498) — [@&#8203;teknium1](https://github.com/teknium1), 1Password provider salvaged from [@&#8203;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. ([#&#8203;67479](https://github.com/NousResearch/hermes-agent/pull/67479), [#&#8203;63494](https://github.com/NousResearch/hermes-agent/pull/63494) — [@&#8203;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. ([#&#8203;67181](https://github.com/NousResearch/hermes-agent/pull/67181) — [@&#8203;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. ([#&#8203;64835](https://github.com/NousResearch/hermes-agent/pull/64835) salvaging [@&#8203;Burgunthy](https://github.com/Burgunthy), [#&#8203;65700](https://github.com/NousResearch/hermes-agent/pull/65700), [#&#8203;60589](https://github.com/NousResearch/hermes-agent/pull/60589) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;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 [#&#8203;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. ([#&#8203;62593](https://github.com/NousResearch/hermes-agent/pull/62593), [#&#8203;63969](https://github.com/NousResearch/hermes-agent/pull/63969), [#&#8203;61616](https://github.com/NousResearch/hermes-agent/pull/61616) — [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor) completing [@&#8203;rob-maron](https://github.com/rob-maron)'s [#&#8203;61578](https://github.com/NousResearch/hermes-agent/issues/61578), [#&#8203;60887](https://github.com/NousResearch/hermes-agent/pull/60887), [#&#8203;65913](https://github.com/NousResearch/hermes-agent/pull/65913), [#&#8203;64541](https://github.com/NousResearch/hermes-agent/pull/64541), [#&#8203;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. ([#&#8203;62650](https://github.com/NousResearch/hermes-agent/pull/62650), [#&#8203;64458](https://github.com/NousResearch/hermes-agent/pull/64458), [#&#8203;64631](https://github.com/NousResearch/hermes-agent/pull/64631), [#&#8203;64597](https://github.com/NousResearch/hermes-agent/pull/64597) — [@&#8203;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. ([#&#8203;60186](https://github.com/NousResearch/hermes-agent/pull/60186) salvaging [@&#8203;web3blind](https://github.com/web3blind), [#&#8203;60492](https://github.com/NousResearch/hermes-agent/pull/60492), [#&#8203;60507](https://github.com/NousResearch/hermes-agent/pull/60507), [#&#8203;59327](https://github.com/NousResearch/hermes-agent/pull/59327) — [@&#8203;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. ([#&#8203;57660](https://github.com/NousResearch/hermes-agent/pull/57660), [#&#8203;58709](https://github.com/NousResearch/hermes-agent/pull/58709), [#&#8203;59215](https://github.com/NousResearch/hermes-agent/pull/59215), [#&#8203;56582](https://github.com/NousResearch/hermes-agent/pull/56582), [#&#8203;57842](https://github.com/NousResearch/hermes-agent/pull/57842) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;srojk34](https://github.com/srojk34), [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor), [@&#8203;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 ([#&#8203;59332](https://github.com/NousResearch/hermes-agent/pull/59332) — [@&#8203;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 ([#&#8203;59389](https://github.com/NousResearch/hermes-agent/pull/59389) — [@&#8203;teknium1](https://github.com/teknium1))
- Segment mixed tool batches to recover lost concurrency; drop per-call base64 re-serialization from request-size estimates ([#&#8203;64460](https://github.com/NousResearch/hermes-agent/pull/64460), [#&#8203;67788](https://github.com/NousResearch/hermes-agent/pull/67788) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;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 ([#&#8203;67154](https://github.com/NousResearch/hermes-agent/pull/67154), [#&#8203;67818](https://github.com/NousResearch/hermes-agent/pull/67818), [#&#8203;65898](https://github.com/NousResearch/hermes-agent/pull/65898), [#&#8203;66033](https://github.com/NousResearch/hermes-agent/pull/66033) — [@&#8203;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 ([#&#8203;66747](https://github.com/NousResearch/hermes-agent/pull/66747), [#&#8203;66347](https://github.com/NousResearch/hermes-agent/pull/66347), [#&#8203;67857](https://github.com/NousResearch/hermes-agent/pull/67857), [#&#8203;66470](https://github.com/NousResearch/hermes-agent/pull/66470) — [@&#8203;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 ([#&#8203;67742](https://github.com/NousResearch/hermes-agent/pull/67742), [#&#8203;67842](https://github.com/NousResearch/hermes-agent/pull/67842), [#&#8203;67195](https://github.com/NousResearch/hermes-agent/pull/67195), [#&#8203;67245](https://github.com/NousResearch/hermes-agent/pull/67245), [#&#8203;67824](https://github.com/NousResearch/hermes-agent/pull/67824), [#&#8203;67838](https://github.com/NousResearch/hermes-agent/pull/67838), [#&#8203;67844](https://github.com/NousResearch/hermes-agent/pull/67844) — [@&#8203;OutThisLife](https://github.com/OutThisLife))
- Systematized perf benchmark harness with trustworthy cold-start + first-token measurement, replacing 12 one-off scripts ([#&#8203;67466](https://github.com/NousResearch/hermes-agent/pull/67466), [#&#8203;67697](https://github.com/NousResearch/hermes-agent/pull/67697) — [@&#8203;OutThisLife](https://github.com/OutThisLife))

##### Everywhere else

- TUI renders streamed markdown incrementally per block ([#&#8203;67236](https://github.com/NousResearch/hermes-agent/pull/67236) — [@&#8203;OutThisLife](https://github.com/OutThisLife))
- Skill discovery cached by scan signature; snapshot manifest builds \~5× faster; text prefilter before AST parse in tool discovery ([#&#8203;61414](https://github.com/NousResearch/hermes-agent/pull/61414), [#&#8203;61131](https://github.com/NousResearch/hermes-agent/pull/61131), [#&#8203;63941](https://github.com/NousResearch/hermes-agent/pull/63941) — [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor), [@&#8203;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 ([#&#8203;61133](https://github.com/NousResearch/hermes-agent/pull/61133), [#&#8203;61368](https://github.com/NousResearch/hermes-agent/pull/61368), [#&#8203;67247](https://github.com/NousResearch/hermes-agent/pull/67247) — [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor), [@&#8203;OutThisLife](https://github.com/OutThisLife))
- `hermes update` skips npm install when Node manifests are unchanged; dashboard session-list payloads trimmed + messages paginated ([#&#8203;61580](https://github.com/NousResearch/hermes-agent/pull/61580), [#&#8203;60883](https://github.com/NousResearch/hermes-agent/pull/60883) — [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor))
- Byte-stable gateway system prompts — pinned session-context render keeps the prompt cache alive across turns ([#&#8203;67403](https://github.com/NousResearch/hermes-agent/pull/67403) — [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor))

##### 🏗️ Core Agent & Architecture

##### Providers & models

- **Fireworks AI provider** with cost estimation + cached picker price columns, promoted to [#&#8203;2](https://github.com/NousResearch/hermes-agent/issues/2) in provider pickers ([#&#8203;62593](https://github.com/NousResearch/hermes-agent/pull/62593), [#&#8203;65476](https://github.com/NousResearch/hermes-agent/pull/65476), [#&#8203;65214](https://github.com/NousResearch/hermes-agent/pull/65214) — [@&#8203;teknium1](https://github.com/teknium1))
- **DeepInfra** hardened integration; **Upstage Solar** provider ([#&#8203;42231](https://github.com/NousResearch/hermes-agent/issues/42231) salvage) ([#&#8203;63969](https://github.com/NousResearch/hermes-agent/pull/63969), [#&#8203;64541](https://github.com/NousResearch/hermes-agent/pull/64541) — [@&#8203;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 ([#&#8203;61616](https://github.com/NousResearch/hermes-agent/pull/61616) — [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor), building on [@&#8203;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 ([#&#8203;60887](https://github.com/NousResearch/hermes-agent/pull/60887), [#&#8203;65913](https://github.com/NousResearch/hermes-agent/pull/65913), [#&#8203;65922](https://github.com/NousResearch/hermes-agent/pull/65922), [#&#8203;56617](https://github.com/NousResearch/hermes-agent/pull/56617), [#&#8203;60943](https://github.com/NousResearch/hermes-agent/pull/60943) — [@&#8203;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 ([#&#8203;64771](https://github.com/NousResearch/hermes-agent/pull/64771), [#&#8203;65472](https://github.com/NousResearch/hermes-agent/pull/65472), [#&#8203;67606](https://github.com/NousResearch/hermes-agent/pull/67606) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;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 ([#&#8203;58884](https://github.com/NousResearch/hermes-agent/pull/58884), [#&#8203;61873](https://github.com/NousResearch/hermes-agent/pull/61873) — [@&#8203;vishal-dharm](https://github.com/vishal-dharm), [#&#8203;57038](https://github.com/NousResearch/hermes-agent/pull/57038), [#&#8203;57028](https://github.com/NousResearch/hermes-agent/pull/57028) — [@&#8203;teknium1](https://github.com/teknium1))
- **Claude Sonnet 5 fully wired** — curated lists, intro pricing, and metadata across every route ([#&#8203;67932](https://github.com/NousResearch/hermes-agent/pull/67932) — [@&#8203;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 ([#&#8203;67971](https://github.com/NousResearch/hermes-agent/pull/67971) — [@&#8203;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 ([#&#8203;68007](https://github.com/NousResearch/hermes-agent/pull/68007), [#&#8203;67977](https://github.com/NousResearch/hermes-agent/pull/67977), [#&#8203;68005](https://github.com/NousResearch/hermes-agent/pull/68005), [#&#8203;67976](https://github.com/NousResearch/hermes-agent/pull/67976) — [@&#8203;teknium1](https://github.com/teknium1))
- kimi-k3 rollout completed across Kimi-direct catalog surfaces with 1M context on canonical Kimi Coding endpoints ([#&#8203;68108](https://github.com/NousResearch/hermes-agent/pull/68108) — [@&#8203;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 ([#&#8203;67758](https://github.com/NousResearch/hermes-agent/pull/67758), [#&#8203;67904](https://github.com/NousResearch/hermes-agent/pull/67904), [#&#8203;67908](https://github.com/NousResearch/hermes-agent/pull/67908) — [@&#8203;teknium1](https://github.com/teknium1))

##### Reasoning & MoA

- `max` + `ultra` effort levels across every surface and route ([#&#8203;62650](https://github.com/NousResearch/hermes-agent/pull/62650) — [@&#8203;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 ([#&#8203;64458](https://github.com/NousResearch/hermes-agent/pull/64458), [#&#8203;64597](https://github.com/NousResearch/hermes-agent/pull/64597), [#&#8203;64631](https://github.com/NousResearch/hermes-agent/pull/64631), [#&#8203;67946](https://github.com/NousResearch/hermes-agent/pull/67946) — [@&#8203;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 ([#&#8203;56756](https://github.com/NousResearch/hermes-agent/pull/56756), [#&#8203;57591](https://github.com/NousResearch/hermes-agent/pull/57591), [#&#8203;64756](https://github.com/NousResearch/hermes-agent/pull/64756) — [@&#8203;teknium1](https://github.com/teknium1))

##### Delegation, approvals & the agent loop

- Live subagent transcripts + durable background completions (see Highlights) ([#&#8203;67479](https://github.com/NousResearch/hermes-agent/pull/67479), [#&#8203;63494](https://github.com/NousResearch/hermes-agent/pull/63494) — [@&#8203;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) ([#&#8203;62661](https://github.com/NousResearch/hermes-agent/pull/62661), [#&#8203;59164](https://github.com/NousResearch/hermes-agent/pull/59164), [#&#8203;54518](https://github.com/NousResearch/hermes-agent/pull/54518), [#&#8203;60504](https://github.com/NousResearch/hermes-agent/pull/60504) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;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 ([#&#8203;56955](https://github.com/NousResearch/hermes-agent/pull/56955), [#&#8203;64775](https://github.com/NousResearch/hermes-agent/pull/64775), [#&#8203;61793](https://github.com/NousResearch/hermes-agent/pull/61793) — [@&#8203;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` ([#&#8203;66514](https://github.com/NousResearch/hermes-agent/pull/66514), [#&#8203;66115](https://github.com/NousResearch/hermes-agent/pull/66115), [#&#8203;60114](https://github.com/NousResearch/hermes-agent/pull/60114), [#&#8203;58155](https://github.com/NousResearch/hermes-agent/pull/58155), [#&#8203;62225](https://github.com/NousResearch/hermes-agent/pull/62225) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor), [@&#8203;JoaoMarcos44](https://github.com/JoaoMarcos44), [#&#8203;64280](https://github.com/NousResearch/hermes-agent/pull/64280) — [@&#8203;teknium1](https://github.com/teknium1))
- Hooks: oversized hook-injected context spills to disk ([#&#8203;20468](https://github.com/NousResearch/hermes-agent/pull/20468) — [@&#8203;teknium1](https://github.com/teknium1))
- Vibe reactions — floating hearts on affection across CLI/TUI/desktop, token-free core detection ([#&#8203;62016](https://github.com/NousResearch/hermes-agent/pull/62016) — [@&#8203;OutThisLife](https://github.com/OutThisLife))

##### Secrets & config

- Pluggable `SecretSource` interface + Bitwarden & 1Password providers (see Highlights) ([#&#8203;59498](https://github.com/NousResearch/hermes-agent/pull/59498) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;hwrdprkns](https://github.com/hwrdprkns))
- `hermes config get` / `unset`; warn on unknown root config keys + doctor deprecated-key reporting; `display.timestamp_format` ([#&#8203;65540](https://github.com/NousResearch/hermes-agent/pull/65540), [#&#8203;67370](https://github.com/NousResearch/hermes-agent/pull/67370), [#&#8203;40622](https://github.com/NousResearch/hermes-agent/pull/40622) — [@&#8203;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` ([#&#8203;65537](https://github.com/NousResearch/hermes-agent/pull/65537), [#&#8203;65468](https://github.com/NousResearch/hermes-agent/pull/65468), [#&#8203;59615](https://github.com/NousResearch/hermes-agent/pull/59615) — [@&#8203;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 ([#&#8203;60186](https://github.com/NousResearch/hermes-agent/pull/60186), [#&#8203;60492](https://github.com/NousResearch/hermes-agent/pull/60492), [#&#8203;60507](https://github.com/NousResearch/hermes-agent/pull/60507), [#&#8203;59327](https://github.com/NousResearch/hermes-agent/pull/59327), [#&#8203;63091](https://github.com/NousResearch/hermes-agent/pull/63091) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;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 ([#&#8203;67275](https://github.com/NousResearch/hermes-agent/pull/67275), [#&#8203;67916](https://github.com/NousResearch/hermes-agent/pull/67916), [#&#8203;65046](https://github.com/NousResearch/hermes-agent/pull/65046), [#&#8203;56868](https://github.com/NousResearch/hermes-agent/pull/56868) — [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor), [@&#8203;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 ([#&#8203;58899](https://github.com/NousResearch/hermes-agent/pull/58899), [#&#8203;59203](https://github.com/NousResearch/hermes-agent/pull/59203), [#&#8203;67274](https://github.com/NousResearch/hermes-agent/pull/67274) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor))

##### 🌐 Gateway, Fleet & Relay

- **Durable delivery-obligation ledger** for final responses (see Highlights) ([#&#8203;67181](https://github.com/NousResearch/hermes-agent/pull/67181) — [@&#8203;teknium1](https://github.com/teknium1))
- **Profile-based routing for inbound messages** + multiplex hardening wave 2 + `GATEWAY_MULTIPLEX_PROFILES` override (see Highlights) ([#&#8203;64835](https://github.com/NousResearch/hermes-agent/pull/64835), [#&#8203;65700](https://github.com/NousResearch/hermes-agent/pull/65700), [#&#8203;60589](https://github.com/NousResearch/hermes-agent/pull/60589) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;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 ([#&#8203;67401](https://github.com/NousResearch/hermes-agent/pull/67401), [#&#8203;65783](https://github.com/NousResearch/hermes-agent/pull/65783), [#&#8203;62645](https://github.com/NousResearch/hermes-agent/pull/62645), [#&#8203;56967](https://github.com/NousResearch/hermes-agent/pull/56967), [#&#8203;57030](https://github.com/NousResearch/hermes-agent/pull/57030) — [@&#8203;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 ([#&#8203;60194](https://github.com/NousResearch/hermes-agent/pull/60194), [#&#8203;57685](https://github.com/NousResearch/hermes-agent/pull/57685), [#&#8203;60944](https://github.com/NousResearch/hermes-agent/pull/60944), [#&#8203;65702](https://github.com/NousResearch/hermes-agent/pull/65702), [#&#8203;58872](https://github.com/NousResearch/hermes-agent/pull/58872) — [@&#8203;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 ([#&#8203;60730](https://github.com/NousResearch/hermes-agent/pull/60730), [#&#8203;60586](https://github.com/NousResearch/hermes-agent/pull/60586), [#&#8203;64649](https://github.com/NousResearch/hermes-agent/pull/64649), [#&#8203;59976](https://github.com/NousResearch/hermes-agent/pull/59976), [#&#8203;59969](https://github.com/NousResearch/hermes-agent/pull/59969), [#&#8203;59983](https://github.com/NousResearch/hermes-agent/pull/59983) — [@&#8203;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 ([#&#8203;65799](https://github.com/NousResearch/hermes-agent/pull/65799) — [@&#8203;teknium1](https://github.com/teknium1))
- WhatsApp: native Baileys polls (clarify renders as a poll), locations, rich inbound metadata; dashboard pairing flow ([#&#8203;58865](https://github.com/NousResearch/hermes-agent/pull/58865), [#&#8203;60571](https://github.com/NousResearch/hermes-agent/pull/60571) — [@&#8203;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 ([#&#8203;66149](https://github.com/NousResearch/hermes-agent/pull/66149), [#&#8203;60187](https://github.com/NousResearch/hermes-agent/pull/60187), [#&#8203;60230](https://github.com/NousResearch/hermes-agent/pull/60230), [#&#8203;60493](https://github.com/NousResearch/hermes-agent/pull/60493), [#&#8203;51751](https://github.com/NousResearch/hermes-agent/pull/51751) — [@&#8203;teknium1](https://github.com/teknium1))
- Slack: live per-tool status line ([#&#8203;67080](https://github.com/NousResearch/hermes-agent/pull/67080) — [@&#8203;teknium1](https://github.com/teknium1), salvaging [#&#8203;62007](https://github.com/NousResearch/hermes-agent/issues/62007))
- Telegram: per-topic free-response allowlist; Google Chat clarify prompts rendered as cards ([#&#8203;65543](https://github.com/NousResearch/hermes-agent/pull/65543), [#&#8203;65546](https://github.com/NousResearch/hermes-agent/pull/65546) — [@&#8203;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 ([#&#8203;58859](https://github.com/NousResearch/hermes-agent/pull/58859), [#&#8203;61415](https://github.com/NousResearch/hermes-agent/pull/61415), [#&#8203;57014](https://github.com/NousResearch/hermes-agent/pull/57014) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;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 ([#&#8203;60638](https://github.com/NousResearch/hermes-agent/pull/60638), [#&#8203;67303](https://github.com/NousResearch/hermes-agent/pull/67303) — [@&#8203;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 ([#&#8203;57590](https://github.com/NousResearch/hermes-agent/pull/57590), [#&#8203;57441](https://github.com/NousResearch/hermes-agent/pull/57441), [#&#8203;67482](https://github.com/NousResearch/hermes-agent/pull/67482) — [@&#8203;OutThisLife](https://github.com/OutThisLife), [@&#8203;teknium1](https://github.com/teknium1))
- **Hermes Cloud connection mode** (salvage of [#&#8203;55402](https://github.com/NousResearch/hermes-agent/issues/55402)); soft gateway switch + gateway-settings polish; terminal execution backend picker with health probes ([#&#8203;61912](https://github.com/NousResearch/hermes-agent/pull/61912), [#&#8203;61916](https://github.com/NousResearch/hermes-agent/pull/61916), [#&#8203;67203](https://github.com/NousResearch/hermes-agent/pull/67203) — [@&#8203;OutThisLife](https://github.com/OutThisLife), [@&#8203;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 ([#&#8203;65204](https://github.com/NousResearch/hermes-agent/pull/65204), [#&#8203;62243](https://github.com/NousResearch/hermes-agent/pull/62243), [#&#8203;65109](https://github.com/NousResearch/hermes-agent/pull/65109), [#&#8203;65174](https://github.com/NousResearch/hermes-agent/pull/65174), [#&#8203;61147](https://github.com/NousResearch/hermes-agent/pull/61147), [#&#8203;57913](https://github.com/NousResearch/hermes-agent/pull/57913) — [@&#8203;ethernet8023](https://github.com/ethernet8023), [@&#8203;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 ([#&#8203;67469](https://github.com/NousResearch/hermes-agent/pull/67469), [#&#8203;67681](https://github.com/NousResearch/hermes-agent/pull/67681), [#&#8203;67282](https://github.com/NousResearch/hermes-agent/pull/67282), [#&#8203;63086](https://github.com/NousResearch/hermes-agent/pull/63086) — [@&#8203;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 ([#&#8203;67206](https://github.com/NousResearch/hermes-agent/pull/67206) salvaging [@&#8203;erosika](https://github.com/erosika), [#&#8203;67209](https://github.com/NousResearch/hermes-agent/pull/67209), [#&#8203;67759](https://github.com/NousResearch/hermes-agent/pull/67759) — [@&#8203;austinpickett](https://github.com/austinpickett), [#&#8203;67472](https://github.com/NousResearch/hermes-agent/pull/67472), [#&#8203;63520](https://github.com/NousResearch/hermes-agent/pull/63520), [#&#8203;60457](https://github.com/NousResearch/hermes-agent/pull/60457), [#&#8203;67029](https://github.com/NousResearch/hermes-agent/pull/67029), [#&#8203;64598](https://github.com/NousResearch/hermes-agent/pull/64598), [#&#8203;57267](https://github.com/NousResearch/hermes-agent/pull/57267) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;OutThisLife](https://github.com/OutThisLife))
- Full TypeScript conversion of the desktop tree ([#&#8203;57855](https://github.com/NousResearch/hermes-agent/pull/57855) — [@&#8203;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 ([#&#8203;60569](https://github.com/NousResearch/hermes-agent/pull/60569), [#&#8203;63699](https://github.com/NousResearch/hermes-agent/pull/63699), [#&#8203;60571](https://github.com/NousResearch/hermes-agent/pull/60571), [#&#8203;65361](https://github.com/NousResearch/hermes-agent/pull/65361), [#&#8203;64636](https://github.com/NousResearch/hermes-agent/pull/64636) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor), [@&#8203;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 ([#&#8203;60515](https://github.com/NousResearch/hermes-agent/pull/60515), [#&#8203;65895](https://github.com/NousResearch/hermes-agent/pull/65895), [#&#8203;61929](https://github.com/NousResearch/hermes-agent/pull/61929), [#&#8203;67046](https://github.com/NousResearch/hermes-agent/pull/67046), [#&#8203;60537](https://github.com/NousResearch/hermes-agent/pull/60537), [#&#8203;61330](https://github.com/NousResearch/hermes-agent/pull/61330) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;OutThisLife](https://github.com/OutThisLife), [@&#8203;benbarclay](https://github.com/benbarclay))
- `hermes serve` is a true headless backend (no web UI build/mount) ([#&#8203;55923](https://github.com/NousResearch/hermes-agent/pull/55923) — [@&#8203;OutThisLife](https://github.com/OutThisLife))

##### 🧰 CLI & TUI

- `/subscription` + `/topup` terminal billing (see Highlights) ([#&#8203;51639](https://github.com/NousResearch/hermes-agent/pull/51639) — [@&#8203;alt-glitch](https://github.com/alt-glitch))
- **`/model --once`** — one-turn model override that reverts automatically ([#&#8203;67113](https://github.com/NousResearch/hermes-agent/pull/67113) — [@&#8203;teknium1](https://github.com/teknium1), salvaging [#&#8203;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 ([#&#8203;57987](https://github.com/NousResearch/hermes-agent/pull/57987), [#&#8203;58763](https://github.com/NousResearch/hermes-agent/pull/58763) — [@&#8203;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 ([#&#8203;45300](https://github.com/NousResearch/hermes-agent/pull/45300), [#&#8203;60111](https://github.com/NousResearch/hermes-agent/pull/60111), [#&#8203;57992](https://github.com/NousResearch/hermes-agent/pull/57992), [#&#8203;57029](https://github.com/NousResearch/hermes-agent/pull/57029), [#&#8203;57225](https://github.com/NousResearch/hermes-agent/pull/57225) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;ethernet8023](https://github.com/ethernet8023))
- TUI: model picker refresh support; custom skill bundles dispatched as agent turns; banner sizes skills display to terminal width ([#&#8203;59782](https://github.com/NousResearch/hermes-agent/pull/59782) — [@&#8203;helix4u](https://github.com/helix4u), [#&#8203;62859](https://github.com/NousResearch/hermes-agent/pull/62859) — [@&#8203;Adolanium](https://github.com/Adolanium), [#&#8203;40624](https://github.com/NousResearch/hermes-agent/pull/40624) — [@&#8203;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` ([#&#8203;57781](https://github.com/NousResearch/hermes-agent/pull/57781) — [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor), [#&#8203;36727](https://github.com/NousResearch/hermes-agent/pull/36727), [#&#8203;40623](https://github.com/NousResearch/hermes-agent/pull/40623) — [@&#8203;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 ([#&#8203;52750](https://github.com/NousResearch/hermes-agent/pull/52750), [#&#8203;57416](https://github.com/NousResearch/hermes-agent/pull/57416), [#&#8203;66151](https://github.com/NousResearch/hermes-agent/pull/66151), [#&#8203;65610](https://github.com/NousResearch/hermes-agent/pull/65610), [#&#8203;65622](https://github.com/NousResearch/hermes-agent/pull/65622), [#&#8203;64463](https://github.com/NousResearch/hermes-agent/pull/64463) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;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 ([#&#8203;57438](https://github.com/NousResearch/hermes-agent/pull/57438), [#&#8203;57902](https://github.com/NousResearch/hermes-agent/pull/57902), [#&#8203;65989](https://github.com/NousResearch/hermes-agent/pull/65989), [#&#8203;64715](https://github.com/NousResearch/hermes-agent/pull/64715) — [@&#8203;SHL0MS](https://github.com/SHL0MS), [#&#8203;65066](https://github.com/NousResearch/hermes-agent/pull/65066), [#&#8203;65486](https://github.com/NousResearch/hermes-agent/pull/65486) — [@&#8203;teknium1](https://github.com/teknium1))
- Browser: full snapshots stored on truncation, eval denylist opt-in; computer\_use follows cua-driver's verify→escalate ladder ([#&#8203;65923](https://github.com/NousResearch/hermes-agent/pull/65923), [#&#8203;67123](https://github.com/NousResearch/hermes-agent/pull/67123) — [@&#8203;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 ([#&#8203;66333](https://github.com/NousResearch/hermes-agent/pull/66333), [#&#8203;63638](https://github.com/NousResearch/hermes-agent/pull/63638), [#&#8203;60226](https://github.com/NousResearch/hermes-agent/pull/60226), [#&#8203;65698](https://github.com/NousResearch/hermes-agent/pull/65698), [#&#8203;63249](https://github.com/NousResearch/hermes-agent/pull/63249) — [@&#8203;teknium1](https://github.com/teknium1))
- Cron: durable execution audit history; one-shot stale-removal race fixed; run-claim TTL derived from HERMES\_CRON\_TIMEOUT ([#&#8203;61791](https://github.com/NousResearch/hermes-agent/pull/61791) — [@&#8203;teknium1](https://github.com/teknium1), [#&#8203;62014](https://github.com/NousResearch/hermes-agent/pull/62014) — [@&#8203;PRATHAMESH75](https://github.com/PRATHAMESH75), [#&#8203;59567](https://github.com/NousResearch/hermes-agent/pull/59567))
- mem0: self-hosted dashboard backend + recall tuning + setup-wizard mode ([#&#8203;56943](https://github.com/NousResearch/hermes-agent/pull/56943), [#&#8203;60494](https://github.com/NousResearch/hermes-agent/pull/60494) — [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor), [@&#8203;teknium1](https://github.com/teknium1))
- Image gen: Codex image inputs; unsupported Codex image accounts classified; tool args recursively normalized by schema (cline port) ([#&#8203;57017](https://github.com/NousResearch/hermes-agent/pull/57017), [#&#8203;63627](https://github.com/NousResearch/hermes-agent/pull/63627), [#&#8203;52220](https://github.com/NousResearch/hermes-agent/pull/52220) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;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 ([#&#8203;56680](https://github.com/NousResearch/hermes-agent/pull/56680), [#&#8203;56582](https://github.com/NousResearch/hermes-agent/pull/56582) — [@&#8203;srojk34](https://github.com/srojk34))
- Six P1 hardening PRs salvaged in one pass — browser guards, MEDIA anchoring, .env lockdown, delegate ACP transport ([#&#8203;57660](https://github.com/NousResearch/hermes-agent/pull/57660) — [@&#8203;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 ([#&#8203;58709](https://github.com/NousResearch/hermes-agent/pull/58709), [#&#8203;58752](https://github.com/NousResearch/hermes-agent/pull/58752), [#&#8203;57890](https://github.com/NousResearch/hermes-agent/pull/57890) — [@&#8203;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 ([#&#8203;59180](https://github.com/NousResearch/hermes-agent/pull/59180), [#&#8203;59215](https://github.com/NousResearch/hermes-agent/pull/59215), [#&#8203;58902](https://github.com/NousResearch/hermes-agent/pull/58902), [#&#8203;58508](https://github.com/NousResearch/hermes-agent/pull/58508) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;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 ([#&#8203;58501](https://github.com/NousResearch/hermes-agent/pull/58501), [#&#8203;58534](https://github.com/NousResearch/hermes-agent/pull/58534), [#&#8203;58915](https://github.com/NousResearch/hermes-agent/pull/58915), [#&#8203;58893](https://github.com/NousResearch/hermes-agent/pull/58893) — [@&#8203;teknium1](https://github.com/teknium1))
- computer-use: subprocess env sanitized across all five cua-driver spawn sites ([#&#8203;58889](https://github.com/NousResearch/hermes-agent/pull/58889), [#&#8203;59165](https://github.com/NousResearch/hermes-agent/pull/59165) — [@&#8203;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 ([#&#8203;58222](https://github.com/NousResearch/hermes-agent/pull/58222) — [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor), [#&#8203;60236](https://github.com/NousResearch/hermes-agent/pull/60236) — [@&#8203;teknium1](https://github.com/teknium1), [#&#8203;49435](https://github.com/NousResearch/hermes-agent/pull/49435) — [@&#8203;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 ([#&#8203;57842](https://github.com/NousResearch/hermes-agent/pull/57842) — [@&#8203;jquesnelle](https://github.com/jquesnelle), [#&#8203;60707](https://github.com/NousResearch/hermes-agent/pull/60707), [#&#8203;65186](https://github.com/NousResearch/hermes-agent/pull/65186) — [@&#8203;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 ([#&#8203;59149](https://github.com/NousResearch/hermes-agent/pull/59149) — [@&#8203;teknium1](https://github.com/teknium1), [#&#8203;64651](https://github.com/NousResearch/hermes-agent/pull/64651), [#&#8203;66040](https://github.com/NousResearch/hermes-agent/pull/66040) — [@&#8203;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/) ([#&#8203;67238](https://github.com/NousResearch/hermes-agent/pull/67238) — [@&#8203;OutThisLife](https://github.com/OutThisLife), [#&#8203;59339](https://github.com/NousResearch/hermes-agent/pull/59339), [#&#8203;58178](https://github.com/NousResearch/hermes-agent/pull/58178) — [@&#8203;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 ([#&#8203;58350](https://github.com/NousResearch/hermes-agent/pull/58350), [#&#8203;64379](https://github.com/NousResearch/hermes-agent/pull/64379), [#&#8203;61139](https://github.com/NousResearch/hermes-agent/pull/61139) — [@&#8203;teknium1](https://github.com/teknium1), [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor))

##### 🔁 Reverted in this window (for the record)

- iron-proxy credential-injection egress firewall ([#&#8203;30179](https://github.com/NousResearch/hermes-agent/issues/30179) → reverted in [#&#8203;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 [#&#8203;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

- [@&#8203;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
- [@&#8203;OutThisLife](https://github.com/OutThisLife) — desktop app (the speed wave, layout-tree shell, Capabilities page, session colors, vibe reactions, TUI incremental markdown, perf harness)
- [@&#8203;kshitijk4poor](https://github.com/kshitijk4poor) — GPT-5.6 end-to-end, DeepInfra + Upstage Solar providers, perf cluster, compression integrity, mem0, dashboard guards
- [@&#8203;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
- [@&#8203;benbarclay](https://github.com/benbarclay) — relay OIDC provisioning, gateway multiplex override, Nous auth self-heal, hosted MCP OAuth groundwork
- [@&#8203;alt-glitch](https://github.com/alt-glitch) — terminal billing (`/subscription`, `/topup`), desktop billing tab
- [@&#8203;helix4u](https://github.com/helix4u) — desktop provider/model UX, TUI model picker refresh, Windows install/updater hardening
- [@&#8203;austinpickett](https://github.com/austinpickett) — desktop custom endpoint settings
- [@&#8203;SHL0MS](https://github.com/SHL0MS) — unbroker + unreal-mcp skills, humanizer expansion

##### Top community contributors

- [@&#8203;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
- [@&#8203;HexLab98](https://github.com/HexLab98) — 11 fixes across MCP capability gating, Windows installer PATH, desktop cron editing, gateway systemd warnings
- [@&#8203;UnathiCodex](https://github.com/UnathiCodex) — desktop stability: zoom across display moves, LaTeX rendering, resume-stall and runtime-readiness fixes
- [@&#8203;xxxigm](https://github.com/xxxigm) — `<think>` leak fix after thinking-only retry flush, dashboard auth/theme/PTY fixes
- [@&#8203;erosika](https://github.com/erosika) — desktop declarative memory-provider panel + honcho recall/timeout correctness
- [@&#8203;Frowtek](https://github.com/Frowtek) — credential security: master stores never mounted into skill sandboxes, live-transcript redaction, dashboard api\_key precedence
- [@&#8203;necoweb3](https://github.com/necoweb3) — browser private-page CDP guard, cron one-shot liveness, gateway compression fail-closed
- [@&#8203;DavidMetcalfe](https://github.com/DavidMetcalfe) — desktop updater version pill, Local/custom endpoint exposure, sidebar collapse behavior
- [@&#8203;shannonsands](https://github.com/shannonsands) — dashboard: mobile channel setup, Discord toolsets from web UI, Telegram setup clarity
- [@&#8203;vishal-dharm](https://github.com/vishal-dharm) — Gemini request-context improvements
- [@&#8203;PRATHAMESH75](https://github.com/PRATHAMESH75) — cron one-shot stale-removal race, dashboard multiplex port-binding guard
- [@&#8203;alelpoan](https://github.com/alelpoan), [@&#8203;embwl0x](https://github.com/embwl0x), [@&#8203;Adolanium](https://github.com/Adolanium), [@&#8203;giggling-ginger](https://github.com/giggling-ginger), [@&#8203;Drexuxux](https://github.com/Drexuxux), [@&#8203;frizikk](https://github.com/frizikk), [@&#8203;JoaoMarcos44](https://github.com/JoaoMarcos44), [@&#8203;wesleysimplici](https://github.com/wesleysimplici), [@&#8203;LeonSGP43](https://github.com/LeonSGP43), [@&#8203;pierrenode](https://github.com/pierrenode), [@&#8203;simpolism](https://github.com/simpolism), [@&#8203;MorAlekss](https://github.com/MorAlekss), [@&#8203;r266-tech](https://github.com/r266-tech), [@&#8203;WadydX](https://github.com/WadydX), [@&#8203;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: [@&#8203;Burgunthy](https://github.com/Burgunthy) (profile routing), [@&#8203;web3blind](https://github.com/web3blind) (sessions export), [@&#8203;hwrdprkns](https://github.com/hwrdprkns) (1Password), [@&#8203;Christopher-Schulze](https://github.com/Christopher-Schulze), [@&#8203;Ahmett101](https://github.com/Ahmett101), [@&#8203;sjiangtao2024](https://github.com/sjiangtao2024), and many more — see the salvage PR bodies for full attribution

##### All contributors

[@&#8203;0-CYBERDYNE-SYSTEMS-0](https://github.com/0-CYBERDYNE-SYSTEMS-0), [@&#8203;0disoft](https://github.com/0disoft), [@&#8203;0xbyt4](https://github.com/0xbyt4), [@&#8203;100yenadmin](https://github.com/100yenadmin), [@&#8203;17324393074](https://github.com/17324393074), [@&#8203;2751738943](https://github.com/2751738943), [@&#8203;8294](https://github.com/8294), [@&#8203;abhibansal-sg](https://github.com/abhibansal-sg),
[@&#8203;adambiggs](https://github.com/adambiggs), [@&#8203;Adolanium](https://github.com/Adolanium), [@&#8203;aeyeopsdev](https://github.com/aeyeopsdev), [@&#8203;aguung](https://github.com/aguung), [@&#8203;AhmetArif0](https://github.com/AhmetArif0), [@&#8203;Ahmett101](https://github.com/Ahmett101), [@&#8203;ai-ag2026](https://github.com/ai-ag2026), [@&#8203;AIalliAI](https://github.com/AIalliAI), [@&#8203;ajzrva-sys](https://github.com/ajzrva-sys),
[@&#8203;alastraz](https://github.com/alastraz), [@&#8203;alelpoan](https://github.com/alelpoan), [@&#8203;alex-fireworks](https://github.com/alex-fireworks), [@&#8203;alex-heritier](https://github.com/alex-heritier), [@&#8203;alex107ivanov](https://github.com/alex107ivanov), [@&#8203;AlexFucuson9](https://github.com/AlexFucuson9), [@&#8203;Alix-007](https://github.com/Alix-007),
[@&#8203;allenliang2022](https://github.com/allenliang2022), [@&#8203;Almurat123](https://github.com/Almurat123), [@&#8203;AlsayedHoota](https://github.com/AlsayedHoota), [@&#8203;alt-glitch](https://github.com/alt-glitch), [@&#8203;alvarosanchez](https://github.com/alvarosanchez), [@&#8203;amanning3390](https://github.com/amanning3390), [@&#8203;AmAzing129](https://github.com/AmAzing129),
[@&#8203;AndreasHiltner](https://github.com/AndreasHiltner), [@&#8203;andrewhomeyer](https://github.com/andrewhomeyer), [@&#8203;annguyenNous](https://github.com/annguyenNous), [@&#8203;ansel-f](https://github.com/ansel-f), [@&#8203;antydizajn](https://github.com/antydizajn), [@&#8203;arminanton](https://github.com/arminanton), [@&#8203;arnispiekus](https://github.com/arnispiekus), [@&#8203;asimons81](https://github.com/asimons81),
[@&#8203;asscan](https://github.com/asscan), [@&#8203;ats3v](https://github.com/ats3v), [@&#8203;austinlaw076](https://github.com/austinlaw076), [@&#8203;austinpickett](https://github.com/austinpickett), [@&#8203;avifenesh](https://github.com/avifenesh), [@&#8203;aydnOktay](https://github.com/aydnOktay), [@&#8203;Bartok9](https://github.com/Bartok9), [@&#8203;bautrey](https://github.com/bautrey), [@&#8203;bbednarski9](https://github.com/bbednarski9),
[@&#8203;bbopen](https://github.com/bbopen), [@&#8203;benbarclay](https://github.com/benbarclay), [@&#8203;bigstar0920](https://github.com/bigstar0920), [@&#8203;binhnt92](https://github.com/binhnt92), [@&#8203;bird](https://github.com/bird), [@&#8203;Black0Fox0](https://github.com/Black0Fox0), [@&#8203;BlackishGreen33](https://github.com/BlackishGreen33), [@&#8203;bo](https://github.com/bo).fu, [@&#8203;brendandebeasi](https://github.com/brendandebeasi),
[@&#8203;briandevans](https://github.com/briandevans), [@&#8203;BROCCOLO1D](https://github.com/BROCCOLO1D), [@&#8203;Bruce-anle](https://github.com/Bruce-anle), [@&#8203;brunz-me](https://github.com/brunz-me), [@&#8203;Burgunthy](https://github.com/Burgunthy), [@&#8203;bytesnail](https://github.com/bytesnail), [@&#8203;catbearlove1-lang](https://github.com/catbearlove1-lang), [@&#8203;Cdddo](https://github.com/Cdddo),
[@&#8203;cgarwood82](https://github.com/cgarwood82), [@&#8203;CharmingGroot](https://github.com/CharmingGroot), [@&#8203;chouqin](https://github.com/chouqin), [@&#8203;Christopher-Schulze](https://github.com/Christopher-Schulze), [@&#8203;claudlos](https://github.com/claudlos), [@&#8203;CocaKova](https://github.com/CocaKova), [@&#8203;Code-suphub](https://github.com/Code-suphub), [@&#8203;CodeForgeNet](https://github.com/CodeForgeNet),
[@&#8203;craigdfrench](https://github.com/craigdfrench), [@&#8203;CrazyBoyM](https://github.com/CrazyBoyM), [@&#8203;crazywriter1](https://github.com/crazywriter1), [@&#8203;cresslank](https://github.com/cresslank), [@&#8203;cruzanstx](https://github.com/cruzanstx), [@&#8203;cyrkstudios](https://github.com/cyrkstudios), [@&#8203;danilofalcao](https://github.com/danilofalcao),
[@&#8203;datachainsystems](https://github.com/datachainsystems), [@&#8203;DatTheMaster](https://github.com/DatTheMaster), [@&#8203;davidb73-hub](https://github.com/davidb73-hub), [@&#8203;davidgut1982](https://github.com/davidgut1982), [@&#8203;DavidMetcalfe](https://github.com/DavidMetcalfe), [@&#8203;davidrobertson](https://github.com/davidrobertson),
[@&#8203;deacon-botdoctor](https://github.com/deacon-botdoctor), [@&#8203;DECK6](https://github.com/DECK6), [@&#8203;deepujain](https://github.com/deepujain), [@&#8203;derek2000139](https://github.com/derek2000139), [@&#8203;designnotdrum](https://github.com/designnotdrum), [@&#8203;deusyu](https://github.com/deusyu), [@&#8203;devatnull](https://github.com/devatnull), [@&#8203;devorun](https://github.com/devorun),
[@&#8203;dexhunter](https://github.com/dexhunter), [@&#8203;dfein38347g](https://github.com/dfein38347g), [@&#8203;Dhravya](https://github.com/Dhravya), [@&#8203;DictatorBacon](https://github.com/DictatorBacon), [@&#8203;digitalbase](https://github.com/digitalbase), [@&#8203;dlkakbs](https://github.com/dlkakbs), [@&#8203;dmabry](https://github.com/dmabry), [@&#8203;DNAlec](https://github.com/DNAlec), [@&#8203;dodo-reach](https://github.com/dodo-reach),
[@&#8203;doncazper](https://github.com/doncazper), [@&#8203;dorokuma](https://github.com/dorokuma), [@&#8203;doxe0x](https://github.com/doxe0x), [@&#8203;Drexuxux](https://github.com/Drexuxux), [@&#8203;dschnurbusch](https://github.com/dschnurbusch), [@&#8203;Dusk1e](https://github.com/Dusk1e), [@&#8203;EdderTalmor](https://github.com/EdderTalmor), [@&#8203;egilewski](https://github.com/egilewski), [@&#8203;elashera](https://github.com/elashera),
[@&#8203;Elektrofussel](https://github.com/Elektrofussel), [@&#8203;eliteworkstation94-ai](https://github.com/eliteworkstation94-ai), [@&#8203;embwl0x](https://github.com/embwl0x), [@&#8203;emo-eth](https://github.com/emo-eth), [@&#8203;emozilla](https://github.com/emozilla), [@&#8203;enzo-adami](https://github.com/enzo-adami), [@&#8203;Epoxidex](https://github.com/Epoxidex), [@&#8203;ErnestHysa](https://github.com/ErnestHysa),
[@&#8203;Erosika](https://github.com/Erosika), [@&#8203;esthonjr](https://github.com/esthonjr), [@&#8203;ethernet8023](https://github.com/ethernet8023), [@&#8203;evefromwayback](https://github.com/evefromwayback), [@&#8203;evelynburger](https://github.com/evelynburger), [@&#8203;F4TB0Yz](https://github.com/F4TB0Yz), [@&#8203;falkoro](https://github.com/falkoro), [@&#8203;fanyangCS](https://github.com/fanyangCS), [@&#8203;firefly](https://github.com/firefly),
[@&#8203;fjlaowan1983](https://github.com/fjlaowan1983), [@&#8203;flewe](https://github.com/flewe), [@&#8203;flo1t](https://github.com/flo1t), [@&#8203;flow-digital-ny](https://github.com/flow-digital-ny), [@&#8203;floze-the-genius](https://github.com/floze-the-genius), [@&#8203;frizikk](https://github.com/frizikk), [@&#8203;Frowtek](https://github.com/Frowtek), [@&#8203;FuryMartin](https://github.com/FuryMartin),
[@&#8203;fyzanshaik](https://github.com/fyzanshaik), [@&#8203;gauravsaxena1997](https://github.com/gauravsaxena1997), [@&#8203;geoffreybutler94](https://github.com/geoffreybutler94), [@&#8203;georgedrury](https://github.com/georgedrury), [@&#8203;gigakun3030](https://github.com/gigakun3030), [@&#8203;giggling-ginger](https://github.com/giggling-ginger),
[@&#8203;Git-on-my-level](https://github.com/Git-on-my-level), [@&#8203;gitcommit90](https://github.com/gitcommit90), [@&#8203;githubespresso407](https://github.com/githubespresso407), [@&#8203;gnodet](https://github.com/gnodet), [@&#8203;GottZ](https://github.com/G…
highamperage added a commit to highamperage/hermes-agent that referenced this pull request Jul 24, 2026
…s, dead RPC removal (NousResearch#61067)

* refactor(shared): move terminal-billing wire types to @hermes/shared

The billing/subscription wire shapes (plus UsageBarData/UsageModelData,
which they reference) move verbatim from ui-tui/src/gatewayTypes.ts into
apps/shared/src/billing-types.ts so the desktop app can share the same
gateway contract. gatewayTypes.ts re-exports every moved name from the
new @hermes/shared/billing subpath, so no ui-tui consumer changes.

The subpath export keeps DOM-less ui-tui from pulling the barrel (whose
WebSocket helpers need the DOM lib). ui-tui also now declares its
@hermes/shared dependency explicitly instead of relying on workspace
hoisting.

* test(cli): pin nous_billing wire-layer status-to-exception mapping

The HTTP layer's error handling had zero coverage through _request:
only 2xx parsing and request shaping were tested, and the mapping cases
in test_remote_spending_gate_contract.py hit _raise_for_error directly.

Adds 19 tests driving _request via a monkeypatched urlopen: the
401-refresh-retry path (success, terminal plain/session_revoked,
idempotency-key preservation, base re-resolution), 403 variants through
the wire, 429/503 retry-after, non-JSON error bodies, 404/502
fallbacks, and URLError normalization.

Two behaviors are pinned as findings rather than fixed: a JSON-body
retryAfter hint is ignored unless the Retry-After header is present,
and a bare socket.timeout propagates uncaught (real urllib wraps
timeouts in URLError before this layer).

* fix(cli): normalize read-phase timeouts to the typed billing error

urlopen wraps connect-phase timeouts in URLError (already mapped to
network_error), but a timeout during resp.read() raises a bare
TimeoutError that escaped the typed-BillingError contract and reached
callers as an unhandled exception. Catch it narrowly and normalize.
The boundary test now asserts normalization instead of documenting the
leak.

* fix(shared): stop typing mutation success payloads as error payloads

BillingMutationResponse.payload was declared BillingErrorPayload, but on
ok:true the gateway passes through the raw NAS success body (rail,
changeType, cancelAtPeriodEnd, ...). The TUI never reads it so nothing
broke, but the shared contract now feeds the desktop app too — widen the
field deliberately and document both shapes.

* feat(shared): typed billing refusal and charge-failure unions

- BillingRefusalCode covers every code the gateway serializes today, with a
  (string & {}) arm so unknown future codes (the NAS W3 card-health family)
  stay assignable — consumers keep their unknown-code fallback.
- ChargeFailureReason models the four NAS terminal reasons plus the raw
  subscription_payment_intent_requires_action code NAS leaks pre-NousResearch#711.
- billing.state now carries the server-derived can_change_plan the gateway
  emits; capability comments updated (canChangePlan is capability-based, not
  an OWNER/ADMIN role gate).

* feat(shared): closed Known* halves for the refusal and charge-failure unions

- KnownBillingRefusalCode / KnownChargeFailureReason are closed literal sets,
  so classification tables, copy maps and tests can be Record-exhaustive and
  break at compile time when a code is added but not mapped. The wire types
  keep the (string & {}) open arm for unknown future codes.
- Add network_error (client-originated transport code the gateway already
  serializes) to the known set.
- Export the union types from the root barrel alongside the other billing
  names.

* feat(shared): canonical billing refusal policy and charge-settlement driver

- billing-policy.ts: one exhaustive Record<KnownBillingRefusalCode,
  BillingRefusalPolicy> classifying every known code (recovery kind,
  mid-poll ambiguity, idempotency-key reuse) with a documented unknown-code
  fallback. Surfaces keep their own copy; the behavior classification now
  has a single home that breaks the build when a new code goes unmapped.
- charge-settlement.ts: the settlement poll state machine (2s cadence,
  5-minute cap, bounded retry-after backoff, ambiguous-on-revocation) as a
  pure dependency-injected driver returning a discriminated outcome.
- The TUI's pollCharge becomes a thin renderer over the shared driver —
  byte-identical output, and the desktop poller can now share the same
  machine instead of a drifting copy.
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
…s, dead RPC removal (NousResearch#61067)

* refactor(shared): move terminal-billing wire types to @hermes/shared

The billing/subscription wire shapes (plus UsageBarData/UsageModelData,
which they reference) move verbatim from ui-tui/src/gatewayTypes.ts into
apps/shared/src/billing-types.ts so the desktop app can share the same
gateway contract. gatewayTypes.ts re-exports every moved name from the
new @hermes/shared/billing subpath, so no ui-tui consumer changes.

The subpath export keeps DOM-less ui-tui from pulling the barrel (whose
WebSocket helpers need the DOM lib). ui-tui also now declares its
@hermes/shared dependency explicitly instead of relying on workspace
hoisting.

* test(cli): pin nous_billing wire-layer status-to-exception mapping

The HTTP layer's error handling had zero coverage through _request:
only 2xx parsing and request shaping were tested, and the mapping cases
in test_remote_spending_gate_contract.py hit _raise_for_error directly.

Adds 19 tests driving _request via a monkeypatched urlopen: the
401-refresh-retry path (success, terminal plain/session_revoked,
idempotency-key preservation, base re-resolution), 403 variants through
the wire, 429/503 retry-after, non-JSON error bodies, 404/502
fallbacks, and URLError normalization.

Two behaviors are pinned as findings rather than fixed: a JSON-body
retryAfter hint is ignored unless the Retry-After header is present,
and a bare socket.timeout propagates uncaught (real urllib wraps
timeouts in URLError before this layer).

* fix(cli): normalize read-phase timeouts to the typed billing error

urlopen wraps connect-phase timeouts in URLError (already mapped to
network_error), but a timeout during resp.read() raises a bare
TimeoutError that escaped the typed-BillingError contract and reached
callers as an unhandled exception. Catch it narrowly and normalize.
The boundary test now asserts normalization instead of documenting the
leak.

* fix(shared): stop typing mutation success payloads as error payloads

BillingMutationResponse.payload was declared BillingErrorPayload, but on
ok:true the gateway passes through the raw NAS success body (rail,
changeType, cancelAtPeriodEnd, ...). The TUI never reads it so nothing
broke, but the shared contract now feeds the desktop app too — widen the
field deliberately and document both shapes.

* feat(shared): typed billing refusal and charge-failure unions

- BillingRefusalCode covers every code the gateway serializes today, with a
  (string & {}) arm so unknown future codes (the NAS W3 card-health family)
  stay assignable — consumers keep their unknown-code fallback.
- ChargeFailureReason models the four NAS terminal reasons plus the raw
  subscription_payment_intent_requires_action code NAS leaks pre-NousResearch#711.
- billing.state now carries the server-derived can_change_plan the gateway
  emits; capability comments updated (canChangePlan is capability-based, not
  an OWNER/ADMIN role gate).

* feat(shared): closed Known* halves for the refusal and charge-failure unions

- KnownBillingRefusalCode / KnownChargeFailureReason are closed literal sets,
  so classification tables, copy maps and tests can be Record-exhaustive and
  break at compile time when a code is added but not mapped. The wire types
  keep the (string & {}) open arm for unknown future codes.
- Add network_error (client-originated transport code the gateway already
  serializes) to the known set.
- Export the union types from the root barrel alongside the other billing
  names.

* feat(shared): canonical billing refusal policy and charge-settlement driver

- billing-policy.ts: one exhaustive Record<KnownBillingRefusalCode,
  BillingRefusalPolicy> classifying every known code (recovery kind,
  mid-poll ambiguity, idempotency-key reuse) with a documented unknown-code
  fallback. Surfaces keep their own copy; the behavior classification now
  has a single home that breaks the build when a new code goes unmapped.
- charge-settlement.ts: the settlement poll state machine (2s cadence,
  5-minute cap, bounded retry-after backoff, ambiguous-on-revocation) as a
  pure dependency-injected driver returning a discriminated outcome.
- The TUI's pollCharge becomes a thin renderer over the shared driver —
  byte-identical output, and the desktop poller can now share the same
  machine instead of a drifting copy.
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
NousResearch#61067
added a @hermes/shared dep for `ui-tui`. added it to the nix deps to fix
builds
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/billing Account usage, credit usage, billing (cross-cutting) area/usage-cost Token accounting, usage reporting, billing, cost tracking comp/cli CLI entry point, hermes_cli/, setup wizard comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have type/refactor Code restructuring, no behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants