Commit graph

1496 commits

Author SHA1 Message Date
Teknium
088b989442
fix(gateway): scope reset banners' session info to the serving profile (#59048 salvage) (#59329)
* fix(gateway): scope reset banners' session info to the serving profile

The auto-reset notice and the manual /reset //new banner both appended
_format_session_info() outside any profile scope, so a multiplexed
gateway advertised the base config's model/provider/context while the
session actually ran on the profile's.

Route both call sites through a new _reset_notice_session_info(source),
which enters _profile_runtime_scope for the source's profile when
gateway.multiplex_profiles is on (mirroring _run_agent's gating), so
_load_gateway_config()/_resolve_gateway_model() resolve the profile's
config.yaml via the existing context-local home override. Single-profile
gateways never enter the scope — behavior unchanged.

Both call sites invoke the helper via asyncio.to_thread: under the
scope, resolution can do blocking work (credential refresh,
context-length HTTP probes) that previously failed fast unscoped and
must not run on the event loop.

Fixes #59003

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(release): map irresi author email for PR #59048 salvage

---------

Co-authored-by: irresi <blueirobin02@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:25:45 +10:00
Ben Barclay
536ffedbf4
feat(docker): re-seed a terminally-dead Nous bootstrap session on boot (#59983)
The stage2-hook auth.json seed is first-boot-only ([ ! -f auth.json ]) to avoid
clobbering rotated refresh tokens on restart. That guard means a container whose
Nous bootstrap session took a terminal invalid_grant (tokens cleared,
providers.nous.last_auth_error.relogin_required stamped) cannot recover from a
restart — it stays unauthenticated until the credential is replaced.

Add a self-heal path: an orchestrator that manages the container supplies a
freshly-issued session via HERMES_AUTH_JSON_REBOOTSTRAP (distinct from the
create-only *_BOOTSTRAP var). On boot, scripts/docker_rebootstrap_nous_session.py
swaps ONLY the providers.nous entry, and ONLY when the on-disk entry is provably
terminal (quarantine marker + no usable tokens). Healthy/rotating/absent/
unparseable auth.json is always a no-op, so the env is safe to leave set across
restarts and never clobbers a good token. Pure stdlib, runs as its own
subprocess, always exits 0 so a re-seed error never fails the boot.

Reuses the same terminal predicate as get_nous_session_validity() so we re-seed
only a session that is genuinely dead.
2026-07-07 06:57:23 +00:00
kshitijk4poor
ef599aa7f0 chore: map spiky02plateau in AUTHOR_MAP for #32824 salvage
The salvaged commits from #32824 use a bare
spiky02plateau@users.noreply.github.com (no numeric-id + prefix), which the
contributor-check.yml gate does not auto-resolve (it only skips the
<id>+<user>@users.noreply form). Add the explicit mapping so attribution CI
passes and release notes credit @spiky02plateau.
2026-07-07 12:01:33 +05:30
teknium1
8f80a982a6 chore: add fanyangCS to AUTHOR_MAP 2026-07-06 18:12:24 -07:00
teknium1
dd7198e71d chore: add tanmayxchoudhary to AUTHOR_MAP 2026-07-06 12:46:20 -07:00
Teknium
5e685999af
fix(ci): make the CI timing report unflakeable (#59818)
The 'CI timing report' job is pure observability — it collects per-job/step
durations from the GitHub API after the run and publishes an HTML gantt
report + PR-vs-main timing diff. It gates nothing (all-checks-pass does not
include it), yet it could redden a PR: the script makes dozens of paginated
API calls with the shared repo GITHUB_TOKEN and had zero retry handling, so
a single 403 (rate-limit burst when several PRs run CI concurrently) failed
the job. Observed twice in a row on PR #59805.

- api_get(): retry 403/429/5xx and connection errors with exponential
  backoff, honoring Retry-After / X-RateLimit-Reset (max 5 attempts, 120s
  cap). Non-transient statuses (404 etc.) still fail fast.
- main(): exhausted retries raise TimingsUnavailable, caught to emit a
  degraded summary line + placeholder HTML artifact and exit 0 — a metrics
  collector must never fail the PR's checks. No timings JSON is written on
  the degraded path so an empty baseline can never be cached.
- ci.yml: baseline-save steps on main skip gracefully when no JSON exists.

Verified with a mocked urlopen harness: retry-then-success (3 attempts),
exhausted-retries -> TimingsUnavailable, 404 fails fast without retry,
degraded main() exits 0 with summary + placeholder and no JSON, and the
--from-json happy path is unchanged.
2026-07-06 12:44:07 -07:00
teknium1
8cc1ca4ce2 chore: add bigstar0920 to AUTHOR_MAP 2026-07-06 12:43:59 -07:00
Hermes Agent
7426c09bee chore: map hellno in AUTHOR_MAP for #49033 salvage
Some checks are pending
CI / Detect affected areas (push) Waiting to run
CI / Python tests (push) Blocked by required conditions
CI / Python lints (push) Blocked by required conditions
CI / TypeScript (push) Blocked by required conditions
CI / Docs Site (push) Blocked by required conditions
CI / Deny unrelated histories (push) Blocked by required conditions
CI / Check contributors (push) Blocked by required conditions
CI / Check uv.lock (push) Blocked by required conditions
CI / Lint Docker scripts (push) Blocked by required conditions
CI / Build&Test Docker image (push) Blocked by required conditions
CI / Supply-chain scan (push) Blocked by required conditions
CI / OSV scan (push) Waiting to run
CI / All required checks pass (push) Blocked by required conditions
CI / CI timing report (push) Blocked by required conditions
Deploy Site / deploy-vercel (push) Waiting to run
Deploy Site / deploy-docs (push) Waiting to run
2026-07-06 04:58:42 -07:00
teknium1
8235f484c9 feat(secrets): adapt 1Password onto the SecretSource interface
Follow-up on the cherry-picked #36896 commits, wiring 1Password into
the new registry as the reference *mapped* source:

- OnePasswordSource adapter (shape=mapped, scheme=op): fetch-only —
  precedence, override semantics, conflict warnings, and env writes
  move to the orchestrator; apply_onepassword_secrets kept as legacy
  shim like Bitwarden's.
- Registered in _ensure_builtin_sources; mapped op:// bindings now
  outrank bulk Bitwarden project dumps on contested vars.
- _cache.py FetchResult/is_valid_env_name re-exported from base so
  there is exactly one canonical definition; bitwarden.py re-adapted
  onto the contributor's DiskCache substrate.
- ErrorKind classification for op failures (auth/binary/empty/network).
- Registry + conformance coverage for OnePasswordSource, incl. the
  headline multi-source test: both vaults claim the same var, mapped
  1Password wins, conflict surfaced, provenance correct.
- env_loader tests migrated off the legacy apply_* mocks onto the
  fetch layer; AUTHOR_MAP entry for @hwrdprkns.
2026-07-06 04:58:07 -07:00
Hermes Agent
1fcf6e58b6 chore: map waseemshahwan in AUTHOR_MAP for #56841 salvage 2026-07-06 03:53:52 -07:00
Hermes Agent
09466971ff chore: map AIalliAI id-form noreply email in AUTHOR_MAP for #44222 salvage 2026-07-06 03:42:14 -07:00
teknium1
4df2536f21 chore: map Ahmett101 noreply email in AUTHOR_MAP for #59455 2026-07-06 03:28:30 -07:00
teknium1
8f849ea365 chore: credit isheng-eqi for #59446 in AUTHOR_MAP 2026-07-06 03:17:21 -07:00
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
konsisumer
18e840469f fix(install): guard Windows desktop installs against broken web_server 2026-07-05 19:35:30 -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
Teknium
6fad6f1dd8
fix(whatsapp): contain and surface inbound media download failures (port nanoclaw#2895) (#59261)
Port from nanocoai/nanoclaw#2895's never-silently-drop guarantee.

Before: saveMedia() in scripts/whatsapp-bridge/bridge_helpers.js awaited
downloadMedia() with no try/catch. A failed CDN fetch (expired media URL,
transient network error — Baileys throws 'Failed to fetch stream from
https://mmg.whatsapp.net/...') rejected out of extractBridgeEvent, which
bridge.js awaits inside its messages.upsert for-loop with no per-message
guard — dropping the failed message AND every remaining message in the
same upsert batch, silently.

After:
- saveMedia catches download/write failures, records the media type, and
  logs a console.warn instead of rejecting.
- appendMediaFailureNote() (exported pure helper, mirroring the file's
  testable-helper convention) surfaces '[<type> could not be downloaded]'
  in the event body, so the agent learns media was sent rather than the
  attachment vanishing. Applied before the '[<type> received]' fallback
  so an uncaptioned failed image reads as a failure, not an arrival.

The reuploadRequest recovery half of nanoclaw#2895 is already wired in
bridge.js (downloadMediaMessage(..., { reuploadRequest:
sock.updateMediaMessage })); this ports the containment half hermes was
missing.

Tests: 3 new cases in bridge.native.test.mjs (note formatting, uncaptioned
failure containment, captioned failure note). All 5 bridge test files pass.
2026-07-05 17:21:28 -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
devatnull
11627fdcb9 feat(whatsapp): native Baileys polls, clarify-as-poll, locations, and rich inbound metadata
Salvaged from PR #58704 by @devatnull, scoped to the WhatsApp surface:
- bridge_helpers.js: pure, tested extraction of inbound Baileys message
  parsing (quoted text, MIME/filename, PTT vs audio, stickers, contacts,
  reactions, polls, locations, GIF playback metadata)
- native poll primitive: /send-poll endpoint, poll messageSecret caching,
  encrypted vote decryption + aggregation via Baileys
- send_clarify() renders multi-choice clarify prompts as native polls;
  votes flow back through the existing clarify text-intercept
- send_location() + /send-location for native WhatsApp location pins
- structured quoted-reply context (fixes duplicated '[Replying to: ...]'
  rendered both by the adapter and gateway/run.py)
- outbound formatting: markdown *italic* -> WhatsApp _italic_, invisible
  unicode sanitization; execSync -> execFileSync hardening; GIF -> mp4
  gifPlayback conversion with truthful image/gif fallback

Out of scope (deliberately not salvaged from #58704): cross-platform
ordered-delivery machinery in gateway/platforms/base.py, LOCATION: and
hermes:poll response-text directives (no prompt wiring exists yet), and
the unconditional WhatsApp reply-anchor suppression.
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
Some checks are pending
CI / Detect affected areas (push) Waiting to run
CI / Python tests (push) Blocked by required conditions
CI / Python lints (push) Blocked by required conditions
CI / TypeScript (push) Blocked by required conditions
CI / Docs Site (push) Blocked by required conditions
CI / Deny unrelated histories (push) Blocked by required conditions
CI / Check contributors (push) Blocked by required conditions
CI / Check uv.lock (push) Blocked by required conditions
CI / Lint Docker scripts (push) Blocked by required conditions
CI / Build&Test Docker image (push) Blocked by required conditions
CI / Supply-chain scan (push) Blocked by required conditions
CI / OSV scan (push) Waiting to run
CI / All required checks pass (push) Blocked by required conditions
CI / CI timing report (push) Blocked by required conditions
Deploy Site / deploy-vercel (push) Waiting to run
Deploy Site / deploy-docs (push) Waiting to run
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