Commit Graph

278 Commits

Author SHA1 Message Date
emozilla 43e67d872f feat: local models — managed llama.cpp runtime with one-click desktop setup
Run models locally as a first-class provider. The CLI grows a managed
llama.cpp runtime (engine install, model download, server supervision);
the desktop app grows the full setup and management story on top of it.
GUI surfaces ship behind the desktop --local launch flag (hermes desktop
--local, or the flag on the packaged app); backend routes and the CLI
are always live.

Runtime (hermes_cli/local_runtime/):
- curated GGUF catalog with per-machine variant selection: hardware
  probe (VRAM/RAM/UMA), fit planning with spill accounting, quant choice
  by context window
- derived recommendation: quality-ranked picks gated by a predicted
  decode-speed floor, bandwidth-aware on unified memory; the decision
  table is pinned as a test (pick AND reason per memory class), and the
  Recommended badge explains its pick in a tooltip fed by the resolver's
  actual branch
- engine install + model download with resumable split parts, cumulative
  plan-level progress, and staged-model integrity (a split GGUF counts
  only when every part is present)
- server supervision: spawn/adopt/stop, router mode with per-model load
  progress relayed over SSE, abandoned-request cleanup

Desktop:
- Settings -> Providers -> Local models: one-click quickstart (install
  engine, download the recommended model, boot) plus per-model download/
  activate/eject, fit-ranked catalog with context pills
- model pickers (composer dropdown + Cmd+K) show staged local models,
  in-flight downloads as live progress rows, and load-into-memory bars
- local-setup campaign tip for eligible hardware; System resources
  statusbar widget (GPU/VRAM/RAM); in-chat load progress during sends
- friendly dead-server errors, and failed agent builds retry on the next
  send instead of wedging the session

