Skip to content

fix(multi-agent): wire agent_id from mem_session_start to mem_agent_search#459

Merged
memtomem merged 1 commit intomainfrom
fix/multi-agent-session-inherits-agent-id
Apr 24, 2026
Merged

fix(multi-agent): wire agent_id from mem_session_start to mem_agent_search#459
memtomem merged 1 commit intomainfrom
fix/multi-agent-session-inherits-agent-id

Conversation

@memtomem
Copy link
Copy Markdown
Owner

Summary

mem_session_start only stored current_session_id while
mem_agent_search(agent_id=None) fell back to current_namespace — a
different axis altogether. The multi-agent guide's
"agent_id is not auto-detected, but inherited via session context"
promise silently broke: an agent that started a session still had to
repeat its agent_id on every search, or got results from
current_namespace instead of its own private namespace.

This PR threads agent_id through the session lifecycle and gives
multi-agent search a documented resolution priority.

Changes

  • New current_agent_id field on AppContext. Set by
    mem_session_start(agent_id=...), reset by mem_session_end.
    Lives behind a new _session_lock distinct from _config_lock so
    long-running config writes can't block session updates and vice versa.
  • _resolve_agent_namespace(app, agent_id) helper documents the
    priority: explicit agent_id arg > current_agent_id >
    current_namespace (legacy fallback). Top-level so the contract is
    unit-testable without standing up MCP components.
  • mem_session_start handles "active session already exists" explicitly.
    The previous session is auto-ended (warning logged + inline notice
    in the return string) instead of silently overwritten — the storage
    row is closed with an auto_ended: true metadata flag for audit. The
    new agent_id replaces the old; agents do not stack.
  • mem_session_end resets both current_session_id and
    current_agent_id.
    "No active session" branch preserved.

State transition table

Trigger current_session_id current_agent_id DB row state
mem_session_start("a"), no active new uuid "a" row created
mem_session_start("b"), active "a" new uuid "b" "a" row closed (auto-ended), "b" row created
mem_session_end(), active None None row closed
mem_session_end(), no active None None unchanged
mem_agent_search(agent_id="x") unchanged unchanged resolves agent-runtime:x
mem_agent_search(agent_id=None), session active unchanged unchanged resolves agent-runtime:<current_agent_id>
mem_agent_search(agent_id=None), no session unchanged unchanged resolves current_namespace (legacy)

Test plan

  • TestResolveAgentNamespace (test_multi_agent.py) — 5 cases for
    the priority order: explicit wins, current_agent_id, legacy namespace,
    all-None, override-while-active.
  • TestSessionAgentInheritance (test_sessions.py) — fresh start
    records agent, second start auto-ends previous, end resets both
    fields, end without active is no-op, the two locks are not aliased.
  • uv run pytest -m "not ollama" — 2339 passed, 46 deselected.
  • uv run ruff check + ruff format --check — clean.

🤖 Generated with Claude Code

memtomem pushed a commit that referenced this pull request Apr 24, 2026
Addresses three nits from the PR #457 review:

1. Drop the unused CLAUDE_/GEMINI_/CODEX_MEMORY_NAMESPACE_PREFIX exports
   from `constants.py`. Promoting them while `cli/ingest_cmd.py` still
   hardcodes the literals would have created a *new* parallel-literal
   seam — exactly what `feedback_drift_close_must_derive` warns against.
   Wiring the ingest CLI is a separate change, not this PR's scope.

2. Drop the `_AGENT_NAMESPACE_PREFIX = AGENT_NAMESPACE_PREFIX` re-export
   alias in `multi_agent.py`. The "Re-export for callers that previously
   imported the local-private symbol" comment was misleading —
   underscore-private symbols have no external callers by definition,
   and grep confirms zero in-repo references on this branch. Stacked
   PRs (#459 / #461) that import the underscore-private symbol get
   updated when they rebase onto this PR; the public
   `AGENT_NAMESPACE_PREFIX` is the right import target.

3. Update the comment above `config.search.system_namespace_prefixes =
   ["archive:"]` in `test_trust_ux.py:68`. After this PR's default flip
   the override is *narrowing* (default has both `archive:` and
   `agent-runtime:`), not "spelling out" — the new comment says so and
   notes that multi-agent isolation has its own coverage in
   `test_multi_agent`.

No behavior change. 2335 tests still pass.

Co-Authored-By: Claude <[email protected]>
memtomem added a commit that referenced this pull request Apr 24, 2026
* fix(multi-agent): hide agent-runtime: from default mem_search

`search.system_namespace_prefixes` default was `["archive:"]` only, so
agent A's private chunks (created by `mem_agent_register` /
`mem_agent_search`) leaked into agent B's `mem_search(namespace=None)`
results — directly contradicting the namespace-based isolation
guarantee documented on the multi-agent guide.

Extends the default to `["archive:", "agent-runtime:"]` and consolidates
the literal in a new `memtomem.constants` module so config, MCP tools,
and CLI all derive the prefix from one source. `mem_agent_search` is
unaffected because it builds an explicit namespace filter; the hidden
count surfaces through the existing `hidden_system_ns` hint.

