Files
hermes-agent/contributors
69k4xmdfm2-blip 21260c3281 fix(gateway): carry the profile in adapter-derived session keys (#88404)
Adapter ingress derives a session key BEFORE the runner stamps
source.profile in _make_profile_message_handler, so the namespace fell
back to the active profile and every bot in a multiplexed gateway
produced agent:main:<platform>:<chat>. A Telegram private chat reports
the user's own id as chat.id, identical for every bot, so two profiles
sharing one human collapsed onto a single lane: _pending_text_batches,
_active_sessions, the busy-session guard and _post_delivery_callbacks are
all keyed on that string. A day of production logs across two bots shows
60 flushes, none carrying the secondary profile's namespace.

set_owner_profile records credential ownership on the adapter and
_session_key_profile resolves the namespace as source.profile ->
_owner_profile -> the session store's resolver, so a secondary adapter
keys into its own namespace even before the source is stamped. Stamped
sources keep priority, so relay/connector ingress, which routes per event
rather than per credential, is unchanged. _configure_profile_adapter
installs the owner alongside the other handlers, covering startup and
reconnect.

Every candidate is type-checked as a non-blank str, and every attribute
read goes through getattr: adapters are routinely built without
BasePlatformAdapter.__init__, and a duck-typed session store returns a
truthy non-string that would otherwise be interpolated into the key as
agent:<MagicMock ...>:.

Also routes the four call sites that passed no profile at all (feishu
media batches, raft, slack _session_key_for_source, telegram photo
batches) through the same resolver.

test_multiplex_busy_input_mode's secondary-adapter busy case seeded
_active_sessions with the unstamped agent:main: key, asserting the
pre-fix collapse. It now seeds the lane the profile-owned adapter
actually derives.

A primary adapter has no owner and an unstamped source, so it resolves
exactly as before; with multiplex_profiles off the resolver returns None
and every key is byte-identical to today's.
2026-08-19 02:00:16 -07:00
..

Contributor email → GitHub login mappings

This directory replaces appending entries to AUTHOR_MAP in scripts/release.py. The old dict caused constant merge conflicts when several salvage PRs landed at once — every PR edited the same lines of the same file. Here, each mapping is its own file, and file additions never conflict.

Adding a mapping

One file per commit-author email, under emails/:

python3 scripts/add_contributor.py <email> <github-login>
# or by hand:
echo "<github-login>" > contributors/emails/<email>
  • File name = the exact commit-author email (as shown by git log --format='%ae').
  • File content = the GitHub login on the first non-comment line. Lines starting with # are comments (use them for the PR reference).

Example — contributors/emails/jane.doe@example.com:

janedoe
# PR #12345 salvage (gateway: fix session key routing)

Rules

  • Do NOT add new entries to AUTHOR_MAP in scripts/release.py. That dict is frozen legacy data; the release tooling merges it with this directory (directory entries win on duplicates).
  • GitHub noreply emails (<id>+<login>@users.noreply.github.com and <login>@users.noreply.github.com) auto-resolve — no file needed.
  • The Contributor Attribution Check CI job fails a PR whose commits carry an unmapped email; the failure message prints the exact command to run.