Skip to content

fix: validate agent_id at LangGraph adapter agent-runtime concat sites#495

Closed
pandas-studio wants to merge 3 commits intofix/issue-493-validate-agent-toolsfrom
fix/issue-492-langgraph-validate-agent-id
Closed

fix: validate agent_id at LangGraph adapter agent-runtime concat sites#495
pandas-studio wants to merge 3 commits intofix/issue-493-validate-agent-toolsfrom
fix/issue-492-langgraph-validate-agent-id

Conversation

@pandas-studio
Copy link
Copy Markdown
Collaborator

Summary

  • Closes the parity gap PR fix: validate agent_id before agent-runtime namespace concat (CLI + MCP) #491 review flagged: the LangGraph adapter's MemtomemStore.start_agent_session was the last entry point that built agent-runtime:<agent_id> from caller input without running validate_agent_id. Apply the gate so a Python caller can no longer land "agent-runtime:foo:bar" in storage even though the MCP / CLI surfaces refuse the same shape.
  • Replace the local _AGENT_NAMESPACE_PREFIX = "agent-runtime:" literal with the memtomem.constants import (AGENT_NAMESPACE_PREFIX, SHARED_NAMESPACE, validate_agent_id) so the integration derives from the single source of truth — the redefinition predated the multi-agent shipping work and was kept on the assumption that integrations shouldn't import from MCP-side modules, but constants.py is intentionally above the MCP / CLI / integration split exactly so all three can derive from it. langgraph.py → constants.py → context._names stays one-way.
  • Stacked on PR fix: validate agent_id at mem_agent_register / search and mm agent register (#493) #494 (mem_agent_register / mem_agent_search: validate vs sanitize divergence #493) so the validate_agent_id docstring there ("Applied at every surface that concatenates AGENT_NAMESPACE_PREFIX with caller input") becomes accurate the moment both PRs land.

The other concat sites in langgraph.py (_resolve_search_namespace, _resolve_add_namespace) only read self._current_agent_id, which is set exclusively by start_agent_session — gating that single entry point covers the whole agent-runtime concat path.

Test plan

  • uv run pytest packages/memtomem/tests/test_langgraph.py packages/memtomem/tests/test_validate_agent_id.py packages/memtomem/tests/test_multi_agent_integration.py packages/memtomem/tests/test_multi_agent.py packages/memtomem/tests/test_agent_cmd.py -m "not ollama" — 149 passed
  • uv run ruff check + ruff format --check on the two changed files
  • test_empty_agent_id_raises updated to expect InvalidNameError ("invalid agent-id '': empty") rather than the old "non-empty" ValueError
  • New test_hostile_agent_id_blocked_before_storage parametrises the same hostile shapes pinned at the MCP / CLI boundaries (foo:bar, ../etc, a/b, a b, -leading-dash) and asserts storage.create_session is never awaited and _current_agent_id stays unset on the rejection path

Closes #492.

🤖 Generated with Claude Code

memtomem pushed a commit that referenced this pull request Apr 26, 2026
Two follow-ups from the PR #495 review:

* Add a `Changed (BREAKING)` entry under `[Unreleased]` for the
  `MemtomemStore.start_agent_session` exception text change. The
  exception type narrows from `ValueError("agent_id must be a
  non-empty string")` to `InvalidNameError("invalid agent-id 'X':
  ...")` — `InvalidNameError` is a `ValueError` subclass so
  `except ValueError:` callers stay green, but any code
  substring-matching the old message text breaks. The CHANGELOG
  bullet calls out the migration ("switch to matching `invalid
  agent-id`") so a Python integrator sees the change without having
  to dig commit history.

* Promote the "why downstream concat sites trust `_current_agent_id`"
  reasoning from the PR description into one-line docstring notes on
  `_resolve_search_namespace` and `_resolve_add_namespace`. Future
  readers shouldn't have to chase the PR to understand why these
  helpers don't re-run `validate_agent_id` before concatenating —
  `start_agent_session` is the sole writer of the field and the
  validator runs there.

Co-Authored-By: Claude <[email protected]>
pandas-studio and others added 3 commits April 26, 2026 14:08
PR #491 / #493 wired ``validate_agent_id`` into every MCP + CLI surface
that builds an ``agent-runtime:<agent_id>`` namespace, but the in-process
LangGraph adapter still trusted callers. ``MemtomemStore.start_agent_session``
checked only ``if not agent_id`` and concatenated the value straight into
the namespace, so a Python caller could land
``"agent-runtime:foo:bar"`` in storage even after the MCP / CLI gate
shipped — exactly the parity gap the PR #491 review flagged.

Apply ``validate_agent_id`` at ``start_agent_session`` (the only entry
point that takes a caller-provided ``agent_id`` and concatenates it into
the agent-runtime namespace; the other concat sites at
``_resolve_search_namespace`` / ``_resolve_add_namespace`` only read
``self._current_agent_id``, which now passes through the gate). Replace
the local ``_AGENT_NAMESPACE_PREFIX = "agent-runtime:"`` literal with
``from memtomem.constants import AGENT_NAMESPACE_PREFIX, SHARED_NAMESPACE,
validate_agent_id`` so the integration derives from the single source of
truth instead of re-declaring the string (the redefinition predated the
multi-agent shipping work and was kept on the assumption that
integrations shouldn't import from MCP-side modules — but
``constants.py`` is intentionally above the MCP / CLI / integration split
exactly so all three can derive from it; ``langgraph.py → constants.py →
context._names`` stays one-way).

Update ``test_empty_agent_id_raises`` to expect the new
``InvalidNameError`` ("invalid agent-id '': empty") and add a parametrised
regression test covering the same hostile shapes pinned at the MCP / CLI
boundaries (``foo:bar``, ``../etc``, ``a/b``, ``a b``,
``-leading-dash``). The new test also asserts ``storage.create_session``
is never awaited and ``_current_agent_id`` stays unset on the rejection
path, so a regression to a warn-only gate would surface immediately.

Closes #492.

Co-Authored-By: Claude <[email protected]>
Two follow-ups from the PR #495 review:

* Add a `Changed (BREAKING)` entry under `[Unreleased]` for the
  `MemtomemStore.start_agent_session` exception text change. The
  exception type narrows from `ValueError("agent_id must be a
  non-empty string")` to `InvalidNameError("invalid agent-id 'X':
  ...")` — `InvalidNameError` is a `ValueError` subclass so
  `except ValueError:` callers stay green, but any code
  substring-matching the old message text breaks. The CHANGELOG
  bullet calls out the migration ("switch to matching `invalid
  agent-id`") so a Python integrator sees the change without having
  to dig commit history.

* Promote the "why downstream concat sites trust `_current_agent_id`"
  reasoning from the PR description into one-line docstring notes on
  `_resolve_search_namespace` and `_resolve_add_namespace`. Future
  readers shouldn't have to chase the PR to understand why these
  helpers don't re-run `validate_agent_id` before concatenating —
  `start_agent_session` is the sole writer of the field and the
  validator runs there.

Co-Authored-By: Claude <[email protected]>
PR #493 narrowed the ``validate_agent_id`` docstring to "MCP + CLI
surfaces" only, deferring the LangGraph claim until #492 actually
wires the adapter. This commit lands on top of that wiring (commit
4633256), so the LangGraph adapter is now a real entry point — re-add
it to the docstring's "Applied at" list and merge the pre-#492 /
pre-#493 history into a single migration paragraph that covers both
surfaces.

The docstring stays accurate at every snapshot: pre-#492 it cited
only the MCP + CLI surfaces, post-#492 it cites all three. No code
change.

Co-Authored-By: Claude <[email protected]>
@memtomem memtomem force-pushed the fix/issue-492-langgraph-validate-agent-id branch from a2cfbdf to 9123fab Compare April 26, 2026 05:08
@pandas-studio pandas-studio deleted the branch fix/issue-493-validate-agent-tools April 26, 2026 05:14
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 26, 2026
@pandas-studio
Copy link
Copy Markdown
Collaborator Author

Replaced by #498 — same 3-commit stack rebased onto current main after #494 squash-merged the base branch (auto-close per feedback_stacked_pr_base_delete_auto_close.md). All review feedback already addressed inline.

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.

1 participant