Behavior change: callers relying on plain `mem_search` to read
`agent-runtime:*` chunks must now either pin `namespace=` or override
`search.system_namespace_prefixes: []` in `config.json`. Documented
under [Unreleased] / Changed.

Threat model: `agent-runtime:<id>` is a *convenience* isolation
boundary, not a security boundary. Direct storage access (raw
`namespace=`, ingest `--namespace`, list_namespaces) still reaches
private chunks; this PR closes the default-search UX leak only.

Test surface (test_multi_agent.py):
- `TestDefaultIsolation` pins constants, default factory freshness,
  and `Mem2MemConfig` default — derives from the constant per
  `feedback_pin_test_constant_over_source_scan`.
- `TestAgentRuntimeIsolationPipeline` end-to-end: `agent-runtime:planner`
  chunks excluded from `namespace=None` search but reachable via
  pinned `namespace=`; `shared` namespace stays surfaceable.

Co-Authored-By: Claude <[email protected]>

* review fixup: drop dead alias/symbols + correct trust_ux comment

Addresses three nits from the PR #457 review:

1. Drop the unused CLAUDE_/GEMINI_/CODEX_MEMORY_NAMESPACE_PREFIX exports
   from `constants.py`. Promoting them while `cli/ingest_cmd.py` still
   hardcodes the literals would have created a *new* parallel-literal
   seam — exactly what `feedback_drift_close_must_derive` warns against.
   Wiring the ingest CLI is a separate change, not this PR's scope.

2. Drop the `_AGENT_NAMESPACE_PREFIX = AGENT_NAMESPACE_PREFIX` re-export
   alias in `multi_agent.py`. The "Re-export for callers that previously
   imported the local-private symbol" comment was misleading —
   underscore-private symbols have no external callers by definition,
   and grep confirms zero in-repo references on this branch. Stacked
   PRs (#459 / #461) that import the underscore-private symbol get
   updated when they rebase onto this PR; the public
   `AGENT_NAMESPACE_PREFIX` is the right import target.

3. Update the comment above `config.search.system_namespace_prefixes =
   ["archive:"]` in `test_trust_ux.py:68`. After this PR's default flip
   the override is *narrowing* (default has both `archive:` and
   `agent-runtime:`), not "spelling out" — the new comment says so and
   notes that multi-agent isolation has its own coverage in
   `test_multi_agent`.

No behavior change. 2335 tests still pass.

Co-Authored-By: Claude <[email protected]>

---------

Co-authored-by: pandas-studio <[email protected]>
Co-authored-by: Claude <[email protected]>
@memtomem memtomem force-pushed the fix/multi-agent-session-inherits-agent-id branch from dc59cc0 to a1f47e4 Compare April 24, 2026 21:51
…earch

`mem_session_start` only stored `current_session_id` while
`mem_agent_search(agent_id=None)` fell back to `current_namespace` — a
different axis altogether. The multi-agent guide's "agent_id is not
auto-detected, but inherited via session context" promise silently
broke: an agent that started a session still had to repeat its
agent_id on every search, or got results from `current_namespace`
instead of its own private namespace.

This PR threads agent_id through the session lifecycle and gives
multi-agent search a documented resolution priority.

Changes:

* New `current_agent_id` field on `AppContext`. Set by
  `mem_session_start(agent_id=...)`, reset by `mem_session_end`. Lives
  behind a new `_session_lock` distinct from `_config_lock` so a
  long-running config write can't block a session update and vice
  versa.

* `_resolve_agent_namespace(app, agent_id)` helper documents the
  priority: explicit `agent_id` arg > `current_agent_id` >
  `current_namespace` (legacy fallback). Extracted as a top-level
  function so the contract is unit-testable without standing up MCP
  components.

* `mem_session_start` now handles the "active session already exists"
  case explicitly. The previous session is auto-ended (warning logged
  + inline notice in the return string) instead of silently overwritten
  — the storage row for the auto-ended session is closed with an
  `auto_ended: true` metadata flag for audit. The new agent_id
  replaces the old; agents do not stack.

* `mem_session_end` resets both `current_session_id` and
  `current_agent_id`. The "no active session" branch is preserved.

Test surface:

* `TestResolveAgentNamespace` (test_multi_agent.py) — 5 cases for
  the priority order (explicit wins, current_agent_id, legacy
  namespace, all-None, override-while-active).
* `TestSessionAgentInheritance` (test_sessions.py) — fresh start
  records agent, second start auto-ends previous, end resets both
  fields, end without active is no-op, the two locks are not
  aliased.

Co-Authored-By: Claude <[email protected]>
@memtomem memtomem force-pushed the fix/multi-agent-session-inherits-agent-id branch from a1f47e4 to b63422d Compare April 24, 2026 22:04
@memtomem memtomem merged commit 6e201bc into main Apr 24, 2026
1 check passed
@memtomem memtomem deleted the fix/multi-agent-session-inherits-agent-id branch April 24, 2026 22:04
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 24, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants