BigQueryAgentAnalyticsPlugin: tool_origin not set to "A2A" when RemoteA2aAgent is used via sub_agents
Summary
When a RemoteA2aAgent is registered via sub_agents=[RemoteA2aAgent(...)] on an LlmAgent,
the BigQueryAgentAnalyticsPlugin logs tool_origin = "TRANSFER_AGENT" instead of "A2A".
This makes it impossible to distinguish A2A remote calls from regular local agent transfers in
observability data.
Workaround: Wrap the RemoteA2aAgent in AgentTool and pass it via tools=[]:
from google.adk.tools.agent_tool import AgentTool
root_agent = Agent(
tools=[AgentTool(agent=agent2_server), ...] # tool_origin = "A2A" ✅
)
AgentTool extends BaseTool, so it passes Pydantic validation. _get_tool_origin then
hits the isinstance(tool, AgentTool) and isinstance(tool.agent, RemoteA2aAgent) branch
and returns "A2A" correctly.
Steps to Reproduce
agent2_server = RemoteA2aAgent(
name="agent2_server",
agent_card="http://localhost:8001/a2a/app/.well-known/agent-card.json"
)
# Path A — sub_agents (idiomatic, broken observability)
root_agent = Agent(
sub_agents=[agent2_server], # tool_origin = "TRANSFER_AGENT" ❌
plugins=[BigQueryAgentAnalyticsPlugin(...)]
)
# Path B — tools=[AgentTool(agent=...)] (workaround, works)
from google.adk.tools.agent_tool import AgentTool
root_agent = Agent(
tools=[AgentTool(agent=agent2_server), ...], # tool_origin = "A2A" ✅
plugins=[BigQueryAgentAnalyticsPlugin(...)]
)
Query to confirm (run against the raw events table):
SELECT
event_type,
agent,
JSON_VALUE(content, '$.tool') AS tool_name,
JSON_VALUE(content, '$.tool_origin') AS tool_origin,
FORMAT_TIMESTAMP("%H:%M:%S", timestamp) AS time
FROM `<project_id>.<dataset_id>.<table_id>`
WHERE event_type IN ('TOOL_STARTING', 'TOOL_COMPLETED')
ORDER BY timestamp DESC
LIMIT 20
| Configuration |
Tool class seen by plugin |
tool_origin |
Works? |
sub_agents=[RemoteA2aAgent(...)] |
TransferToAgentTool(agent_names=["name"]) |
"TRANSFER_AGENT" |
✅ runs, ❌ wrong origin |
tools=[AgentTool(agent=RemoteA2aAgent(...))] |
AgentTool(agent=RemoteA2aAgent) |
"A2A" |
✅ runs, ✅ correct origin |
Root Cause
Two-layer issue:
1. TransferToAgentTool discards agent object references.
In flows/llm_flows/agent_transfer.py:51, when building the tool for sub-agent transfers,
only the agent names (strings) are passed — not the agent objects:
transfer_to_agent_tool = TransferToAgentTool(
agent_names=[agent.name for agent in transfer_targets] # objects discarded here
)
2. _get_tool_origin cannot introspect TransferToAgentTool for A2A targets.
In plugins/bigquery_agent_analytics_plugin.py:197-204, the check for TransferToAgentTool
fires unconditionally before the A2A check, with no way to know the target is a
RemoteA2aAgent:
def _get_tool_origin(tool):
if isinstance(tool, TransferToAgentTool):
return "TRANSFER_AGENT" # always fires for sub_agents, even for A2A targets
if isinstance(tool, AgentTool):
if isinstance(tool.agent, RemoteA2aAgent):
return "A2A" # only reachable via AgentTool(agent=RemoteA2aAgent(...))
Proposed Fix (for the underlying bug)
Option A (minimal — 3 files):
tools/transfer_to_agent_tool.py — store agent objects alongside names:
def __init__(self, agent_names: list[str], agents: list[BaseAgent] | None = None):
super().__init__(func=transfer_to_agent)
self._agent_names = agent_names
self._agents = agents or []
flows/llm_flows/agent_transfer.py — pass agent objects when constructing the tool:
transfer_to_agent_tool = TransferToAgentTool(
agent_names=[agent.name for agent in transfer_targets],
agents=transfer_targets,
)
plugins/bigquery_agent_analytics_plugin.py — check for A2A targets in _get_tool_origin:
if isinstance(tool, TransferToAgentTool):
if (RemoteA2aAgent is not None
and hasattr(tool, '_agents')
and any(isinstance(a, RemoteA2aAgent) for a in tool._agents)):
return "A2A"
return "TRANSFER_AGENT"
Option B (cleaner): Introduce a TransferToA2aAgentTool subclass for A2A transfers,
handled as a dedicated case in _get_tool_origin.
Expected Behavior
tool_origin = "A2A" should be logged whenever a RemoteA2aAgent is invoked, regardless
of whether it was registered via sub_agents or tools=[AgentTool(agent=...)].
Environment
google-adk version: 1.28.0
BigQueryAgentAnalyticsPlugin version: same
BigQueryAgentAnalyticsPlugin:
tool_originnot set to"A2A"whenRemoteA2aAgentis used viasub_agentsSummary
When a
RemoteA2aAgentis registered viasub_agents=[RemoteA2aAgent(...)]on anLlmAgent,the
BigQueryAgentAnalyticsPluginlogstool_origin = "TRANSFER_AGENT"instead of"A2A".This makes it impossible to distinguish A2A remote calls from regular local agent transfers in
observability data.
Workaround: Wrap the
RemoteA2aAgentinAgentTooland pass it viatools=[]:AgentToolextendsBaseTool, so it passes Pydantic validation._get_tool_originthenhits the
isinstance(tool, AgentTool) and isinstance(tool.agent, RemoteA2aAgent)branchand returns
"A2A"correctly.Steps to Reproduce
Query to confirm (run against the raw events table):
tool_originsub_agents=[RemoteA2aAgent(...)]TransferToAgentTool(agent_names=["name"])"TRANSFER_AGENT"tools=[AgentTool(agent=RemoteA2aAgent(...))]AgentTool(agent=RemoteA2aAgent)"A2A"Root Cause
Two-layer issue:
1.
TransferToAgentTooldiscards agent object references.In
flows/llm_flows/agent_transfer.py:51, when building the tool for sub-agent transfers,only the agent names (strings) are passed — not the agent objects:
2.
_get_tool_origincannot introspectTransferToAgentToolfor A2A targets.In
plugins/bigquery_agent_analytics_plugin.py:197-204, the check forTransferToAgentToolfires unconditionally before the A2A check, with no way to know the target is a
RemoteA2aAgent:Proposed Fix (for the underlying bug)
Option A (minimal — 3 files):
tools/transfer_to_agent_tool.py— store agent objects alongside names:flows/llm_flows/agent_transfer.py— pass agent objects when constructing the tool:plugins/bigquery_agent_analytics_plugin.py— check for A2A targets in_get_tool_origin:Option B (cleaner): Introduce a
TransferToA2aAgentToolsubclass for A2A transfers,handled as a dedicated case in
_get_tool_origin.Expected Behavior
tool_origin = "A2A"should be logged whenever aRemoteA2aAgentis invoked, regardlessof whether it was registered via
sub_agentsortools=[AgentTool(agent=...)].Environment
google-adkversion: 1.28.0BigQueryAgentAnalyticsPluginversion: same