Skip to content

Batch salvage group 4: 9 low-risk new-contributor PRs (proxy-env/gateway-fixes/security-headers/custom-providers)#27308

Merged
teknium1 merged 10 commits into
mainfrom
hermes/hermes-e1ed3e9f
May 17, 2026
Merged

Batch salvage group 4: 9 low-risk new-contributor PRs (proxy-env/gateway-fixes/security-headers/custom-providers)#27308
teknium1 merged 10 commits into
mainfrom
hermes/hermes-e1ed3e9f

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Batch salvage group 4 — 9 low-risk new-contributor PRs onto current main with per-commit contributor authorship preserved. Continues the LHF run (groups 1-3 = #27247, #27292, #27302).

Salvaged PRs

PR Author Change
#26657 @EloquentBrush0x fix(gateway): trust_env=True on SMS / Slack / Teams / Google Chat aiohttp sessions so HTTP_PROXY/HTTPS_PROXY are honored
#25658 @subtract0 fix(gateway): use $rc instead of read-only zsh $status in the update wrapper
#26961 @zwolniony fix(doctor): use x-goog-api-key (Google's preferred header) instead of Bearer for generativelanguage.googleapis.com connectivity probe
#26582 @that-ambuj fix(gateway): preserve underscores inside identifiers in the plain-text markdown strip via word-boundary anchors
#26134 @francip feat(gateway): make auto-TTS markdown strip overridable via prepare_tts_text() hook
#25294 @zccyman fix(auxiliary): resolve api_key_env alias in the named-custom-provider path of resolve_provider_client
#26814 @lidge-jun fix(api-server): add browser security headers (CSP/HSTS/XFO/Permissions-Policy)
#26768 @phoenixshen fix: respect user-configured vision model for OpenRouter instead of always using the default
#26635 @AhmetArif0 fix(line): trust_env=True on LINE adapter aiohttp sessions (complements #26657 for non-Slack platforms)

Pre-flight drop

#27147 (0xchainer — skill load returning None) dropped from this batch — already on main as commit 4b17c24. Same pattern as #27154 in group 3: the fix landed independently under proper attribution before this batch ran. Will close as redundant.

Attribution

Test plan

  • Compile-clean across all 14 touched .py files.
  • Targeted suites: tests/gateway/test_api_server.py + test_bluebubbles.py + test_update_streaming.py + tests/hermes_cli/test_doctor.py → 259 passed in 6.69s.

Rebase-merge so per-commit contributor authorship survives.

EloquentBrush0x and others added 10 commits May 16, 2026 23:09
…eams, Google Chat adapters

aiohttp.ClientSession defaults to trust_env=False, which silently ignores
HTTP_PROXY, HTTPS_PROXY, and ALL_PROXY environment variables. Users behind
a corporate or network proxy cannot reach external APIs on any of these
platforms — all outbound requests fail with connection errors.

Symmetric with wecom.py (line 276), weixin.py (lines 1055/1268/1274), and
matrix.py (no-proxy path) which already set this flag. Complements the
open LINE fix (#26635) with the remaining gateway and plugin adapters.

Changed:
- gateway/platforms/sms.py: persistent Twilio session (connect) + fallback
  session (send) — both hit https://api.twilio.com
- gateway/platforms/slack.py: ephemeral response_url POST session —
  hits https://hooks.slack.com/... callback URLs
- plugins/platforms/teams/adapter.py: standalone send session —
  hits login.microsoftonline.com (token) + Bot Framework service URL
- plugins/platforms/google_chat/adapter.py: standalone send session —
  hits https://chat.googleapis.com/v1/...

WhatsApp sessions are excluded: they connect to http://127.0.0.1:{port}
(local bridge) and must not be routed through a system proxy.
…) hook

Refactor the inlined `re.sub(...)[:4000].strip()` cleanup at the
auto-TTS site in `_process_message_background` into an overridable
method `BasePlatformAdapter.prepare_tts_text(text: str) -> str`.

The default implementation is byte-identical to the previous inline
expression — strip `* _ \` # [ ] ( )` and truncate to 4000 chars — so
every existing adapter (Telegram, Discord, Slack, Matrix, IRC, etc.)
gets exactly the same behaviour as before. Zero behaviour change for
any consumer that doesn't override the method.

Why add the hook: voice-first platform adapters need stricter
cleanup than text-bubble platforms. The default strips a handful of
markdown sigils, which is fine when the output goes into a Discord
embed or a Telegram message bubble — but read aloud by a TTS engine,
URLs (`https://example.com/foo`), fenced code blocks, file paths
(`/Users/x/foo.py`), and `MEDIA:` tags turn into long sequences of
unintelligible characters. With this hook an adapter can drop those
spans before TTS while leaving the data-channel transcript intact
for visual rendering.

Without the hook, voice adapters have to either
  - duplicate the auto-TTS flow inside their own `handle_response`
    pipeline, which means re-implementing the entire `extract_media`,
    `extract_images`, `extract_local_files`, attachment routing and
    error-handling sequence in `_process_message_background`, or
  - live with TTS speaking URLs character-by-character.

Both are worse than a 7-line method addition.

Example consumer:
  https://github.com/kortexa-ai/hermes-livekit — LiveKit WebRTC voice
  gateway plugin. Its `LiveKitAdapter.prepare_tts_text()` additionally
  strips fenced code blocks, inline code, URLs, file paths, and
  `MEDIA:` tags before TTS synthesis, while the full response still
  reaches connected clients via the data channel. Drop-in installable
  via `pip install git+https://github.com/kortexa-ai/hermes-livekit.git`.

Carved out of #3894 (LiveKit WebRTC gateway PR) so the generic hook
can land independently of the LiveKit platform itself.
…th of resolve_provider_client

In resolve_provider_client(), the named custom provider code path at
~line 2914 only checked the ``key_env`` field when looking for an
environment-variable-based API key. The documented ``api_key_env``
snake_case alias was silently ignored, causing custom providers
configured with ``api_key_env`` to fall through to the
``no-key-required`` placeholder — which produces a confusing 401
(``****ired`` mask) on auth-required remote endpoints.

This mirrors the same fix already applied to run_agent.py in commit
6ddc48b (fix(fallback): resolve api_key_env in fallback chain entries).

Also adds a logger.warning() when the placeholder is reached, so
future alias gaps are easier to debug.

Closes #25091
_OPENROUTER_MODEL hardcoded 'google/gemini-3-flash-preview' which
returns 404 on OpenRouter, breaking all vision tasks for users who
rely on the OpenRouter default.  Additionally, _try_openrouter()
ignored the user-configured auxiliary.vision.model entirely.

Changes:
- Update _OPENROUTER_MODEL default to google/gemini-2.5-flash (valid)
- Add optional 'model' parameter to _try_openrouter()
- Pass configured model from _resolve_strict_vision_backend() through
  to _try_openrouter()

This allows users who set auxiliary.vision.model (e.g. x-ai/grok-4.3)
to have it actually used, while maintaining backward compatibility.
_LineClient's five aiohttp.ClientSession() calls omit trust_env=True,
silently bypassing HTTP_PROXY / HTTPS_PROXY / ALL_PROXY. Result: every
LINE API call (reply, push, loading, fetch_content, get_bot_user_id)
ignores the system proxy.

Fix: add trust_env=True to all five session constructions. Symmetric
with the wecom and weixin adapters which already set this flag. No
behavior change for users not behind a proxy.
…tors

Adds release-note attribution mappings for 9 contributors from group 4:
- @EloquentBrush0x (PR #26657)
- @subtract0 (PR #25658)
- @zwolniony (PR #26961)
- @that-ambuj (PR #26582)
- @zccyman (PR #25294)
- @lidge-jun (PR #26814)
- @phoenixshen (PR #26768)
- @AhmetArif0 (PR #26635)
- (francip already mapped from prior PR #26134 attribution)

#27147 dropped from this batch — already landed on main as 4b17c24.
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-e1ed3e9f vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 8358 on HEAD, 8356 on base (🆕 +2)

🆕 New issues (1):

Rule Count
invalid-argument-type 1
First entries
agent/auxiliary_client.py:3410: [invalid-argument-type] invalid-argument-type: Argument to function `_try_openrouter` is incorrect: Expected `str`, found `str | None`

✅ Fixed issues: none

Unchanged: 4370 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

francip added a commit to kortexa-ai/hermes-livekit that referenced this pull request May 21, 2026
…ce (0.3.1)

Two voice-path bugs surfaced by review feedback on the now-closed core
PR NousResearch/hermes-agent#3894:

- _on_track_subscribed seeded _last_audio_time on subscribe, defeating
  the _check_silence_loop guard that treats a missing entry as "never
  spoke" and discards accumulated noise. A participant publishing only
  silence would accrue a stale timestamp and eventually trip STT on
  silence. The timestamp is now set only on the first chunk above the
  RMS floor.

- The track-end utterance flush in _cleanup_participant computed
  speech_end = max(0, len(buf) - silence_bytes), unconditionally
  trimming a fixed silence window. When a track ends right after a word
  with no trailing silence buffered, that chops real speech or zeroes
  the flush. On track end the flush now transcribes the whole buffer —
  trailing silence handed to STT is harmless, lost words are not.

Also corrects a stale prepare_tts_text docstring: upstream
BasePlatformAdapter now calls the hook (landed via
NousResearch/hermes-agent#27308), so the override is live.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists provider/gemini Google Gemini (AI Studio, Cloud Code) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.