Co-developed with NVIDIA field feedback on RTX 5090 and DGX Spark.
2026-09-01 16:01:53 -04:00
Shannon Sands f5bb1e144d fix(gateway): address startup-watchdog review findings (OOF-298, PR #89750)
Independent review of the initial startup-liveness watchdog surfaced two
P1s and three P2s. All are addressed here.

P1 — legitimate slow startups (large state.db schema migrations inside
SessionDB.__init__, which run synchronously before the loop starts) could
exceed the fixed 300s deadline and restart-loop. The watchdog now checks
process CPU time (time.process_time(), process-wide) when the deadline
expires: continuous CPU consumption means a live migration, so the deadline
is extended (with a warning log per extension). The OOF-298 deadlock class
parks every thread in futex waits and accrues ~zero CPU, so it still fires
on schedule. Documented limitation: a spinning busy-wait deadlock reads as
progress and won't fire — the observed incident class is parked threads.

P1 — import-time deadlocks were outside coverage. The implementation moved
to a stdlib-only top-level module (hermes_startup_watchdog), and
hermes_cli/main.py arms it via an argv fast-path ("gateway" + "run" in
argv) BEFORE the heavy module-level import graph. gateway/startup_watchdog
remains as a re-export shim so the intuitive import path keeps working for
the disarm site, tests, and REPL use. Import-lightness is a correctness
property, tested via AST inspection: at fire time the wedged main thread
may hold the import lock, so the fire path performs no imports on its own
thread — the lifecycle-ledger write runs on a bounded-join helper thread
and os._exit happens regardless.

P2 — disarm/fire race: the handle now has an explicit state machine
(armed → disarmed | firing) guarded by a lock; whichever transition takes
the lock first wins, so a disarm landing after deadline expiry but before
the fire transition is honored. Regression test forces the exact
interleaving by blocking inside the CPU probe.

P2 — uncovered entry points: cli.py --gateway and scripts/hermes-gateway
run_gateway() now arm the watchdog before importing the gateway graph.
hermes_cli/gateway.py run_gateway() keeps an idempotent backstop arm for
programmatic callers.

P2 — respawn-storm backoff interaction: the storm breaker's intentional
backoff sleep (up to minutes, ~zero CPU — indistinguishable from a parked
deadlock) now calls kick_startup_watchdog(extra_s=backoff) so the deadline
is pushed past the sleep instead of firing mid-backoff.

Also: the faulthandler stack dump is now additionally written to
logs/gateway-startup-watchdog.log (stderr may be absent on detached/
windowless runs); the disarm site in gateway/run.py moved inside the
loop-confirmed branch (if the loop is NOT live, the milestone was not
reached and the watchdog must stay armed); hermes_startup_watchdog added
to pyproject py-modules so sealed venvs ship it; SERVICE_RESTART_EXIT_CODE
is duplicated in the stdlib-only module with a parity test against
gateway.restart.

Tests: 38 in tests/gateway/test_startup_watchdog.py (contracts incl.
stdlib-only AST check and shim re-export identity, config resolution,
arm/disarm/kick, CPU-progress extension vs no-progress fire, probe-failure
fails toward firing, disarm-vs-fire race, dump record + file stacks,
lifecycle ledger, custom exit code).
2026-08-31 14:01:39 -07:00
Teknium 29112bef09 chore: release v0.21.0 (2026.8.31) 2026-08-31 12:29:27 -07:00
walker 1f713dce38 test(packaging): exempt every exact pin from exclude-newer — release-day brick class
Each release exact-pins at least one dependency to a version published
days before the release (v0.20.6: snowballstemmer==3.1.1, psutil==7.2.2).
For two weeks after release the relative exclude-newer cutoff filters
those versions out, so any venv that predates the release cannot resolve
the new pins at all ('no version of snowballstemmer==3.1.1' — observed
2026-08-29 updating three production installs v0.20.0 -> v0.20.6, one
Termux and two Linux servers; the Termux host additionally bricked on
psutil==7.2.2 sdist resolution, and cryptography's isolated build
environment resolved maturin/setuptools-rust under the same cutoff).

Same zero-float-protection logic as the setuptools/pillow/mcp/
firecrawl-anydoc exemptions: the pin bump WAS the review, so the cutoff
adds nothing for an exact pin and can only brick. Extend
exclude-newer-package to every exact-pinned package in
[project].dependencies / optional-dependencies (table moved to
one-key-per-line — 97 entries), plus maturin and setuptools-rust for
wheel-less sdist builds of the exempted cryptography pin.

test_exact_pinned_deps_exempt_from_exclude_newer enforces the invariant
going forward: adding a name==version pin without a matching
exclude-newer-package entry fails CI.
2026-08-31 10:03:34 -07:00
Olympusbuildz a300a7fa79 fix(packaging): include wheel in PEP 517 build-system requires
Windows installer editable builds fail in uv's isolated sandbox with
ModuleNotFoundError: wheel.cli because build-system.requires only listed
setuptools. setuptools.build_meta and our setup.py bdist_wheel guard both
import wheel during the build.

Also whitelist wheel in tool.uv.exclude-newer-package so the existing
build-system exclude-newer brick guard stays green.

Fixes #96488

Signed-off-by: Olympusbuildz <Olympus.roots@outlook.com>
Co-authored-by: Olympusbuildz <Olympus.roots@outlook.com>
Signed-off-by: Olympusbuildz <Olympus.roots@outlook.com>
2026-08-31 10:03:34 -07:00
Teknium a9e72f1b58 refactor(read_file): schema diet + bundle anydoc 0.2.4 + typed NeedsOcrError/hosted-OCR wiring (#97195)
* refactor(read_file): capability-gate the anydoc format list; PDF coverage teaching lives in the response-time warning (426 -> 244/291 tok/call)

* feat(read_file): bundle firecrawl-anydoc 0.2.4 in core, typed NeedsOcrError handling, config-gated hosted OCR with local-OCR-first guidance

* refine(read_file): NEEDS-OCR warning hints at checking for an OCR skill without naming one; hosted_ocr knob unadvertised (maintainer-directed)

* simplify(read_file): drop the anydoc schema gate — bundled core dep makes absence a broken install, not a variant; formats stated unconditionally (263 tok/call)

* feat(read_file): PDF wording upgrades to 'scanned or text' when a trusted hosted-OCR route exists (direct key or explicit config; nous gateway excluded until Parse proxy works)

* simplify(read_file): FIRECRAWL_API_KEY is the ONLY hosted-OCR gate — nous gateway route removed (Parse proxy broken), config true no longer unlocks; false still disables
2026-08-28 08:46:11 -07:00
kshitijk4poor c0ff25a1f8 fix(gateway): stop blocking the event loop — off-loop hot sites + ASYNC lint ratchet
Pattern-A architectural fix: blocking calls inside async functions freeze
the gateway/uvicorn event loop for every adapter, timer, and health check.
Known incidents: 17-minute getaddrinfo freeze (#91912 class), 10s restart
freeze in start_gateway (#36163).

Fixes at the four unguarded core sites:
- gateway/platforms/webhook.py: `gh pr comment` subprocess (30s timeout)
  now runs via asyncio.to_thread — a webhook delivery no longer freezes
  every other platform for the duration of a network call.
- gateway/run.py start_gateway --replace: two time.sleep() waits (10s +
  5s worst case) become await asyncio.sleep() (re-lands #36163 at current
  line numbers, credit AhmetArif0).
- gateway/slash_commands.py /save: session render + file write move off
  the loop (scales with transcript size).
- hermes_cli/web_server.py voice TTS: multi-MB audio file read + unlink
  move off the loop.

Prevention gate so the bug class cannot re-enter:
- pyproject.toml [tool.ruff.lint] select gains ASYNC210/220/221/251
  (blocking HTTP / Popen / subprocess.run / time.sleep in async def).
  These run in the existing blocking `ruff check .` CI job.
- Frozen ratchet baseline in per-file-ignores for the remaining legacy
  sites (detached restart watchers; router sweep in flight via #84376;
  two platform adapters), each documented for burn-down. New files or
  new violations fail CI immediately.
- tests/** keeps the relaxation (deliberate sleeps in fixtures).

Verification:
- ruff check . green on this branch; sabotage file with time.sleep +
  subprocess.run in async def fails the gate with 2 errors.
- New behavioral test test_webhook_offloop_delivery.py asserts loop
  liveness DURING delivery (ticker coroutine): 1 tick on the old
  blocking code (fails), 21 ticks off-loop (passes).
- 50 webhook/replace gateway tests + 26 save/export tests pass.

Co-authored-by: AhmetArif0 <147827411+AhmetArif0@users.noreply.github.com>
2026-08-28 07:50:51 -07:00
Gille 0dfba37b11 fix(dashboard): trust configured reverse proxies (#94126)
* fix(dashboard): trust configured reverse proxies

* fix(dashboard): trust IPv6 loopback proxies
2026-08-27 10:35:22 -07:00
Teknium 5fc308a707 chore: release v0.20.6 (2026.8.27) 2026-08-27 05:06:41 -07:00
alt-glitch e455e4afd0 feat(tool-search): multi-query search, batched describe, Snowball stemming
tool_search now takes queries: string[] (searched independently against
the same catalog, limit applies per query, default 5 / max 25) and
returns the split shape: per-query groups carry tool names only, one
shared tools map holds each matched tool's source, description (400-char
cap) and required parameter names once. When some queries miss, a single
top-level available_sources + hint block replaces the old per-response
fallback.

tool_describe now takes names: string[] and returns a map keyed by name;
unknown names collect in not_found (with the refresh hint) and
non-deferrable names keep their per-name spelling-check error in errors,
so one bad name no longer fails the whole call. Duplicates dedupe
silently.

The shared tokenizer now applies Snowball stemming (english, exact-pinned
snowballstemmer) at both index and query time, closing the measured
plural/singular miss where 'issues' failed to return create_issue. The
inline BM25 is unchanged. Stemmer instances are thread-local (they carry
mutable parse state and bridge dispatch can run on parallel tool-call
threads).

New config knobs under tools.tool_search: max_queries / max_describe_names
(default 10 each, floor 1, no upper clamp) bound the per-call array
inputs; over-cap calls error so the model repairs in one round-trip.

No backward compatibility with the single query/name shapes, by decision.
scripts/analyze_livetest.py renders both shapes since transcripts on disk
may predate this change.
2026-08-25 16:39:12 -07:00
Teknium fcbd1076a9 chore: release v0.20.5 (2026.8.19) 2026-08-21 05:16:27 -07:00
Teknium 7e05e9080b chore: release v0.20.4 (2026.8.18) 2026-08-18 00:26:29 -07:00
Teknium 7339f5f160 chore: release v0.20.3 (2026.8.16.2) 2026-08-17 11:43:05 -07:00
elphamale 11a9dcf567 feat(mcp): migrate to the mcp 2.x SDK
mcp 2.0.0 implements MCP revision 2026-07-28 and makes three breaking
changes Hermes sits on top of: `mcp.server.fastmcp` is gone, every model
field is renamed to snake_case (camelCase survives only as a
serialization alias, which pydantic does not expose to attribute
access), and the SDK's own HTTP stack moved from `httpx` to `httpx2`.

Bump the pin across the dev/mcp/computer-use extras and port the tree:

- `mcp_serve.py` and `agent/transports/hermes_tools_mcp_server.py` move
  from `FastMCP` to `mcp.server.MCPServer`, which has the same
  decorator/add_tool surface. The hermes-tools server already
  synthesised `__signature__` from Hermes' JSON Schema, which is exactly
  what 2.0's `add_tool` reads.
- SDK model reads go through `mcp_field(obj, snake, camel)`, which reads
  both spellings. A single-spelling read fails *silently* on the other
  generation — empty tool schemas, dropped structured content, tool
  results vanishing from sampling conversations — and `mcp` is an
  optional extra users install at their own version.
- `sdk_httpx()` resolves the httpx flavour from the SDK's own transport
  module, so objects handed to `streamable_http_client`, the `sse_client`
  factory, and the OAuth metadata helpers come from the module the
  installed SDK actually imports.
- HTTP support is gated on either streamable-HTTP entry point, not just
  the deprecated alias 2.0 removed.
- OAuth: `OAuthClientProvider` lost its `timeout` argument (the
  configured `oauth.timeout` now bounds the callback waiter's own poll
  loop, where the browser round-trip was always awaited), and
  `callback_handler` must return `AuthorizationCodeResult` rather than a
  tuple. 2.0 also validates the RFC 9207 `iss` parameter, so the
  callback handler and paste fallback capture it.

`mcp`/`mcp-types` 2.0.0 are inside the 14-day `exclude-newer` window, so
two narrow `exclude-newer-package` entries unblock `uv lock`, annotated
for removal on or after 2026-08-11. `httpx2` needs no exemption: 2.7.0 is
already outside the window and satisfies mcp's floor.

Refs #69931

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 23:26:10 -07:00
Teknium df4b65147d chore: release v0.20.2 (2026.8.16) 2026-08-16 10:32:53 -07:00
Teknium bc36d7d6c8 fix(deps): exempt no-upload-date and exact-pinned packages from exclude-newer bricking
The relative exclude-newer = "14 days" cutoff bricks installs whenever the
resolver cannot see (or accept) a package's upload date:

- defusedxml / python-olm / unpaddedbase64 (#80387, #79434): ancient frozen
  releases (2021-2023) whose upload dates are often absent from mirror
  indexes and stale uv HTTP caches. uv then filters them entirely
  ("there are no versions of defusedxml"), breaking [youtube]/[wecom]/
  [matrix] resolution and daily `uv sync --locked` runs.

- setuptools / pillow / mcp (#78227, #75992, #76020): exact-pinned deps.
  When the pinned version's upload date is invisible, the resolver filters
  the ONLY acceptable candidate — setuptools==83.0.0 in
  [build-system].requires meant the project could not even be built from a
  git checkout on released v0.20.0. Exempting an exact pin costs nothing:
  the version cannot float without a reviewed pin bump.

Changes:
- pyproject.toml: add all six to the existing exclude-newer-package
  whitelist, with rationale comments per class.
- uv.lock: regenerated; diff is the whitelist metadata only (verified
  zero version drift, still 249 packages).
- tests/test_packaging_metadata.py: new standing guard
  test_build_system_requires_exempt_from_exclude_newer — every
  [build-system].requires package must be whitelisted while a relative
  exclude-newer cutoff is configured. Verified both directions (fails
  when setuptools is removed from the whitelist).
- scripts/install.sh: fix the stale tier-name comparison ("all (with
  RL/matrix extras)" vs actual "all") that mislabeled every successful
  Tier-1 install as a fallback-tier install (#79434 bonus finding).

Verification: uv lock --check green on uv 0.11.19 and 0.12.5;
uv sync --extra all --locked green; uv pip install -e '.[all]' resolves;
whitelist mechanism A/B-proven on a minimal project (unsatisfiable ->
resolves; build-requires variant: uv build fails -> succeeds).

Reported-by: MichaelClawHub (#80387), liujianqiu (#79434), maxonliu (#78227)
2026-08-14 21:50:48 -07:00
joaomarcos 69d1843512 fix(packaging): ship bundled plugin manifests 2026-08-14 21:45:08 -07:00
ethernet 89d3e43f5e fix(nix): add registration lifecycle to pyproject.toml 2026-08-13 18:32:22 -04:00
Teknium 720f0443a0 chore: release v0.20.1 (2026.8.13) 2026-08-13 11:52:47 -07:00
Teknium 91345435a1 chore(deps): bump platform SDK pins — PTB 22.8, slack-bolt 1.30.0, mautrix 0.21.1
Weekly platform API scout, Aug 12 2026:
- python-telegram-bot 22.6 -> 22.8: full Bot API 9.6 + 10.0 support
  (poll persistent ids, guest mode types, live photos). No hermes code
  uses the deprecated surfaces (positional InputMedia* filename,
  correct_option_id, InputPollOption.de_json) — verified by grep.
- slack-bolt 1.29.0 -> 1.30.0: assistant DM suggested-prompts widening;
  no breaking changes (Bolt Python is unaffected by the Bolt JS v5 /
  Node SDK major-version wave).
- mautrix 0.21.1: corrupted-invite safety, /messages response parsing
  fix, custom join-rule enum values.

Validated: uv lock regenerated; real-SDK install (uv sync) then
tests/gateway telegram (590 passed) + slack/matrix (662 passed) suites
against the upgraded packages.
2026-08-12 19:43:53 -07:00
Teknium 2e0183169c feat(plugins): community plugin index + hermes plugins search (#64181)
Static machine-readable community plugin index with fuzzy search and
index-resolved installs, mirroring the Skills Hub catalog pattern
(fetch → HERMES_HOME/cache with 24h TTL → bundled seed fallback).

- hermes_cli/plugin_index.py: index fetch/cache/seed chain, fuzzy
  search (name/description/tags/author + typo tolerance), capability
  filter, bare-name resolution. Canonical URL overridable via
  plugins.index_url config key.
- hermes_cli/data/plugin_index.json: bundled seed (offline fallback +
  format reference) with 5 real ecosystem plugins, each pinned to an
  exact commit SHA.
- hermes plugins search [term] [--json] [--capability] [--refresh]:
  Rich table or JSON output, offline-safe, with an explicit
  'indexed ≠ audited' footer.
- hermes plugins install <name>: bare names (no slash, no URL scheme)
  resolve through the index to owner/repo[/subdir] @ pinned ref and
  hand off to the existing install path (ref wired through the #82029
  exact-ref support). Ambiguous names list candidates and exit;
  explicit owner/repo and Git URL installs are untouched, and an
  explicit --ref always beats the index pin.
- Docs: discovery section in user-guide plugins.md (format, submission
  workflow via PR to hermes-plugin-index, security framing) and
  reference/cli-commands.md rows.
- Tests: tests/hermes_cli/test_plugin_index_search.py (38 tests, no
  live network) covering parsing, search, remote→cache→seed fallback,
  TTL, install resolution/ambiguity/passthrough, and --json output.
2026-08-12 18:52:08 -07:00
ethernet 37e46c774c cleanup: remove references to simple-term-menu
we migrated away long ago.
clean up all docs references the dependency itself
2026-08-10 15:13:29 -04:00
ethernet 2b618fe7e5 fix(sec): move cryptography to 50.0.0
cryptography 48.0.1 carries three advisories (GHSA-m2h6-j472-rp4c,
GHSA-jwv3-5hgf-82ww, CVE-2026-69247). msal and alibabacloud-tea-openapi
cap cryptography below 49, so the bump needs an override-dependencies
entry in [tool.uv] to take effect.

The cap is conservative, not a real limit: we installed tea-openapi
against cryptography 50 and its client ran with no errors.

This override only governs `uv lock` / `uv sync`. The lazy-install
path does not read [tool.uv] and can still downgrade the pin; the next
commit closes that path.

aiohttp moves to 3.14.3 in the same pass, for GHSA-9548-qrrj-x5pj.
2026-08-10 13:49:37 -04:00
ethernet 30da5d0a89 test: run os-specific tests on their real host, not a faked one
many tests patched sys.platform or a module's _IS_WINDOWS flag, then
ran on linux ci. the patch selects the branch under test, but the host
does not have the behavior the branch exists for. the test proves the
patch, not the platform. some gated assertions never ran on any host.

this commit adds three markers: linux_only, macos_only, windows_only.
a conftest hook skips a marked test on the other hosts, with a clear
reason. no test fakes a host now. two documented fakes remain
(android/termux, freebsd) because no ci runner exists for them.

each fake site got one of four treatments:
- gate it: the real host supplies the platform; mocks cover real
  dependencies only, never host identity
- patch the module's own probe when the subject is the probe's consumer
- assert against the real host when the fake stood in for any non-x host
- delete the patch when it set the value the host already has

bare skipif(sys.platform != ...) guards became markers too. the lane
model skips these on linux and never imports them on windows, so they
ran on no host. platform parametrize tables are now one marked test
per os.

running on real hosts found real errors: a chrome-sandbox failure in
test_gui_command that main hides, and two windows failures fixed here.
the agents.md testing section now documents the policy.
2026-08-09 22:09:49 -04:00
Teknium 7537de9e74 fix(deps): patch 31 known CVEs across Python and npm lockfiles
OSV weekly scan reported 50 known vulnerabilities in pinned deps.
This bumps everything with a released, semver-compatible fix:

Python (uv.lock):
- aiohttp 3.14.1 -> 3.14.3 (GHSA-cq5v-8q36-5273, GHSA-mfx4-hv73-q22v,
  GHSA-mq44-7p77-q5h7)
- h2 4.3.0 -> 4.4.1 (CVE-2026-71554 request smuggling; exclude-newer
  exception documented in pyproject, remove after 2026-08-17)

npm (root workspace):
- brace-expansion 5.0.8 -> 5.0.9, undici 6.27->6.28 / 7.28->7.29,
  js-yaml 4.3.1, nanoid 3.3.17/3.3.18, ip-address 10.4.0,
  mermaid 11.16.1 + dompurify 3.4.13 (root overrides so the
  streamdown transitive copy is pinned too)
- electron 40.10.2 -> 40.10.6 (GHSA-r4w5-6pfg-jxp5; the 41.x major
  for GHSA-9f4c-93c8-jc8g is deferred to its own PR)

npm (website): mermaid, dompurify, js-yaml, nanoid, fast-uri 3.1.5,
postcss 8.5.23, undici 7.29.0
npm (photon sidecar): @opentelemetry/core 2.8.0 via override, undici
npm (whatsapp-bridge): body-parser 1.20.6

min-release-age excludes added to .npmrc/website/.npmrc for the
sub-2wk CVE-fix releases, each with a removal date.

Remaining findings are blocked upstream: cryptography <49 cap
(alibabacloud-tea-openapi), image-size (no fixed release), tar 6.x
transitive majors, electron 41.

Local rescan: 50 -> 19 known vulns, 0 introduced.
2026-08-08 14:06:48 -07:00
Bryan Bednarski 920eaf2fc4 chore(relay): require 0.7.1
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
2026-08-07 10:31:49 -06:00
kshitij 1709f82c15 chore: remove dead tomli dependency declaration
requires-python is >=3.11 so tomllib is always in stdlib; the
tomli fallback branch in _lint_toml_inproc was unreachable. Removes
the dependency from pyproject.toml + uv.lock and deletes the dead
try/except ImportError fallback in the code.
2026-08-04 14:34:24 +05:30
阿泥豆 eb78ab235f fix(file-ops): decouple BOM detection from pre_content, add V4A backward compat
Bug 1 (UTF-8 BOM loss on V4A UPDATE):
_file_has_bom() trusted pre_content for BOM detection, but the most
common pre_content provider — read_file_raw() — deliberately strips
BOMs so the agent never sees U+FEFF glyphs.  Passing BOM-stripped
content through pre_content caused a false-negative: the method
returned False and write_file() silently removed the marker on rewrite.

Fix: _file_has_bom() now always probes the first 3 bytes on disk
(head -c 3), ignoring pre_content for BOM purposes.  pre_content is
still used by two other consumers — line-ending detection and lint/LSP
delta computation — neither of which is affected by BOM stripping.

Bug 2 (backward compatibility):
_apply_update() called write_file(path, content, pre_content=...) as a
keyword argument.  Duck-typed file_ops implementations that only
implement the two-argument write_file(path, content) contract would
raise TypeError.

Fix: wrap the call in try/except TypeError, falling back to the
two-argument form when the keyword is not accepted.

Also declare tomli in pyproject.toml (pre-existing conditional import
for pre-3.11 Python, caught by the pre-commit dep scan after staging
file_operations.py).

Tests:
Add TestV4ABomRoundTrip with two cases:
  - UPDATE on BOM-bearing file preserves the marker
  - UPDATE on plain file does not inject a BOM

Addresses teknium1 review on PR #55661.
2026-08-04 14:34:24 +05:30
Jeffrey Quesnelle 70db671fac Merge pull request #76643 from NousResearch/fix/termux-nemo-relay
exempt android installs from nemo-relay
2026-08-04 00:06:56 -04:00
Jeffrey Quesnelle dac4bbea09 fix comment about relay workaround 2026-08-03 19:13:22 -04:00
Teknium 3c27eb6234 chore: release v0.20.0 (2026.8.3)
The Herald Release — voice (streaming TTS, barge-in, wake words), A2A v1.0,
outbound webhooks, grounded citations, desktop platform wave. ~3,650 commits,
~1,400 PRs, ~1,200 issues closed, 650+ contributors since v0.19.0.

Also: contributor audit additions (18 email mappings, bot-filter widening).
2026-08-03 09:57:23 -07:00
emozilla 3b767d8905 exempt android installs from nemo-relay 2026-08-02 02:45:21 -04:00
Eugeniusz Gilewski 64dd865912 fix(deps): repair Google transitive security floors (#72108)
Google API and authentication packages permit vulnerable httplib2 and pyasn1
transitives, while the Workspace and Google Chat runtime installers previously
treated any importable version as sufficient. Existing environments could
therefore remain vulnerable after the project dependency pins were repaired.

Carry the fixed versions through the Google and Vertex extras, lazy feature
requirements, lockfile, and both runtime installers. Route the documented
Google Chat installation path through its maintained secure requirements
instead of an unconstrained direct pip command.

Detect stale distributions, install only unsatisfied requirements, and verify
the result before continuing. Behavioral tests cover those repair invariants
without freezing manifests, lockfiles, or complete package sets.

Related #72108
Extracted from #72840
Co-authored-by: Teknium <127238744+teknium1@users.noreply.github.com>
2026-07-31 23:18:38 -07:00
Yorkstone Supplies (sycamoregroupltd) a7c26bbb5c fix(deps): pin patched httplib2 for google extra 2026-07-31 22:28:21 -07:00
ethernet 88aed1b797 feat(sec): add exclude-newer = 14 days to uv.lock
disabled for 2 packages that are too new
2026-07-31 13:42:03 -04:00
ethernet ea266a6061 fix(sec): update pynacl to 1.6.2
pynacl 1.5.0 has 1 known vulnerability:
PYSEC-2026-3002
it's fixed in >=1.6.2
2026-07-31 13:42:03 -04:00
ethernet abcd213504 fix(sec): update pillow to 12.3.0
pillow 12.2.0 has 26 known vulnerabilities:

GHSA-45hq-cxwh-f6vc
GHSA-4x4j-2g7c-83w6
GHSA-5x94-69rx-g8h2
GHSA-62p4-gmf7-7g93
GHSA-6r8x-57c9-28j4
GHSA-8v84-f9pq-wr9x
GHSA-9hw9-ch79-4vh6
GHSA-fj7v-r99m-22gq
GHSA-jjj6-mw9f-p565
GHSA-pg7v-jwj7-p798
GHSA-phj9-mv4w-65pm
GHSA-vjc4-5qp5-m44j
GHSA-xj96-63gp-2gmr
PYSEC-2026-2253
PYSEC-2026-2254
PYSEC-2026-2255
PYSEC-2026-2256
PYSEC-2026-2257
PYSEC-2026-3451
PYSEC-2026-3452
PYSEC-2026-3453
PYSEC-2026-3454
PYSEC-2026-3493
PYSEC-2026-3494
PYSEC-2026-3495
PYSEC-2026-3496

they're fixed in >= 12.3.0
2026-07-31 13:42:03 -04:00
ethernet a7efeb0829 fix(sec): update mcp to 1.28.1
mcp 1.26.0 has 3 known vulnerabilities: PYSEC-2026-3481,
PYSEC-2026-3482, PYSEC-2026-3483

they're fixed in >= 1.28.1
2026-07-31 13:42:03 -04:00
ethernet e9f64e83c1 fix(sec): update setuptools to 83 2026-07-30 21:36:47 -04:00
Teknium cc4cab2f59 chore: release v0.19.1 (2026.7.30) 2026-07-30 16:45:08 -07:00
ethernet 36e41c09ed fix(nix): include new flat modules at the root (#74362) 2026-07-30 20:28:36 +05:30
BlutAgent b631f80b7a fix(tests): register no_isolate and ssh pytest markers
Registers the two genuinely-unregistered markers (no_isolate, ssh) in
pyproject.toml, silencing PytestUnknownMarkWarning for tests/tools/test_mcp_*
and tests/tools/test_file_sync_perf.py.

Dropped hunk: the live_system_guard_bypass line from the original PR — that
marker is already registered dynamically via pytest_configure/addinivalue_line
in tests/conftest.py, so the ini entry would be a duplicate.

Salvaged from #71403.
2026-07-29 21:30:53 -07:00
Jeff Watts f708a85d2e test(tui_gateway): make the tui gateway suite order-independent
Cross-file leaks made tests/tui_gateway + tests/test_tui_gateway_server.py
fail when run in one process (issue #57068):

- conftest: _hermetic_environment now re-pins hermes_state.DEFAULT_DB_PATH
  to the fake home so no test ever touches the developer's real state.db
- conftest: new autouse _reset_tui_gateway_server_state snapshots/restores
  _methods, cfg cache, db handle and _real_stdout, and tears down leftover
  sessions via the production _close_session_by_id(..., end_reason=
  "test_cleanup") boundary
- per-file fixtures scope patch.dict(sys.modules) to the import only
- drop reload()-based teardowns that duplicated atexit hooks

Conflict resolution vs main: kept main's mod._live_transports.clear() in
the test_protocol.py server fixture alongside the snapshot/restore logic.

Combined run now 792 passed / 0 failed.

Salvaged from PR #57066 by @lEWFkRAD. Fixes #57068.
2026-07-29 21:30:53 -07:00
Teknium c770515e2b modernize re-added Vercel integrations: SDK 0.7.2, telemetry off, sibling-site wiring
- Bump vercel SDK pin 0.5.7 -> 0.7.2 (pyproject, lazy_deps) and regenerate uv.lock
- Disable the SDK's new default-on telemetry (VERCEL_TELEMETRY_DISABLED=1
  set before import, user-overridable) per the no-opt-out-telemetry policy
- Move _model_flow_ai_gateway into hermes_cli/model_setup_flows.py (god-file
  decomposition landed after the removal)
- Widen post-removal backend sets that vercel_sandbox missed: terminal_tool
  container_backend + _CONTAINER_BACKENDS, file_tools fallback set,
  env_probe._REMOTE_BACKENDS, approval._should_skip_container_guards,
  prompt_builder probe container_config
- Add terminal.vercel_runtime to config_defaults + TERMINAL_CONFIG_ENV_MAP
- Re-add vercel dependency group to nix #full variant (reverts #33773 workaround)
- Update restored tests to current contracts: upload-only credential sync-back
  (bcfc7458fa), registry-derived provider env list, parametrized backend fixture,
  drop tests superseded on main (slack wizard move #41112, nous status format)
2026-07-29 19:48:37 -07:00
Teknium ad12df6ba4 Revert "remove Vercel AI Gateway and Vercel Sandbox (#33067)"
This reverts commit febc4cfec0.
2026-07-29 19:48:37 -07:00
victor-kyriazakos 1773752c8c Merge remote-tracking branch 'origin/main' into feat/gateway-health-diagnostics-monitoring
# Conflicts:
#	uv.lock
2026-07-29 15:37:14 +00:00
Teknium 0cf58de85e Merge remote-tracking branch 'origin/main' into wake-toggle-config
# Conflicts:
#	tests/test_tui_gateway_server.py
#	tui_gateway/server.py
2026-07-28 12:37:35 -07:00
Ben Barclay 7d19033d2e fix(wake): run openWakeWord on tflite on macOS ARM64
openWakeWord's ONNX backend returns near-zero scores on Apple Silicon
(dscripka/openWakeWord#336), so "Hey Hermes" never crossed the 0.5
threshold: the listener armed, the microphone worked, and nothing fired.

Bisecting the pipeline puts the fault in exactly one stage — feeding the
same audio through both backends, the melspectrogram front-end is
bit-identical (maxdiff 0.00000) and the wake classifier agrees on
identical features, while the shared embedding model diverges by 45.44.
Cross-feeding confirms it: tflite features scored through the *onnx*
classifier give 0.9948 vs 0.000009 for onnx features. A telling
secondary symptom is that scores fall as input gets louder (0.5x ->
0.00031, 8x -> 0.000066), which is garbage inference rather than a weak
detection.

Selecting tflite in config alone does not fix it. openWakeWord hardcodes
`import tflite_runtime.interpreter` but declares tflite-runtime for
`platform_system == "Linux"` only; on macOS the equivalent wheel is
ai-edge-litert, so that import always fails and model.py silently
downgrades back to onnx. The result is a detector that reports itself
listening and can never fire.

- default the backend per platform (tflite on macOS ARM64, onnx
  elsewhere) instead of hardcoding onnx, and pick the matching bundled
  model artifact
- bridge tflite_runtime -> ai_edge_litert through sys.modules, in-process,
  with no writes to site-packages
- refuse the silent onnx downgrade on macOS ARM64 and report the missing
  runtime through check_wake_word_requirements() so the GUI surfaces an
  actionable hint rather than arming a dead ear
- lazy-install ai-edge-litert via its own feature key, because lazy-dep
  specs cannot carry PEP 508 markers (_spec_is_safe rejects ";")

An explicit `inference_framework` in config still wins, so anyone pinning
a backend keeps it.

Verified on macOS 26.5.2 / M-series: "hey hermes" scores 0.0005 on onnx
and 0.9423 on tflite from the same clip, with cross-phrase controls at
0.0003. Live over-the-air through the real microphone fires 4/4
utterances (peak 0.9532).
2026-07-28 07:59:34 -07:00
Teknium a832139ba3 feat(wake): eager-install voice deps with the desktop; wake probes never run pip
Two fixes from live testing (Teknium):

1. Desktop installs now ship the wake/voice stacks up front.
   install.sh + install.ps1 desktop stages run 'uv pip install
   -e .[wake,voice]' (best-effort, lazy-install remains the fallback)
   before building the app, so the first ear-click arms instantly
   instead of sitting through a multi-minute onnxruntime download.
   CLI-only installs keep the lazy path — [all] curation unchanged.

2. The vanished ear: the STT/TTS gate made wake.status call
   check_tts_requirements(), whose edge path runs _import_edge_tts →
   lazy_deps.ensure — a synchronous PIP INSTALL inside a status poll.
   On a venv without edge-tts that blew the desktop's 30s RPC timeout,
   armWakeWord caught the error, the atom never learned enabled=true,
   and the ear unmounted. _tts_ready is now a pure probe: deps missing
   + lazy installs allowed counts as ready (installs at first speak)
   WITHOUT touching pip; check_tts_requirements only runs once deps
   are present. Regression test asserts the probe never calls it while
   deps are missing.
2026-07-28 07:59:34 -07:00
Hermes Agent 0ae305ed4e feat(voice): open-vocabulary wake phrases via sherpa-onnx KWS
New "sherpa" wake_word provider: the configured phrase is BPE-tokenized
at runtime against a small streaming zipformer KWS model (~13 MB English,
one-time download cached under HERMES_HOME), so ANY typed phrase works
with zero training — including per-profile phrases like "hey coder".

wake.sherpa lazy-dep group + [wake] extra grow sherpa-onnx/sentencepiece;
requirements probe routes per provider; sensitivity maps onto sherpa
keywords_threshold. E2E-verified on real audio (target phrase fires,
foreign phrase stays silent, reset drops buffered state).
2026-07-28 07:58:16 -07:00