Problem or Use Case
Hermes profiles give you fully isolated agent identities — each with its own
SOUL, memory, skills, and config. But today, the only way to expose them as
distinct "agents" to a user is to run one gateway process per profile, each
bound to its own platform credential.
This works on platforms where multiple credentials are cheap and idiomatic:
- Telegram: one bot token per profile → users get
@CoderHermesBot,
@WriterHermesBot, etc.
- Discord: one bot user per profile → users see different bot accounts
in a server.
But for other channels, a user who wants both a coding assistant and a writing
assistant has to choose one — or run two completely separate gateways and
remember which port/URL is which.
And for some channel, like WeChat, it can only bind to one single instance.
Proposed Solution
Make the gateway profile-agnostic and surface profile switching through
chat-level slash commands. No new platform credentials needed.
User-facing surface
| Command |
Effect |
/profile |
Show the chat's active profile + host info |
/profile ls |
List all available profiles (one per ~/.hermes/profiles/<name>/) |
/profile coder |
Bind this chat to the coder profile (persisted; survives restart) |
/profile default |
Reset to the default profile |
@coder fix this bug |
Route just this turn to coder; chat binding intact |
Replies are prefixed with [<agent>] so the user can tell who answered.
Toggle via gateway.show_agent_name: true/false in config.yaml.
Isolation guarantees
- Memory / skills / soul: hard-isolated per profile (each profile's
~/.hermes/profiles/<name>/ directory).
- Session / transcript: each
(chat, agent) pair owns an independent
session_id — /profile coder after talking to default starts (or
resumes) coder's own conversation. Switching back to default restores
its prior transcript. Matches "two independent Telegram bots" semantics
rather than "one bot wearing different hats."
- Gateway runtime state (sessions DB, gateway config, host platform
tokens): stays in the host profile — the gateway is the orchestrator,
the agents are what it routes between.
Mechanism (one paragraph)
HERMES_HOME becomes a ContextVar consulted by hermes_constants.get_hermes_home()
before the env var, so each turn runs inside agent_home_scope(<agent.home>)
and every profile-aware path (memory, skills, SOUL.md) resolves to the right
profile without rebinding the process env var. build_session_key takes an
agent_name parameter so (chat, agent) pairs naturally get distinct
session_keys → distinct session_ids → distinct transcripts. Chat-level
bindings persist to sessions/chat_bindings.json.
Backward compatibility
- Default agent keeps the legacy
agent:main:... session_key prefix → existing
state.db and sessions.json work with zero migration.
- Single-profile users see no behavior change:
get_chat_agent defaults to
default, response prefix is on by default but can be disabled, no new
commands required.
build_session_key(source) without agent_name still works (defaults to
default).
Alternatives Considered
No response
Feature Type
Gateway / messaging improvement
Scope
Large (new module or significant refactor)
Contribution
Debug Report (optional)
Problem or Use Case
Hermes profiles give you fully isolated agent identities — each with its own
SOUL, memory, skills, and config. But today, the only way to expose them as
distinct "agents" to a user is to run one gateway process per profile, each
bound to its own platform credential.
This works on platforms where multiple credentials are cheap and idiomatic:
@CoderHermesBot,@WriterHermesBot, etc.in a server.
But for other channels, a user who wants both a coding assistant and a writing
assistant has to choose one — or run two completely separate gateways and
remember which port/URL is which.
And for some channel, like WeChat, it can only bind to one single instance.
Proposed Solution
Make the gateway profile-agnostic and surface profile switching through
chat-level slash commands. No new platform credentials needed.
User-facing surface
/profile/profile ls~/.hermes/profiles/<name>/)/profile codercoderprofile (persisted; survives restart)/profile default@coder fix this bugcoder; chat binding intactReplies are prefixed with
[<agent>]so the user can tell who answered.Toggle via
gateway.show_agent_name: true/falseinconfig.yaml.Isolation guarantees
~/.hermes/profiles/<name>/directory).(chat, agent)pair owns an independentsession_id—/profile coderafter talking todefaultstarts (orresumes) coder's own conversation. Switching back to
defaultrestoresits prior transcript. Matches "two independent Telegram bots" semantics
rather than "one bot wearing different hats."
tokens): stays in the host profile — the gateway is the orchestrator,
the agents are what it routes between.
Mechanism (one paragraph)
HERMES_HOMEbecomes aContextVarconsulted byhermes_constants.get_hermes_home()before the env var, so each turn runs inside
agent_home_scope(<agent.home>)and every profile-aware path (memory, skills, SOUL.md) resolves to the right
profile without rebinding the process env var.
build_session_keytakes anagent_nameparameter so(chat, agent)pairs naturally get distinctsession_keys → distinct session_ids → distinct transcripts. Chat-level
bindings persist to
sessions/chat_bindings.json.Backward compatibility
agent:main:...session_key prefix → existingstate.dbandsessions.jsonwork with zero migration.get_chat_agentdefaults todefault, response prefix is on by default but can be disabled, no newcommands required.
build_session_key(source)withoutagent_namestill works (defaults todefault).Alternatives Considered
No response
Feature Type
Gateway / messaging improvement
Scope
Large (new module or significant refactor)
Contribution
Debug Report (optional)