Commit Graph

1225 Commits (b8dd1bf3a5b94de3eb705d6d94f289b47e4b2715)

Author SHA1 Message Date
teknium1 7e7e3af5b0 chore: map allenliang2022 in AUTHOR_MAP for #56932 test fold-in 2026-07-06 01:56:09 -07:00
kshitijk4poor c67aab763d chore: map isheng-eqi in AUTHOR_MAP for #59428 salvage
check-attribution CI fails on unmapped bare (non-noreply) contributor
emails. isheng-eqi's commit email (ishengeqi@163.com) has no + so it does
not auto-resolve — add the explicit mapping.
2026-07-06 13:47:18 +05:30
teknium1 f600dfca96 chore(release): map author emails for PR #56854/#57417 salvage 2026-07-05 21:48:59 -07:00
teknium1 43a4256320 fix(mcp): wake stale cached servers on session startup + AUTHOR_MAP
register_mcp_servers now nudges cached entries whose session is None
via _signal_reconnect, so a new agent session recovers a parked server
immediately instead of waiting up to _PARKED_RETRY_INTERVAL for the
next self-probe (#50170). Gate-check idea credit: @izumi0uu (#50184),
@LeonSGP43 (#37772), @Tranquil-Flow (#37899).
2026-07-05 21:48:36 -07:00
teknium1 5cc7c9b6a0 chore(release): map derek2000139 author email for PR #57838 salvage 2026-07-05 21:36:23 -07:00
teknium1 81becec45d chore: docs table entry + AUTHOR_MAP for preflight cluster salvage 2026-07-05 21:36:19 -07:00
teknium1 b6f230b88e chore(release): map EdderTalmor author email for PR #41575 salvage 2026-07-05 19:13:20 -07:00
teknium1 b80b0b682a test(mcp): parked server self-probe revival + AUTHOR_MAP for #54139 salvage 2026-07-05 19:10:31 -07:00
teknium1 8a9e30dbd5 chore: AUTHOR_MAP entries for #54494/#56699 salvage 2026-07-05 19:10:00 -07:00
teknium1 6133285596 chore(release): map Alix-007 author email for PR #54620 salvage 2026-07-05 17:38:36 -07:00
teknium1 c5a8df3af2 chore(release): map jashlee+microsoft@microsoft.com -> s905060 (PR #57943 salvage) 2026-07-05 17:38:32 -07:00
teknium1 2f2e60801c chore: add l0h1nth to AUTHOR_MAP for PR #32210 salvage 2026-07-05 14:41:57 -07:00
teknium1 e985e34659 chore: add falkoro to AUTHOR_MAP 2026-07-05 14:41:40 -07:00
Teknium 55e3ee1ab8
fix: remove dead f-string prefixes via ruff F541 (216 sites) (#52336)
ruff check --fix --select F541 . on current main. Pure prefix removals;
adjacent-string concatenations keep the f only on interpolating fragments.
No string content or live placeholder altered.
2026-07-05 13:42:46 -07:00
teknium1 b3b1e58ad6 fix(codex): stream commentary deltas through the reasoning channel
Follow-up to the salvaged #58696 (devatnull) + #41343 (annguyenNous)
commits: instead of fully suppressing commentary/analysis-phase stream
deltas, fire on_reasoning_delta so the CLI/gateway display them like
thinking text. Matches Codex CLI semantics where commentary is never
the turn's final answer, while keeping the narration visible in the
reasoning display. Adds devatnull to AUTHOR_MAP.
2026-07-05 06:29:45 -07:00
teknium1 372c0b5f45 chore: add devatnull to AUTHOR_MAP for PR #58700 salvage 2026-07-05 06:29:26 -07:00
teknium1 b0f2bdbe8b fix(whatsapp): gate poll-vote events to Hermes-created polls + salvage follow-ups
- bridge: only enqueue poll_update events for polls Hermes itself created
  (tracked via recentlySentIds when /send-poll returns) so arbitrary human
  polls in group chats don't inject agent-visible messages on every vote
- update test_already_whatsapp_italic for the new markdown-italic mapping
- AUTHOR_MAP entry for @devatnull (PR #58704 salvage)
2026-07-05 06:27:20 -07:00
teknium1 0ca2a927cf chore: add devatnull to AUTHOR_MAP for PR #58697 salvage 2026-07-05 06:12:49 -07:00
teknium1 95fc3c6b45 chore: add alastraz to AUTHOR_MAP for PR #41383 salvage 2026-07-05 05:36:31 -07:00
teknium1 dec4485d2f chore(release): AUTHOR_MAP entries for salvaged PR authors 2026-07-05 00:59:35 -07:00
teknium1 e4da3a7a52 chore(release): AUTHOR_MAP entry for salvaged PR author 2026-07-05 00:55:51 -07:00
teknium1 020a71678c chore(release): AUTHOR_MAP entries for salvaged PR authors 2026-07-05 00:48:50 -07:00
Teknium 6f052b7ff1
fix(copilot): set x-initiator per turn so user prompts bill as premium requests (salvage #4097) (#58544)
* fix(cli): set correct x-initiator header per Copilot turn

copilot_default_headers() always hardcoded x-initiator: agent, but
GitHub Copilot billing requires "user" for user-initiated prompts and
"agent" for tool/follow-up calls. This caused premium requests to never
be consumed correctly, risking billing issues or account bans.

Adds is_agent_turn param to copilot_default_headers() and injects
extra_headers={"x-initiator": "user"} on the first API call of each
user turn when targeting Copilot URLs. The flag flips to False after
injection so subsequent calls (tool use, streaming fallback) default
back to "agent".

Fixes #3040

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(release): add AUTHOR_MAP entry for @tjp2021 (PR #4097 salvage)

---------

Co-authored-by: Tim <tim@iteachyouai.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-05 00:44:15 -07:00
teknium1 d810ff2f2b chore(release): AUTHOR_MAP entries for salvaged PR authors 2026-07-05 00:41:34 -07:00
teknium1 10f7cb043c chore(release): AUTHOR_MAP entries for salvaged PR authors 2026-07-04 15:44:50 -07:00
teknium1 4ac8c7546b fix(vision): mkdir converted-PNG output dir; wire SVG pass-through to rasterizer; AUTHOR_MAP for #52688
- _normalize_to_supported_image: ensure cache/vision exists before writing
  the converted PNG (fresh HERMES_HOME had no dir -> FileNotFoundError).
- resolver: SVG passes through as image/svg+xml instead of erroring; the
  call sites rasterize to PNG via the salvaged normalize step (cairosvg /
  svglib / rsvg-convert / inkscape, best-effort with actionable error).
- normalization offloaded via asyncio.to_thread at both call sites.
- tests: resolver pass-through + rasterization + no-converter error paths.
- AUTHOR_MAP: jonathan@mintrx.com -> JAlmanzarMint (PR #52688 salvage).
2026-07-04 15:30:50 -07:00
teknium1 6fcd470d54 chore(release): AUTHOR_MAP entries for salvaged PR authors 2026-07-04 15:18:41 -07:00
Teknium e670d9cdd6
Merge pull request #58489 from NousResearch/revert-30179
Revert "feat(egress): iron-proxy credential-injection firewall" (#30179)
2026-07-04 13:41:24 -07:00
teknium1 f9c543d6d1 chore(release): add AUTHOR_MAP entry for @danilofalcao (PR #56674 salvage) 2026-07-04 13:40:59 -07:00
teknium1 c6dc7c03c3
Revert "Merge pull request #30179 from NousResearch/feat/iron-proxy"
This reverts commit 8790adc4c6, reversing
changes made to fe5054bccf.
2026-07-04 13:38:59 -07:00
Teknium 8790adc4c6
Merge pull request #30179 from NousResearch/feat/iron-proxy
feat(egress): iron-proxy credential-injection firewall for sandboxes
2026-07-04 13:29:23 -07:00
kshitijk4poor 60906be3fc chore: map yingwaizhiying@gmail.com -> msh01 in AUTHOR_MAP
Contributor of the salvaged PR #58276 fix commit. Required so the
contributor attribution CI check passes on the rebase-merge that
preserves their authorship.
2026-07-04 21:04:27 +05:30
kshitijk4poor a37fd66dec fix(telegram): shut down abandoned init app + AUTHOR_MAP + cover the deadline helper
Follow-up to @msh01's wall-deadline init-timeout fix.

- Resource leak: on timeout the initialize() task is abandoned without
  awaiting its (shielded, possibly-never-completing) cancellation, so the
  half-built PTB app's httpx client / connection pool was never closed —
  up to 8x across the retry ladder. Add an optional on_abandon cleanup to
  _await_with_thread_deadline that best-effort app.shutdown()s the abandoned
  app, run detached + exception-swallowed so it can never re-block or re-hang
  the ladder (mirrors _close_client_on_timeout in agent/auxiliary_client.py).
- Cover the helper itself: the salvaged test monkeypatched out the real
  _await_with_thread_deadline, so its abandonment/cleanup path was untested.
  Add direct tests for happy-path return, prompt-timeout-with-cleanup, and
  cleanup-error-swallowed; the wedged coroutines swallow cancellation for a
  bounded window (proving the helper returns before cancellation completes,
  the #58236 shielded-scope behavior) without leaving an immortal task that
  would wedge pytest teardown. Widen the salvaged stub to accept on_abandon.
- Attribution: add yingwaizhiying@gmail.com -> msh01 to AUTHOR_MAP (bare
  gmail does not auto-resolve the check-attribution gate).

Known follow-up (not addressed here): the retry ladder reuses the same
self._app across all 8 attempts; a fresh app per attempt would fully close
the coherence risk if an abandoned initialize() completes in the background.
That is a larger restructure of the ~130-line builder+handler setup, left
for a separate change.
2026-07-04 20:45:48 +05:30
teknium1 14cbbd541e
Merge remote-tracking branch 'origin/main' into iron-proxy-followups
# Conflicts:
#	hermes_cli/config.py
#	hermes_cli/main.py
#	website/docs/reference/cli-commands.md
2026-07-04 03:09:43 -07:00
teknium1 ca596e228c chore: map marxb@protonmail.com to Marxb85 in AUTHOR_MAP 2026-07-04 02:37:33 -07:00
kshitijk4poor 3c8c968d16 chore: map huanshan5195 in AUTHOR_MAP for #57601 salvage
check-attribution requires every contributor author email to be in
AUTHOR_MAP; the salvaged commit is authored by
huanshan5195 <huanshan5195@users.noreply.github.com>.
2026-07-04 14:19:44 +05:30
kshitijk4poor 8229d7765a chore: map lavya@loom.local -> LavyaTandel in AUTHOR_MAP
Salvage of PR #57893 (envelope-layout prompt-cache marker fix, #57845)
uses the contributor's local git identity lavya@loom.local, which is not
GitHub-resolvable. Add the mapping so contributor_audit passes when the
salvage PR lands.
2026-07-04 13:54:27 +05:30
kshitijk4poor a3ea73932a chore(release): map iacobs@webflakes.com -> m0n5t3r in AUTHOR_MAP
Second contributor identity from PR #28652 (issue #28651).
2026-07-03 20:14:27 +05:30
kshitijk4poor ad3261bc77 chore: add AUTHOR_MAP entry for PR #57692 salvage (CocaKova) 2026-07-03 18:38:12 +05:30
teknium1 0e9136cb27 chore: add suninrain086 to AUTHOR_MAP for salvaged #50685 2026-07-03 03:54:01 -07:00
kshitijk4poor 0950dae2fa Merge remote-tracking branch 'upstream/main' into HEAD
# Conflicts:
#	scripts/release.py
2026-07-03 03:52:15 +05:30
kshitijk4poor 26edfab004 chore: add trismegistus-wanderer to AUTHOR_MAP for PR #31856 salvage 2026-07-03 03:30:54 +05:30
kshitijk4poor eb806c7f50 chore(release): add infinitycrew39 to AUTHOR_MAP (#56431 salvage) 2026-07-03 03:27:32 +05:30
kshitijk4poor de67f430b2 chore: map gumclaw@gumroad.com in AUTHOR_MAP for PR #57322 salvage 2026-07-03 03:26:53 +05:30
Brooklyn Nicholson ab942330fc chore(release): map yingliang-zhang in AUTHOR_MAP for #57335 2026-07-02 15:44:37 -05:00
David Zhang 30e947e0a0 feat(gateway): persist per-session /model overrides across gateway restarts
Per-session /model overrides (_session_model_overrides) were in-memory only,
so a gateway restart silently reverted every session to the global default
model. Persist the non-secret parts (model/provider/base_url ONLY — never
api_key) into the session entry in sessions.json and lazily rehydrate them
on first use after a restart, re-resolving credentials through the normal
runtime provider resolution.

- gateway/session.py: SessionEntry.model_override field with
  sanitize_model_override() (allowlist: model/provider/base_url) applied on
  both serialization and deserialization; SessionStore.set_model_override /
  get_model_override accessors. reset_session() already creates a fresh entry,
  so /new keeps its clear-on-reset semantics — a restart cannot resurrect an
  override the user reset away.
- gateway/slash_commands.py: write-through at both /model set sites (text
  command + picker) after storing the in-memory override.
- gateway/run.py: _rehydrate_session_model_override() called from
  _resolve_session_agent_runtime(); in-memory state always wins, credentials
  are re-resolved per provider (credential-less fallback on failure). Session
  expiry finalization also drops the persisted override.
- tests/gateway/test_session_model_override_persistence.py: restart
  round-trip, /new clearing, api_key-never-serialized (including tampered
  sessions.json), rehydration + live-state precedence + credential-failure
  degradation.

Salvaged from #3659 by @Git-on-my-level, narrowed to the restart-persistence
gap confirmed in triage.
2026-07-02 05:51:12 -07:00
Jneeee b98baa3039 feat(config): extra HTTP headers for LLM API calls (#3526 salvage)
Named providers / custom_providers entries in config.yaml now accept an
extra_headers dict scoped to that endpoint — for reverse proxies, API
gateways, and custom auth schemes (e.g. Cloudflare Access service tokens).

- hermes_cli/config.py: normalize extra_headers on provider entries
  (_normalize_custom_provider_entry + providers-dict translation), add
  get_custom_provider_extra_headers /
  apply_custom_provider_extra_headers_to_client_kwargs helpers keyed on
  base_url (case/trailing-slash insensitive, no substring bypass —
  mirrors the TLS helpers)
- hermes_cli/runtime_provider.py: surface extra_headers in the resolved
  runtime for named custom providers (providers dict, legacy
  custom_providers list, and the credential-pool path)
- run_agent.py / agent/agent_init.py: merge per-provider extra_headers
  onto the OpenAI client default_headers at construction and on every
  _apply_client_headers_for_base_url re-application (credential swaps,
  rebuilds), most-specific level wins; OpenAI-wire only (native
  Anthropic/Bedrock scoped out)
- agent/auxiliary_client.py: accept model.extra_headers as an alias of
  model.default_headers for the global variant
- cli-config.yaml.example: documented commented example
- Header values are treated as secrets and never logged

Salvaged from PR #3526 by @jneeee, reimplemented against current main.

Co-authored-by: Teknium <127238744+teknium1@users.noreply.github.com>
2026-07-02 05:33:25 -07:00
Mibayy 4a09b692ec feat(api-server): per-client model routing via model_routes (#3176 salvage)
Adds a no-code routing layer to the OpenAI-compatible API server so one
Hermes deployment can map different API clients to different
model/provider backends. Clients pick a backend by sending a configured
alias as the OpenAI 'model' field; unmatched values fall back to the
global model. Configured aliases are listed by GET /v1/models.

Precedence (highest first): session /model override > model_routes
route > global config. Route provider credentials resolve through
_resolve_runtime_agent_kwargs_for_provider (same seam as
channel_overrides); per-route api_key/base_url are upstream provider
credential overrides — never caller auth, never logged.

Salvaged and rebased from PR #3176 by @Mibayy onto current main.
2026-07-02 05:23:28 -07:00
Mibayy ce9aa869fc feat(commands): /compact alias + --preview/--dry-run flags for /compress (#3243 salvage)
Salvaged from PR #3243 by @Mibayy, reimplemented against current main
(the original diff targeted a removed gateway/run.py handler).

- /compact is now a first-class alias of /compress (CLI, gateway,
  Telegram/Slack/Discord command lists, autocomplete) — also fixes the
  dangling '/compact' references in gateway error messages
  (gateway/run.py context-exhausted banners).
- --preview / --dry-run: report what WOULD be compressed (message
  counts, token estimate, 'here [N]' boundary) without touching the
  transcript. Flags coexist with the existing 'here [N]' / focus-topic
  args on both the CLI and gateway surfaces via shared pure helpers in
  hermes_cli/partial_compress.py.
- --aggressive (LLM-free hard truncation) is intentionally NOT
  implemented: it would need its own transcript-persistence branch
  outside the guarded _compress_context rotation machinery (#44794
  data-loss class). The flag is recognized and returns an explanatory
  message pointing at '/compress here [N]' and /undo instead of being
  mis-parsed as a focus topic.
- locales: gateway.compress.aggressive_unsupported added to all 16
  catalogs (parity test enforced).
- release.py: AUTHOR_MAP entry for contributor credit.
2026-07-02 05:10:31 -07:00
Morgan K 39bff67957 feat(gateway): add 'log' option to display.tool_progress
Salvage of #3459 by @keslerm, reimplemented against the restructured
progress-callback block in gateway/run.py (resolve_display_setting,
needs_progress_queue, thinking-relay). Duplicate PR #3458 by @dlkakbs was
submitted 4 minutes earlier with the same feature — both credited.

Co-authored-by: Dilee <uzmpsk.dilekakbas@gmail.com>

tool_progress: log keeps the chat silent and appends timestamped tool-call
lines to ~/.hermes/logs/tool_calls.log via a dedicated queue drained by an
async writer (RotatingFileHandler 5MB x 3, RedactingFormatter so secrets
never land on disk). Gateway-only by design; thinking_progress relaying and
the webhook gate are unaffected. /verbose now cycles
off -> new -> all -> verbose -> log.
2026-07-02 05:09:38 -07:00
Mibayy 070ac2a719 fix(status): label provider as custom when config.yaml model.base_url is set
Salvage of the surviving hunk of #3296 by @Mibayy. The PR's gateway
_handle_provider_command hunk targets code removed on main (/provider was
absorbed into /model + /status, which already read model.base_url); the
hermes status mislabel was the remaining live symptom:
_effective_provider_label() only checked the legacy OPENAI_BASE_URL env var,
so a custom endpoint configured canonically in config.yaml still displayed
as OpenRouter.
2026-07-02 04:59:02 -07:00
Teknium 44650a5ce3 chore: add AUTHOR_MAP entry for @ajmeese7 (#3219 salvage) 2026-07-02 04:48:02 -07:00
kshitijk4poor be21e06ab3 chore(release): map ai-lab@foxmail.com to CrazyBoyM
Adds the AUTHOR_MAP entry for CrazyBoyM (ai-lab@foxmail.com) so the
contributor-attribution CI check passes when PR #55828's commits are
rebase-merged with authorship preserved.
2026-07-02 16:55:02 +05:30
Tarun Ravikumar 2068754d6f feat(api-server): inline MEDIA: image tags as base64 data URLs for remote frontends
Salvage of the surviving piece of #2696 by @tarunravi. The PR's other two
changes (tool progress streaming, SSE None-sentinel fix) were independently
superseded on main by the structured hermes.tool.progress SSE events and the
rewritten queue-drain loop.

Remote OpenAI-compatible frontends can't read server-local file paths, so
MEDIA:<path> tags (browser screenshots, generated images) were dead text.
_resolve_media_to_data_urls() now inlines small (<=5MB) local images as
markdown data URLs across all four response surfaces: chat completions
(non-streaming), session chat, session chat stream final event, and the
Responses API. Non-image, missing, or oversized paths pass through
untouched.
2026-07-02 03:23:44 -07:00
CharmingGroot 88bd1c01e1 fix(email): harden adapter against malformed IMAP responses
Salvage of #2794 by @CharmingGroot, ported to the relocated
plugins/platforms/email/adapter.py:

- Guard raw_email = msg_data[0][1] against IndexError/TypeError and
  non-bytes payloads. UIDs are added to _seen_uids before fetch, so an
  exception mid-batch permanently skipped every remaining message in
  the batch — now the bad message is logged and skipped instead.
- Message-ID domain generation falls back to 'localhost' when
  EMAIL_ADDRESS lacks '@' (now via a shared _message_id_domain() helper
  covering all 3 send paths; the PR fixed 2 of 3).
2026-07-02 03:12:53 -07:00
crazywriter1 c43aa6301d feat(gateway): per-channel model and system prompt overrides (Fixes #1955)
- ChannelOverride + channel_overrides; session /model > channel > global
- Thread/parent lookup; YAML bridge for discord.channel_overrides
- Guard channel_overrides when config lacks platforms (test mocks)
- Add sampiyonyus@gmail.com to AUTHOR_MAP
2026-07-02 03:08:11 -07:00
Teknium 6546c58641 chore: add AUTHOR_MAP entry for @VolodymyrBg (#2861 salvage) 2026-07-02 03:00:17 -07:00
teknium1 8b1ad38ecb chore: add AUTHOR_MAP entry for @sahibzada-allahyar (#39227 salvage) 2026-07-02 01:58:19 -07:00
kshitijk4poor 71c0622122 chore(release): map kiljadn@gmail.com to designnotdrum for #56480 salvage
Attribution audit gate: the salvaged contributor commits carry
kiljadn@gmail.com (Nick Mason / @designnotdrum). Add the mapping so
contributor_audit.py resolves the author on this PR.
2026-07-02 13:25:25 +05:30
kshitijk4poor e2ffbf0cf4 chore(release): add AUTHOR_MAP entries for compression-routing salvage
Map the two contributor emails whose commits are cherry-picked into the
compression-routing-integrity salvage so scripts/contributor_audit.py
attributes them at release time:

- jvsantos.cunha@gmail.com -> plcunha (PR #55300)
- jakepresent1@gmail.com   -> jakepresent (PR #55721)

r266-tech (PR #50517) is already mapped.
2026-07-02 12:49:42 +05:30
Teknium 7c1a029553
chore: release v0.18.0 (2026.7.1) (#56611) 2026-07-01 13:07:40 -07:00
kshitijk4poor 148674e27c chore: add AUTHOR_MAP entry for zmlgit (PR #54872 salvage) 2026-07-01 22:42:58 +05:30
nankingjing 5eaccf5802 fix(gateway): queue interrupts during in-flight context compression
With the default busy_input_mode=interrupt, a burst of rapid gateway
messages arriving while context compression is in flight could interrupt
the current turn and start a fresh turn against the pre-rotation parent
session. Because compression is interrupt-immune (#23975), the still-
running compression later rotates the id out from under that new turn,
and if the new turn also grew past the compression threshold it started
its own uncancellable compression on the same stale parent — forking
multiple orphaned one-shot sibling continuations (#56391).

While a state.db compression lock is held for the session, demote
'interrupt' busy-input mode to 'queue' semantics (mirroring the subagent
protection in #30170), so the follow-up message waits for the in-flight
compression + its id rotation to land instead of racing a new turn
against the stale parent. Ack copy explains the compression demotion.

Fixes #56391.
2026-07-01 06:38:24 -07:00
kshitij 7b45a22ddf
Merge pull request #56382 from kshitijk4poor/chore/authormap-gromykoss-56372
chore: add Gromykoss to AUTHOR_MAP for PR #56372 salvage
2026-07-01 17:59:06 +05:30
Steve Lawton c73e74386b feat(vertex): add Google Vertex AI provider for Gemini (OAuth2)
Adds Vertex AI as a first-class provider for Gemini models via Vertex's
OpenAI-compatible endpoint. Vertex authenticates with short-lived OAuth2
access tokens (service-account JSON or ADC), not a static API key — the
missing piece behind the recurring requests (#13484, #12639, #56259).

- agent/vertex_adapter.py: OAuth2 token minting + refresh-on-expiry
  (5-min margin), ADC->service-account fallback, global vs regional
  endpoint URLs. Config precedence: env var > config.yaml > default.
- plugins/model-providers/vertex/: provider profile (auth_type=vertex),
  reuses Gemini's extra_body.google.thinking_config translation.
- runtime_provider: vertex short-circuit BEFORE the credential pool so a
  credentials-file path is never mistaken for a static API key; mints a
  fresh token + computes base_url per resolve.
- run_agent + conversation_loop: _try_refresh_vertex_client_credentials()
  re-mints the token and rebuilds the client on a mid-session 401, so a
  long-lived gateway agent survives token expiry (~1h).
- auxiliary_client: vertex auth_type branch for side-LLM tasks.
- config.yaml: vertex.project_id / vertex.region (non-secret, bridged to
  env); credential path stays in .env (VERTEX_CREDENTIALS_PATH).
- setup wizard + model picker: dedicated _model_flow_vertex; curated
  google/gemini-* model list; --provider choices.
- pricing/metadata: Vertex prices off the gemini docs snapshot; endpoint
  host auto-maps to the vertex provider (no probe spam).
- lazy_deps + pyproject [vertex] extra: google-auth, opt-in only.
- docs: guides/google-vertex.md + providers page; tests for adapter +
  runtime resolution.

Salvages and modernizes #8427 by @slawt onto current main: rewired from
the legacy PROVIDER_REGISTRY path to the provider-profile architecture,
moved non-secret config out of .env into config.yaml, and added the
per-turn 401 token-refresh the original lacked.
2026-07-01 05:25:33 -07:00
kshitijk4poor 0ebbfbcc84 chore: add Gromykoss to AUTHOR_MAP for PR #56372 salvage
Maps gromyko.ss83@gmail.com -> Gromykoss so the contributor-attribution
audit passes for the #56372 salvage (context_compressor END MARKER reorder).
2026-07-01 17:52:57 +05:30
teknium1 2f167a2b84 fix: comment accuracy + AUTHOR_MAP for salvaged PR #50204
- Correct the exit-75 comment: Hermes-generated units set
  StartLimitIntervalSec=0 (rate limiting disabled), so StartLimitBurst
  does not bound loops. The real bound is that genuine crashes exit
  non-zero-but-not-75, and RestartForceExitStatus=75 only whitelists
  the planned code.
- Add randomuser2026x AUTHOR_MAP entry (CI blocks unmapped emails).
2026-07-01 05:13:03 -07:00
Brett 9f03095044 fix(telegram): cap initialize() with per-attempt timeout so unreachable fallback IPs can't hang startup
Wrap each Telegram initialize() attempt in asyncio.wait_for(HERMES_TELEGRAM_INIT_TIMEOUT,
default 30s). When api.telegram.org and all fallback IPs are unreachable, the connect
chain has no outer bound, so a single initialize() blocks for minutes and the
retry-on-exception loop never fires — the gateway appears to hang after the banner.
The timeout guarantees each attempt is bounded, then retries with backoff, then fails
with an actionable error. Also adds WARNING-level progress logs before DoH discovery
and each connect attempt (visible at default log level).

Salvaged onto plugins/platforms/telegram/adapter.py (Telegram moved from
gateway/platforms/ since the PR was opened). Adds env var to docs + AUTHOR_MAP.

Co-authored-by: Hermes Agent <127238744+teknium1@users.noreply.github.com>
2026-07-01 05:07:10 -07:00
teknium1 050e602de3 chore(release): map HODLCLONE author for PR #49351 salvage 2026-07-01 05:06:00 -07:00
teknium1 cfbc7ed1f9 fix(browser): narrow credential-query denylist to unambiguous names
Follow-up on the salvaged #49830 hardening. The contributor's sensitive
query-param set included bare English words (code, key, auth, session,
sig) that double as ordinary page facets — ?code= on promo/challenge
pages, ?key= as a search facet, ?session= on blogs — so web_extract and
cloud browser_navigate would refuse a large slice of normal browsing.

Narrow the set to unambiguously credential-named params (access_token,
authorization, client_secret, password, token, x-amz-signature, ...).
Prefix-based vendor-key redaction (is_safe_url) still catches recognizable
key shapes; this set is the belt-and-suspenders for opaque secrets carried
under an explicit credential-named parameter.

Also fixes two intra-PR-staleness test breakages surfaced by salvaging onto
current main:
- web_extract_tool() no longer accepts use_llm_processing= (signature
  changed since the PR was authored) — dropped the invalid kwarg.
- agent.redact now fully masks keyed 'token=<secret>' to 'token=***'
  instead of partial 'sk-...'; the console-redaction test now asserts the
  real invariant (secret body gone) rather than the exact mask format.

Added a regression test that generic English-word query params are NOT
blocked by the credential guard.
2026-07-01 05:04:41 -07:00
teknium1 3b41df6d46 test(gateway): regression for multi-profile node symlink leak; AUTHOR_MAP
Add tmp_path symlink regression tests for both generate_systemd_unit and
generate_launchd_plist (~/.local/bin/node -> profile node install must not
leak the profile target into the generated unit PATH). Register
jearnest11's AUTHOR_MAP entry for the salvage cherry-pick.
2026-07-01 04:57:21 -07:00
teknium1 55c8b2c81f chore(release): add AUTHOR_MAP entry for udatny (#29433 salvage) 2026-07-01 04:55:15 -07:00
Zeheng Huang 4c2c54c78c fix(matrix): await inbound sync handlers
Register the Matrix room-message, reaction, and invite handlers with
mautrix's wait_sync=True. mautrix's handle_sync() only returns the tasks
for handlers registered as sync-awaited; non-waited handlers are
fire-and-forget via background_task.create() and are NOT returned. Since
_dispatch_sync() awaits only the returned tasks (await asyncio.gather),
the inbound handlers previously had no completion point, so Tuwunel/
mautrix homeservers connected and completed initial sync but dispatched
zero inbound messages.

Fixes #46142.

Co-authored-by: Zeheng Huang <153708448+hunjaiboy@users.noreply.github.com>
2026-07-01 04:42:33 -07:00
kshitij d4e8c358c0
Merge pull request #56330 from kshitijk4poor/chore/authormap-valenteff
chore: add AUTHOR_MAP entry for valenteff (#53277 salvage)
2026-07-01 16:49:16 +05:30
shawchanshek 3b739b990b fix(title_generator): strip think blocks from LLM output before extracting title
Think-enabled models (MiniMax M2.7, DeepSeek, etc.) emit inline
<think>...</think> reasoning even for simple prompts like title
generation, and the raw XML was leaking into session titles. Route the
title-model response through the canonical strip_think_blocks scrubber
before cleanup so every tag variant — closed pairs, unterminated blocks,
orphan closes, mixed case — is handled, not just a single literal
<think> pair.

- 2 regression tests: closed <think> pair stripped, unterminated block
  at start yields no title.

Salvaged from PR #44126 by @shawchanshek.
2026-07-01 04:18:48 -07:00
kshitijk4poor 9c870548e3 chore: add AUTHOR_MAP entry for valenteff (#53277 salvage) 2026-07-01 16:44:07 +05:30
kshitijk4poor b3f55c2037 chore: add AUTHOR_MAP entries for session-persistence salvage batch
Maps the two plain-email contributors whose PRs are being salvaged so
contributor_audit.py passes:
- info@djimit.nl -> djimit (PR #48034)
- lubos@komfi.health -> lubosxyz (PR #49225)

The other two PRs in the batch (#50405 sasquatch9818, #48764 srojk34)
use users.noreply.github.com emails, which check-attribution auto-skips.
2026-07-01 16:38:56 +05:30
kshitij 8415c4703a
Merge pull request #56317 from kshitijk4poor/chore/authormap-bitcryptic
chore: add AUTHOR_MAP entry for bitcryptic-gw (#53997 salvage)
2026-07-01 16:33:47 +05:30
Tao Chen d3c8667462 fix(slack): authorize bot/workflow senders before the no-user-id guard
Slack Workflow Builder posts (and other app/bot messages) arrive as
subtype=bot_message with user=None. _is_user_authorized rejected them at
the `if not user_id: return False` guard, which runs *before* the #4466
{PLATFORM}_ALLOW_BOTS bypass — so @mentioning the bot from a Slack
workflow silently did nothing, even with SLACK_ALLOW_BOTS (or
SLACK_ALLOW_ALL_USERS) set. The chat-scoped allowlist for Telegram/QQ
already runs before that guard for the same reason (channel broadcasts
with no from_user); Slack was both missing from the bot-bypass map and
had the bypass running too late.

- gateway/authz_mixin: move the {PLATFORM}_ALLOW_BOTS bypass ahead of the
  no-user-id guard and add Platform.SLACK -> SLACK_ALLOW_BOTS.
- plugins/platforms/slack/adapter: set is_bot=True on inbound
  bot_message events so the gateway can identify workflow/app senders
  (they carry no user_id to match against the allowlist).

Tested: new tests/gateway/test_slack_bot_auth_bypass.py plus the existing
Discord/Feishu bot-auth and gateway authz/gating suites all pass.
2026-07-01 16:32:32 +05:30
kshitijk4poor fcbf850f33 chore: add AUTHOR_MAP entry for bitcryptic-gw (#53997 salvage) 2026-07-01 16:28:15 +05:30
teknium1 27347b2239 fix(gateway): align resume safety-net note with canonical recovery wording
Follow-up on the salvaged resume_pending fix: the empty-turn safety net
now emits the same reason-aware recovery note as the _is_resume_pending
branch (reason phrase + 'session restored' guidance + no-re-execute
instruction) instead of a second, differently-worded note. Also adds the
AUTHOR_MAP entry for the salvaged commit.
2026-07-01 03:57:44 -07:00
teknium1 49a87bcd1e chore(release): map SahilRakhaiya05 contributor email for #44073 salvage 2026-07-01 03:56:28 -07:00
Swissly 242c9639a8 fix(cron): prevent multi-target delivery loop crash on per-target failure
The standalone thread-pool fallback in _deliver_result() runs inside the
`except RuntimeError:` block (taken when asyncio.run() sees a running loop).
When future.result() raised there (SMTP ConnectionError, timeout, etc.), the
exception was NOT caught by the sibling `except Exception:` — it escaped
_deliver_result() and crashed the whole delivery loop, silently skipping every
remaining target. Multi-target delivery (e.g. deliver: 'email:a,email:b') is a
documented feature, so this broke a promised contract.

Wrap the fallback in its own try/except so a per-target failure is logged with
exc_info and the loop continues to the next target.

Fixes #47163
2026-07-01 03:48:37 -07:00
teknium1 5c2dccd06f chore(release): map kangsoo-bit author for PR #47508 salvage 2026-07-01 03:42:32 -07:00
teknium1 9dd6451c80 chore(release): add WXBR to AUTHOR_MAP for #46183 salvage 2026-07-01 03:34:49 -07:00
YLChen-007 e23f723389 fix: make streaming reasoning-tag filter case-insensitive
The streaming think-tag suppressors in cli.py (_stream_delta) and
gateway/stream_consumer.py (_filter_and_accumulate) matched tag names
with case-sensitive str.find(), so only the exact-case literals in the
tag tuples were caught. Mixed-case variants a model may emit — <Think>,
<ThInK>, <REASONING>, <Thought> — slipped through and leaked raw
reasoning into the user-visible stream.

Match against a lowercased view of the buffer with lowercased tag names
at all three sites (open-tag boundary search, partial-tag hold-back,
close-tag search) in both paths. Only KNOWN tag names are matched — no
substring matching — and the block-boundary gating that protects prose
mentions of <think> is preserved.

- 6 parametrized case-insensitive regression tests in each of
  tests/gateway/test_stream_consumer.py and
  tests/cli/test_stream_delta_think_tag.py.

Salvaged from PR #27289 by @YLChen-007.
2026-07-01 03:25:02 -07:00
teknium1 1c350728ec chore(release): map Lazymonter into AUTHOR_MAP for PR #42914 salvage 2026-07-01 03:21:20 -07:00
Teknium 2b8adb8683 chore(release): map tgmerritt author for PR #43553 salvage 2026-07-01 03:17:48 -07:00
testingbuddies24 e07768a53f fix(gateway): strip orphan think-tag close tags in progressive stream
When a model emits an inline <think>...</think> block but the opening
tag is dropped upstream (thinking-mode toggle, truncated stream, or
incomplete upstream filtering), the bare </think> close tag leaked
through to the user in the live progressive edit. The agent-side final
scrubber (agent/think_scrubber.py) already had _strip_orphan_close_tags;
this ports the same logic into GatewayStreamConsumer so the streaming
display stays clean too.

- _filter_and_accumulate: strip orphan close tags before appending the
  'no-opening-tag' branch text to _accumulated.
- _flush_think_buffer: same on stream end for held-back partials.
- 14 regression tests (TestStripOrphanCloseTags): all 6 close-tag
  variants, multi-tag, partial-tag-untouched, trailing whitespace,
  and end-to-end through _filter_and_accumulate / _flush_think_buffer.

Only strips KNOWN close-tag names (case-insensitive) — never arbitrary
tag-shaped substrings — so comparison operators and unrelated prose are
preserved.

Salvaged from PR #43192 by @testingbuddies24.
2026-07-01 03:04:01 -07:00
Wing Huang 828f33e6b1 fix(ci): map contributor email for attribution check
scripts/release.py AUTHOR_MAP is greped by the Contributor Attribution
Check to resolve a commit author's email -> GitHub username. Add
huangsen365@gmail.com -> huangsen365 so this PR's commits pass the check.

(This commit originally also carried a gateway race-test flake fix; that
edit is now dropped because main independently hardened the same test with
a superior server._sessions snapshot/restore isolation, making ours
redundant.)
2026-07-01 02:51:45 -07:00
teknium1 d15a288812 chore(release): map arthurzhang author for PR #34718 salvage 2026-07-01 02:45:22 -07:00
mrparker0980 10a54ccc2c fix(security): anchor @file context refs to canonical read deny-list
`@file` / `@folder` context-reference expansion enforced its own narrow
deny-list (`_ensure_reference_path_allowed` in `agent/context_references.py`)
that only covered `~/.ssh` keys, a handful of shell dotfiles, `~/.hermes/.env`,
and `skills/.hub`. It never blocked the credential stores that the canonical
read guard (`agent/file_safety.get_read_block_error`) protects: provider API
keys (`~/.hermes/auth.json`), Anthropic OAuth tokens
(`~/.hermes/.anthropic_oauth.json`), MCP OAuth material (`~/.hermes/mcp-tokens/`),
webhook HMAC secrets, and project-local `.env` files.

This matters because the messaging gateway feeds **untrusted** remote text
straight into reference expansion: `gateway/run.py` calls
`preprocess_context_references_async(..., allowed_root=_msg_cwd)` where
`_msg_cwd` defaults to the operator's HOME when `TERMINAL_CWD` is unset. A chat
peer (Telegram/Discord/Slack/...) could send `@file:~/.hermes/auth.json`, pass
the `allowed_root` check (it resolves under HOME), slip past the narrow list,
and have the operator's live keys read into the agent's context — where the
model would typically echo or act on them.

Rather than duplicate and re-sync a second secret list, this routes the guard
through the existing single source of truth. A reviewer might ask "why not just
add `auth.json` to the local list?" — because the local list has already drifted
once (a prior commit had to add `.config/gh`); anchoring to
`get_read_block_error` means every future addition there protects this path too.
The narrow checks are kept as a fallback since they also cover dirs that guard
does not (`.aws`, `.gnupg`, `.kube`, etc.), and the canonical lookup is wrapped
so it can never crash reference expansion.

N/A

- [x] 🔒 Security fix

- `agent/context_references.py`: `_ensure_reference_path_allowed` now also
  consults `agent.file_safety.get_read_block_error` after its existing checks
  and refuses the reference when that canonical guard flags the resolved path.
  The lookup is wrapped so guard-resolution failures fall back to the explicit
  checks instead of breaking expansion.
- `tests/agent/test_context_references.py`: added
  `test_blocks_canonical_read_denylist_credential_stores`, asserting that
  `@file` attaches for `auth.json`, `.anthropic_oauth.json`, `mcp-tokens/*`, and
  a project-local `.env` are all refused and their secret bodies never reach the
  expanded message.
- `scripts/release.py`: added the contributor email to `AUTHOR_MAP` (release
  gate).

1. `scripts/run_tests.sh tests/agent/test_context_references.py` — all 15 tests
   pass, including the new credential-store case.
2. Regression proof: stash `agent/context_references.py`, run the suite with
   `-- -k canonical`, and confirm the new test fails (secrets leak into the
   message) without the fix; restore and confirm it passes.
3. `ruff check agent/context_references.py tests/agent/test_context_references.py`
   and `python scripts/check-windows-footguns.py agent/context_references.py
   tests/agent/test_context_references.py` both pass.

- [x] I've read the Contributing Guide
- [x] My commit messages follow Conventional Commits (`fix(scope):`, etc.)
- [x] I searched for existing PRs to make sure this isn't a duplicate
- [x] My PR contains **only** changes related to this fix (plus the AUTHOR_MAP release gate)
- [x] I've run the test suite for the touched area and all tests pass
- [x] I've added tests for my changes (required for bug fixes)
- [x] I've tested on my platform: macOS 15 (Darwin 25.5)

- [x] I've updated relevant documentation (README, `docs/`, docstrings) — or N/A
- [x] I've updated `cli-config.yaml.example` if I added/changed config keys — or N/A
- [x] I've updated `CONTRIBUTING.md` or `AGENTS.md` if I changed architecture or workflows — or N/A
- [x] I've considered cross-platform impact (Windows, macOS) — or N/A
- [x] I've updated tool descriptions/schemas if I changed tool behavior — or N/A
2026-07-01 02:43:49 -07:00
kshitijk4poor 8f1d22d7ed chore(release): map r266-tech contributor noreply email for #55780 salvage 2026-07-01 15:01:33 +05:30
teknium1 116a63d3a0 chore(release): map jcjc81 + Tranquil-Flow in AUTHOR_MAP for #54947 cluster salvage 2026-07-01 02:29:24 -07:00
Teknium 522a5e93b2 chore(release): map x9x9x9x9x9x91 for #49247 salvage 2026-07-01 02:18:56 -07:00
itenev f981d47cb0 fix(gateway): prevent Discord disconnects from blocking event loop
models_dev.py's fetch uses a synchronous requests.get(timeout=15). Called
from the async gateway message handlers, it blocked the event loop for up
to 15s, starving Discord heartbeats and causing ClientConnectionResetError
disconnects.

Adds get_model_context_length_async() which offloads the entire sync
resolution chain to a worker thread via asyncio.to_thread(), and switches
the two async gateway call sites (_prepare_inbound_message_text,
_handle_message_with_agent) to await it. The loop stays responsive; the
sync path remains the single source of truth for the cache.

Salvaged from PR #22753 by @itenev. Follow-up: dropped the unused
fetch_models_dev_async/lookup_models_dev_context_async aiohttp variants
from the original PR (dead code with zero callers that had drifted from
the sync cache logic) — the to_thread wrapper already runs the sync path
off-loop, so they were redundant.
2026-07-01 02:17:35 -07:00
Teknium ea533e7f41 chore(release): map justin-cyhuang contributor email for #31960 salvage 2026-07-01 02:12:25 -07:00
Teknium 259e6b87a7 fix(teams-pipeline): reject dot-only recording display_name
Path(raw).name reduces '..'/'.'/'' to themselves, so basename
extraction alone still let a Graph-provided display_name of '..' or
'../' escape the temp recording directory (tmp_dir / '..' resolves to
the parent). Reject the dot-only basenames explicitly and fall back to
the artifact id. Extends @outsourc-e's regression coverage with the
dot-only cases.
2026-07-01 02:03:48 -07:00
teknium1 6d30f8c0ab chore: add AUTHOR_MAP entry for PR #52534 salvage (@qWaitCrypto) 2026-07-01 02:03:40 -07:00
teknium1 49cb06c07a chore(release): map sasquatch9818 for PR #41198 salvage 2026-07-01 01:54:45 -07:00