Skip to content

fix(session): scope build_session_key by active profile, preserve default (#12099)#12266

Closed
briandevans wants to merge 2 commits into
NousResearch:mainfrom
briandevans:fix/build-session-key-profile-isolation
Closed

fix(session): scope build_session_key by active profile, preserve default (#12099)#12266
briandevans wants to merge 2 commits into
NousResearch:mainfrom
briandevans:fix/build-session-key-profile-isolation

Conversation

@briandevans

Copy link
Copy Markdown
Contributor

Fixes #12099.

TL;DR

gateway.session.build_session_key hardcoded agent:main as the leading component of every session key, ignoring the active Hermes profile. Two profiles (e.g. default and coder) that share a memory backend therefore collide on the same remote namespace.

Fix: thread the active profile through build_session_key. The default profile keeps the legacy agent:main prefix (backcompat for every existing session and remote memory record); named profiles get agent:<profile>:….

Why it matters (beyond cosmetics)

Local session storage is already isolated at the filesystem level — each profile has its own HERMES_HOME and therefore its own sessions/ directory — so the hardcode doesn't corrupt local SQLite/JSONL.

The real collision is in external memory-provider namespaces. plugins/memory/honcho/client.py::resolve_session_name takes the gateway session key and uses it verbatim as the remote session name (colons sanitized to hyphens):

# Gateway session key: stable per-chat identifier passed by the gateway
# (e.g. "agent:main:telegram:dm:8439114563"). Sanitize colons to hyphens
# for Honcho session ID compatibility. This takes priority over strategy-
# based resolution because gateway platforms need per-chat isolation that
# cwd-based strategies cannot provide.
if gateway_session_key:
    sanitized = re.sub(r'[^a-zA-Z0-9_-]+', '-', gateway_session_key).strip('-')

The equivalent pattern exists in RetainDB / ByteRover / Supermemory integrations. Two profiles that point at the same memory backend therefore read/write the same remote namespace — exactly the "WeChat messages may read or write to another profile's memory" case from the report.

Root cause

gateway/session.py:440-479 (pre-fix) hardcoded agent:main in 5 places — 4 DM branches and one group/channel branch — with no profile parameter on the function.

Fix

Small _resolve_session_key_prefix(profile) helper + optional profile kwarg:

def _resolve_session_key_prefix(profile: Optional[str]) -> str:
    if profile is None:
        try:
            from hermes_cli.profiles import get_active_profile_name
            profile = get_active_profile_name()
        except Exception:
            # hermes_cli may be unavailable in narrow unit-test contexts;
            # fall through to the legacy default so keys stay stable.
            profile = "default"

    if not profile or profile == "default":
        return "agent:main"
    return f"agent:{profile}"

and rebuild every formatted return in build_session_key using that prefix. The 5 "agent:main" literals are replaced with the resolved prefix — nothing else changes in the DM / group / thread logic.

Behaviour matrix

Active profile Resolved prefix Example DM key
unset / default agent:main agent:main:telegram:dm:99
coder agent:coder agent:coder:telegram:dm:99
custom (HERMES_HOME outside the profile tree) agent:custom agent:custom:telegram:dm:99
explicit profile="default" agent:main agent:main:...
explicit profile="coder" agent:coder agent:coder:...

Every existing caller (gateway/platforms/base.py, slack.py, feishu.py, wecom.py, tests) uses the default profile=None path. Zero caller signatures change.

Narrow scope — explicitly not changed

  • Migration of existing sessions. The default-profile prefix stays agent:main on purpose. Orphaning every user's existing session history (and their memory-provider records) is not acceptable.
  • Per-message caching. run_agent.py:720 already stores _gateway_session_key per agent instance. Profile resolution is process-lifetime-stable and sub-millisecond — no need for module-level caching here.
  • sessions_dir layout. Already HERMES_HOME-scoped, unchanged.
  • External memory-provider sanitization rules. The sanitize-colons-to-hyphens step in honcho/client.py is unchanged — my fix just gives it a more discriminating input.
  • Cross-profile session merging. Out of scope. If users need to pull sessions across profiles, that's a separate feature, not a bug fix.

Regression coverage

tests/gateway/test_session.py gets a new TestProfileScopedSessionKeys class with 10 parametrised cases:

  • 3 backward-compat canariesprofile="default" and profile=None with get_active_profile_name mocked to "default" must still produce agent:main:….
  • 3 new-behaviour cases — named profile via explicit arg, via env, and for group/thread keys.
  • 1 reporter repro — same (telegram, chat_id=99) under two different profiles must produce different keys.
  • 1 override pin — explicit profile="default" wins over an active "coder" profile.
  • 1 "custom" profile case — unusual HERMES_HOME paths surface as agent:custom:….
  • 1 import-failure canaryImportError from hermes_cli.profiles falls back to agent:main instead of raising.

8 of 10 fail on clean origin/main with signature and behaviour errors:

TypeError: build_session_key() got an unexpected keyword argument 'profile'
AssertionError: assert 'agent:main:telegram:dm:99' == 'agent:custom:telegram:dm:99'

The 2 that pass on main are backcompat canaries — they happen to match because of the hardcoded string. Keeping them pins preserved behaviour so any future regression that changes the default-profile key shape gets caught.

Validation

source venv/bin/activate
python -m pytest \
  tests/gateway/test_session.py \
  tests/gateway/test_session_race_guard.py \
  tests/gateway/test_session_model_reset.py \
  tests/test_mcp_serve.py \
  tests/gateway/test_session_dm_thread_seeding.py -q
# 180 passed

Broader tests/gateway under -n auto → 14 pre-existing baseline failures (dingtalk card lifecycle, matrix encrypted upload, approve/deny E2E, whatsapp bridge runtime / xdist flakes). Zero in touched code. Per-test baseline audit available on request.

Pre-empted review questions

Q. Why keep the agent:main literal for the default profile? Isn't the natural rename agent:default?
Two reasons:

  1. Remote memory providers (Honcho/RetainDB/ByteRover) use the key as a namespace. Changing the default prefix would orphan every existing memory record for every user on the default profile.
  2. tests/test_mcp_serve.py, tests/gateway/test_session*.py, tests/cron/test_codex_execution_paths.py all hardcode agent:main:… — a rename would churn ~80 test literals unrelated to this bug. Scope creep.

The explicit mapping (defaultagent:main, named profile → agent:<name>) documents the historical name at the only place it's relevant.

Q. Is get_active_profile_name() cheap enough to call on every build_session_key?
Yes. It does one Path.resolve() and a startswith check. Benchmarked locally at ~30 µs. build_session_key is itself called once per inbound message — orders of magnitude below network latency.

Q. Import cycle risk?
No. gateway/session.py imports from hermes_cli/profiles.py inside _resolve_session_key_prefix (deferred). hermes_cli/profiles.py only imports from hermes_constants and (conditionally inside functions) from gateway.status — not from gateway.session. No cycle.

Q. What about run_agent.py:720's cached _gateway_session_key?
That cache stores the key after it's been built. As long as the active profile doesn't change mid-process (it can't — HERMES_HOME is fixed per process), the cached value is correct.

Q. What if someone flips active_profile at runtime?
They can't. HERMES_HOME is baked in at process start by hermes_constants.get_hermes_home(). Profile switches require a process restart by design.


Co-authored via LLM assistance; I've reviewed every line and am responsible for correctness.

Copilot AI review requested due to automatic review settings April 18, 2026 20:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes session-key collisions across multiple Hermes profiles by scoping gateway.session.build_session_key with the active profile while preserving the legacy agent:main prefix for the default profile (backward compatible with existing sessions and external memory namespaces).

Changes:

  • Add _resolve_session_key_prefix(profile) and thread optional profile through build_session_key to generate agent:<profile>:... for named profiles and agent:main:... for default.
  • Replace hardcoded agent:main literals in session key construction with the resolved prefix.
  • Add regression tests covering default backcompat, named-profile isolation, and fallback behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
gateway/session.py Adds profile-aware session key prefix resolution and applies it throughout build_session_key.
tests/gateway/test_session.py Adds regression coverage ensuring profile-scoped keys and preserved default behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread gateway/session.py
Comment on lines +496 to +502
- The ``"default"`` profile keeps the legacy ``agent:main`` prefix for
backward compatibility — existing sessions and external memory
namespaces continue to resolve. Named profiles produce
``agent:<profile>:...`` so multi-profile deployments no longer share
a namespace. See #12099.
"""
prefix = _resolve_session_key_prefix(profile)

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

build_session_key now emits keys like agent:<profile>:... for named profiles. There is existing parsing logic in gateway/run.py::_parse_session_key that only accepts agent:main:... (it checks parts[1] == "main"), which will cause session-key parsing to fail for non-default profiles and can break features that rely on it (e.g., shutdown notifications and synthetic process-event routing when the session store entry is missing). Please update _parse_session_key (and any similar parsers) to accept any agent:<scope> second component, or otherwise make parsing profile-aware.

Suggested change
- The ``"default"`` profile keeps the legacy ``agent:main`` prefix for
backward compatibilityexisting sessions and external memory
namespaces continue to resolve. Named profiles produce
``agent:<profile>:...`` so multi-profile deployments no longer share
a namespace. See #12099.
"""
prefix = _resolve_session_key_prefix(profile)
- Session keys continue to use the legacy ``agent:main`` prefix for
compatibility with existing downstream parsers that still expect that
exact second component. Preserve parseability first; profile-aware
parsing should be added before emitting ``agent:<profile>:...`` here.
"""
prefix = _resolve_session_key_prefix(profile)
if prefix.startswith("agent:") and prefix != "agent:main":
prefix = "agent:main"

Copilot uses AI. Check for mistakes.
Comment thread tests/gateway/test_session.py Outdated
Comment on lines +950 to +952
def test_hermes_cli_import_failure_falls_back_to_default(self):
"""If hermes_cli.profiles can't be imported (narrow unit-test contexts),
we must not raise — fall back to legacy agent:main prefix."""

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test/docstring says it covers the case where hermes_cli.profiles can't be imported, but the patch is applied to hermes_cli.profiles.get_active_profile_name and then raises ImportError from the mocked function call. Either adjust the wording to reflect that it’s exercising an exception raised while resolving the active profile, or change the test to simulate an actual import failure (e.g., making the import of hermes_cli.profiles raise ModuleNotFoundError).

Suggested change
def test_hermes_cli_import_failure_falls_back_to_default(self):
"""If hermes_cli.profiles can't be imported (narrow unit-test contexts),
we must not raise fall back to legacy agent:main prefix."""
def test_active_profile_resolution_import_error_falls_back_to_default(self):
"""If resolving the active profile raises ImportError, we must not raise
fall back to the legacy agent:main prefix."""

Copilot uses AI. Check for mistakes.
@briandevans

Copy link
Copy Markdown
Contributor Author

Thanks for the catch @copilot-pull-request-reviewer — both addressed in aad2af8f.

1. _parse_session_key strict-prefix coupling (the real find).
You're right that this was a silent-break for named-profile users — _notify_active_sessions_of_shutdown (run.py:1549) and the synthetic process-event router (run.py:7929) would skip entries whose key started with agent:<profile>:….

Fixed at the parser rather than the producer (your suggestion diff inverted my original intent by forcing the prefix back to agent:main, which would lose the multi-profile namespace isolation that's the whole point of #12099). The parser now accepts any non-empty parts[1]; structural checks (parts[0] == "agent" + 5+ fields) remain. Docstring updated to match.

Also audited the other uses of agent:main prefixes across gateway/ and run_agent.py:

  • gateway/run.py:2098 (session-expiry log summary) already uses parts[2] for platform — profile-agnostic. Safe.
  • gateway/mirror.py:72 — docstring example only, no functional parsing.
  • run_agent.py:720 — comment, no parsing.

Added 5 regression tests under TestParseSessionKey* in test_background_process_notifications.py pinning that named-profile keys parse identically to default-profile keys for routing purposes (same platform/chat_id outputs), plus an agent::telegram:… empty-scope canary. Updated test_parse_session_key_wrong_prefix to drop the agent:cron:… assertion (that key now is a valid structural parse under the fix — "cron" is still reserved at the profile-validation layer, but _parse_session_key is a structural parser, not a semantic validator).

2. Import-failure test wording.
Fair nit — the patch raises from the mocked function, not the import itself. Renamed to test_active_profile_resolution_failure_falls_back_to_default, reworded the docstring to match, and added a second RuntimeError side-effect so the test pins that any exception (not just ImportError) triggers the fallback — matching the broad except Exception in the fix.

Focused suite → 42 passed.

@briandevans

Copy link
Copy Markdown
Contributor Author

CI audit on the initial push (pre-aad2af8f):

build-and-push FAILURE — Docker entrypoint smoke test:

mkdir: cannot create directory '/opt/data/cron': Permission denied
mkdir: cannot create directory '/opt/data/sessions': Permission denied
…

Pre-existing infrastructure issue (container UID vs mounted /opt/data ownership). Unrelated to session keys. Reproduces on origin/main builds.

test FAILURE — same deterministic baselines I already classified against clean origin/main (6fb69229):

  • test_browser_camofox_state.py::test_config_version_matches_current_schema (assert 19 == 18)
  • test_web_server.py::test_no_single_field_categories (category-merge drift)
  • test_concurrent_interrupt.py (_Stub missing _apply_pending_steer_to_tool_results)
  • test_send_message_tool.py xdist flakes (pass locally under -n auto on clean main)

None touch gateway/session.py, gateway/run.py, or any session-key parser.

Green: check-attribution, e2e, supply-chain scan.

After aad2af8f (follow-up that addresses Copilot's real find — _parse_session_key coupling + test wording): CI re-running now.

@briandevans

Copy link
Copy Markdown
Contributor Author

CI after aad2af8f (the _parse_session_key follow-up):

  • e2e → SUCCESS ✓
  • check-attribution → SUCCESS ✓
  • Scan PR for supply chain risks → SUCCESS ✓
  • build-and-push → FAILURE. Same Docker entrypoint permission errors as the initial push (mkdir: cannot create directory '/opt/data/cron': Permission denied). Pre-existing infra issue, reproduces on origin/main builds.
  • test → FAILURE. Same deterministic baselines I classified in the first audit — test_browser_camofox_state.py (assert 19 == 18), test_web_server.py (category-merge drift), 2× test_concurrent_interrupt.py (_Stub missing attr), plus the usual test_send_message_tool.py xdist flakes.

None touch gateway/session.py, gateway/run.py, or the new _parse_session_key tests.

Focused suite on e627b3f7: 42 passed (10 profile-scoped session keys + 5 new _parse_session_key named-profile tests + 27 existing background-process-notifications).

@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins tool/memory Memory tool and memory providers labels Apr 23, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related: #12102, #12103, #12108 all address the same root cause (hardcoded agent:main in build_session_key). This PR appears to be the most comprehensive fix with backcompat for the default profile.

@briandevans

Copy link
Copy Markdown
Contributor Author

Thanks @alt-glitch for the cross-reference triage. 👍

For reference, the test coverage here (10 cases in TestProfileScopedSessionKeys):

  • 3 backward-compat canaries (default profile preserves legacy agent:main prefix — critical for not orphaning existing sessions or remote memory-provider namespaces like Honcho/RetainDB/ByteRover)
  • 3 named-profile isolation cases (DM / group / group-with-thread)
  • 1 reporter repro (two profiles same chat_id → distinct keys)
  • 1 explicit-override canary (profile="default" wins over an active "coder" profile)
  • 1 custom profile case (unusual HERMES_HOME paths)
  • 1 import-failure fallback

If maintainers prefer #12102 / #12103 / #12108 over this one, happy to defer and close — just flagging the backcompat dimension so whichever lands carries it forward.

briandevans and others added 2 commits May 4, 2026 20:13
…ault (NousResearch#12099)

``gateway.session.build_session_key`` hardcoded ``agent:main`` as the
leading component of every session key.  The hardcode ignored the
active Hermes profile (``hermes_cli.profiles.get_active_profile_name``)
and caused multi-profile deployments to collide on a shared namespace.

Reporter: NousResearch#12099.

Why it matters (beyond cosmetics)
---------------------------------
Local session storage is already isolated at the filesystem level
(each profile has its own ``HERMES_HOME`` → its own ``sessions/``
directory), so the hardcode didn't corrupt local SQLite / JSONL.

The real collision is in **external memory-provider namespaces**.
``plugins/memory/honcho/client.py::resolve_session_name`` (and the
equivalent code paths in RetainDB / ByteRover / Supermemory) take
the gateway session key and use it *verbatim* as the remote session
name — see ``plugins/memory/honcho/client.py:546-554``::

    # Gateway session key: stable per-chat identifier passed by the gateway
    # (e.g. "agent:main:telegram:dm:8439114563"). Sanitize colons to hyphens
    # for Honcho session ID compatibility. This takes priority over strategy-
    # based resolution because gateway platforms need per-chat isolation that
    # cwd-based strategies cannot provide.
    if gateway_session_key:
        sanitized = re.sub(r'[^a-zA-Z0-9_-]+', '-', gateway_session_key).strip('-')

Two profiles that point at the same Honcho / memory backend therefore
read and write the same remote namespace — exactly the "WeChat messages
may read or write to another profile's memory" case from the report.

Root cause
----------
``gateway/session.py:440-479`` (pre-fix) hardcoded ``agent:main`` in
5 places — 4 DM branches and one group/channel branch — with no
profile parameter.

Fix
---
Add a small ``_resolve_session_key_prefix(profile)`` helper and an
optional ``profile`` kwarg to ``build_session_key``:

* ``profile is None`` (the default — unchanged call sites) resolves
  the active profile from ``hermes_cli.profiles.get_active_profile_name``.
* Profile ``"default"`` (or an empty/None string after fallback) keeps
  the legacy ``agent:main`` prefix — preserving every existing
  session key, every hardcoded test fixture (``tests/test_mcp_serve.py``
  has 30+ such strings), and every remote memory-provider record.
* Named profiles get ``agent:<profile>:…``, so "coder" and "default"
  now produce distinct keys for the same ``(platform, chat_id)``.
* Explicit ``profile=`` argument wins over the resolved default.
* Import failures of ``hermes_cli.profiles`` fall back to the legacy
  prefix so narrow unit-test contexts don't start raising.

Behaviour matrix
----------------
| Active profile | Resolved prefix | Example DM key |
| --- | --- | --- |
| *unset / ``default``* | ``agent:main`` | ``agent:main:telegram:dm:99`` |
| ``coder`` | ``agent:coder`` | ``agent:coder:telegram:dm:99`` |
| ``custom`` (HERMES_HOME outside profile tree) | ``agent:custom`` | ``agent:custom:telegram:dm:99`` |
| explicit ``profile="default"`` | ``agent:main`` | ``agent:main:...`` |
| explicit ``profile="coder"`` | ``agent:coder`` | ``agent:coder:...`` |

Narrow scope — explicitly not changed
-------------------------------------
* **Migration of existing sessions.**  The default-profile prefix
  stays ``agent:main`` on purpose, so nobody's sessions orphan.
* **Caller API.**  Every existing caller
  (``gateway/platforms/base.py``, ``gateway/platforms/slack.py``,
  ``gateway/platforms/feishu.py``, ``gateway/platforms/wecom.py``,
  and tests) uses the default ``profile=None`` path.  No caller
  signatures changed.
* **Per-message caching.**  ``run_agent.py:720`` already stores
  ``_gateway_session_key`` per agent instance.  The resolved profile
  is stable for a process lifetime (HERMES_HOME doesn't change
  mid-process), so repeated resolution is sub-millisecond and local.
* **``sessions_dir`` layout.**  Already ``HERMES_HOME``-scoped;
  unchanged.

Regression coverage
-------------------
``tests/gateway/test_session.py`` gets a new
``TestProfileScopedSessionKeys`` class with 10 parametrised cases:

* 3 backward-compat canaries — ``profile="default"`` and ``profile=None``
  with ``get_active_profile_name`` mocked to ``"default"`` must still
  produce ``agent:main:…``.
* 3 new-behaviour cases — named profile via explicit arg, via env, and
  for group/thread keys.
* 1 reporter repro — same ``(telegram, chat_id=99)`` under two
  different profiles must produce different keys.
* 1 override pin — explicit ``profile="default"`` wins over an active
  ``"coder"`` profile.
* 1 "custom" profile case — unusual ``HERMES_HOME`` paths surface as
  ``agent:custom:…``.
* 1 import-failure canary — ``ImportError`` from ``hermes_cli.profiles``
  falls back to ``agent:main`` instead of raising.

8 of the 10 fail on clean ``origin/main``.  The 2 that pass on main
(both backcompat canaries for the default profile) pin preserved
behaviour — they'd catch any future regression that accidentally
changes the default-profile key shape.

Validation
----------
``source venv/bin/activate && python -m pytest
tests/gateway/test_session.py tests/gateway/test_session_race_guard.py
tests/gateway/test_session_model_reset.py tests/test_mcp_serve.py
tests/gateway/test_session_dm_thread_seeding.py -q`` → **180 passed**.

Broader ``tests/gateway`` under ``-n auto`` → 14 pre-existing baseline
failures (dingtalk card lifecycle, matrix encrypted upload,
approve/deny E2E, whatsapp bridge runtime / xdist flakes).  Zero in
touched code paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…NousResearch#12099)

Copilot flagged that ``gateway.run._parse_session_key`` only accepted
``agent:main:...`` keys — so after the parent change threaded profile
scope through ``build_session_key``, named-profile session keys like
``agent:coder:telegram:dm:99`` would silently return ``None`` from the
parser.  That broke:

* Gateway shutdown notifications (``_notify_active_sessions_of_shutdown``
  at ``run.py:1549``) — named-profile users wouldn't receive the
  restart warning.
* Synthetic process-event routing when the session-store lookup misses
  (``run.py:7929``) — chat routing would fall back to env metadata only.

Fix: relax the strict ``parts[1] == "main"`` check to require only a
non-empty profile scope.  Structural checks remain — ``parts[0]`` must
be ``agent`` and the key must have at least 5 colon-separated fields.

Also:

* Update the existing ``test_parse_session_key_wrong_prefix`` test —
  the ``agent:cron:...`` assertion was pinning the old strict
  behaviour; replace it with an ``other:main:...`` check for the
  ``parts[0] != "agent"`` path and an ``agent::...`` check for the
  empty-scope path.
* Add five new regression tests under
  ``test_background_process_notifications.py`` pinning that named-
  profile keys (DM, group, thread, ``custom`` profile) parse correctly
  and produce the same platform/chat_id routing as the equivalent
  default-profile key.
* Rename / broaden
  ``test_hermes_cli_import_failure_falls_back_to_default`` →
  ``test_active_profile_resolution_failure_falls_back_to_default``
  (Copilot nit — the patch fixture raises from the mocked function,
  not from the import itself).  Add a second ``RuntimeError`` side-
  effect to pin that the fallback covers any exception.

Validation
----------
``source venv/bin/activate && python -m pytest
tests/gateway/test_session.py::TestProfileScopedSessionKeys
tests/gateway/test_background_process_notifications.py -q`` →
**42 passed**.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@briandevans briandevans force-pushed the fix/build-session-key-profile-isolation branch from aad2af8 to 290c20d Compare May 5, 2026 03:14
@briandevans

Copy link
Copy Markdown
Contributor Author

Rebased onto current origin/main (d12f59aa5) — was 2400+ commits behind. Resolved conflicts in gateway/session.py (preserved is_shared_multi_user_session and the WhatsApp canonicalization in build_session_key's DM and group/participant paths) and tests/gateway/test_session.py (kept TestWhatsAppIdentifierPublicHelpers alongside TestProfileScopedSessionKeys).

Re-ran focused tests on the rebased head (290c20d05):

  • tests/gateway/test_session.py::TestProfileScopedSessionKeys — 10/10 pass
  • tests/gateway/test_background_process_notifications.py — 32/32 pass

Fix is unchanged: build_session_key threads the active profile through, "default" keeps the legacy agent:main prefix (backcompat with existing sessions and external memory-provider namespaces), named profiles get agent:<profile>:.... _parse_session_key accepts any non-empty profile scope. WhatsApp canonicalization on group participant_id and DM chat_id is preserved through the prefix rewrite.

@briandevans

Copy link
Copy Markdown
Contributor Author

Closing to keep the queue clean — happy to reopen if this is still useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins P1 High — major feature broken, no workaround tool/memory Memory tool and memory providers type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

build_session_key() hardcodes 'agent:main', breaking multi-profile session isolation

3 participants