Skip to content

fix(a2a): namespace forwarded session context_id by app/user - #7306

Open
chelsealong wants to merge 2 commits into
google:mainfrom
chelsealong:fix/a2a-namespace-context-id-7305
Open

chelsealong wants to merge 2 commits into
google:mainfrom
chelsealong:fix/a2a-namespace-context-id-7305

Conversation

@chelsealong

Copy link
Copy Markdown
Contributor

Summary

Fixes #7305.

RemoteA2aAgent with forward_session_id_as_context_id=True forwarded the
raw ctx.session.id as the remote A2A context_id. Local ADK session
identity also includes user_id and app_name, so two distinct local users
(or apps) that happen to reuse the same session ID were mapped to the same
remote context. Against a remote implementation that keys conversation state
off context_id, this let one user's conversation history leak to another
user.

Fix

Added _namespaced_session_context_id() in
src/google/adk/a2a/agent/_remote_a2a_agent.py, which derives the forwarded
context_id from a SHA-256 digest of app_name, user_id, and session.id
(joined with a separator byte that cannot appear in the inputs), instead of
the raw session ID. RemoteA2aAgent._run_async_impl now calls this helper
wherever it previously read ctx.session.id directly. Updated the
forward_session_id_as_context_id docstring in
src/google/adk/a2a/agent/config.py to describe the namespaced derivation.

This only changes the derivation of the default context_id used when no
context ID has already been established by a prior remote response
(context_id from _construct_message_parts_from_session still takes
precedence, preserving existing conversation continuity).

Test plan

Updated tests/unittests/a2a/agent/test_remote_a2a_agent.py:

  • test_run_async_impl_uses_session_id_when_opted_in now asserts the
    namespaced (hashed) context_id is used, not the raw session id.
  • Added test_run_async_impl_namespaces_context_id_by_user, a regression
    test asserting that two sessions with the same session.id but different
    user_id produce different derived context_id values (this is the exact
    collision scenario in the issue).

Confirmed the new/updated tests fail without the fix:

$ git stash push -- src/google/adk/a2a/agent/_remote_a2a_agent.py src/google/adk/a2a/agent/config.py
$ python -m pytest tests/unittests/a2a/agent/test_remote_a2a_agent.py::TestRemoteA2aAgentExecution -q
...
FAILED ...::test_run_async_impl_uses_session_id_when_opted_in - AttributeError: module 'google.adk.a2a.agent._remote_a2a_agent' has no attribute '_namespaced_session_context_id'
FAILED ...::test_run_async_impl_namespaces_context_id_by_user - AttributeError: module 'google.adk.a2a.agent._remote_a2a_agent' has no attribute '_namespaced_session_context_id'
2 failed, 14 passed
$ git stash pop

With the fix applied:

$ python -m pytest tests/unittests/a2a/ -q
731 passed, 49 skipped, 42 warnings in 3.99s

Also ran pre-commit run --files <changed files> (ruff, isort, pyink,
addlicense, ADK compliance checks, doc-link checks, codespell): all passed.

AI-assistance disclosure

This change was authored with the help of an AI coding agent (Claude Code),
which investigated the issue, wrote the fix and the regression test, and ran
the test suite and pre-commit checks locally. All output was reviewed before
submission.

RemoteA2aAgent.forward_session_id_as_context_id forwarded the raw
ctx.session.id as the remote A2A context_id. Local ADK session identity
also includes user_id and app_name, so two distinct users (or apps) that
happen to reuse the same session ID were mapped to the same remote
context, letting one user's conversation history leak to another on a
remote server that keys state off context_id.

Derive the forwarded context_id from a hash of app_name, user_id, and
session id instead of the raw session id, so distinct local identities
never collapse onto the same remote context.

Fixes google#7305
@mynewestgitaccount

Copy link
Copy Markdown

a SHA-256 digest of app_name, user_id, and session.id

This change would largely kill the utility of the forwarding feature, which is to have a consistent id that can be easily correlated across agents. A single SHA-256 digest can't be decomposed into its component parts, so downstream there is no functional difference between sending this and a random string.

@chelsealong

Copy link
Copy Markdown
Contributor Author

Good point. I dropped the hash: the forwarded context_id is now app_name/user_id/session_id, each part percent-encoded, so it's still namespaced but readable and splittable downstream (_namespaced_session_context_id in _remote_a2a_agent.py; docstring in config.py updated). The test now asserts the exact value. Tests in tests/unittests/a2a pass (731 passed), and the updated test fails against the previous implementation. (AI-assisted change.)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RemoteA2aAgent can collide distinct users when forwarding session_id as context_id

3 participants