Is your feature request related to a specific problem?
Yes. ADK's observability answers what happened: OpenTelemetry spans for agent invocation, model calls, and tool execution; persisted session Events for function calls, responses, and confirmation actions; the BigQuery Agent
Analytics plugin streaming structured events at the runner level.
Incident response and compliance for production agents need a different artifact, what was decided:
- why this tool was selected for this step
- under which authority the call ran (agent-auth vs user-auth, and which principal)
- what a guardrail or plugin refused, and on what rule
- what confirmation was required, and what approval made the action permissible
Each of those decisions passes through an ADK surface (plugin callbacks, tool confirmation, per-tool auth config), but no component writes a unified record of them. A team with all five security layers configured still cannot reconstruct, from framework output alone, the moment an agent crossed from suggestion into action.
Credit to Alex Shev for naming this in a community discussion of indirect prompt injection defenses: the layer most often missing is the decision ledger between policy and action.
Describe the Solution You'd Like
An opt-in, runner-level decision ledger plugin (so every agent on a runner inherits it) that emits one structured, persistable entry per decision point: tool call, refusal, confirmation request, confirmation resolution. Deterministic fields (authority, policy outcome, confirmation payload) are attested by the framework; the model's stated rationale is recorded but typed as untrusted, since a hijacked agent will narrate a plausible justification. Schema and pseudo-code below.
To my knowledge, no framework persists this unified record today. In ADK all four decision surfaces already exist, so this is integration work, not new machinery.
Impact on your work
I am a Google Developer Expert for AI producing developer education on ADK agent security. The recurring question from production teams in those discussions is not "how do I block the attack" but "how do I reconstruct what my agent decided when something gets through." Today my honest answer is that every team must build its own ledger from plugin callbacks, and none of those one-off schemas interoperate. A first-class answer would close the most common gap I see between teams configuring ADK's security layers and teams being able to audit them. No hard timeline; this is an ecosystem maturity request, not a personal blocker.
Willingness to contribute
Yes. I am willing to contribute the plugin implementation and a schema RFC if maintainers are open to the direction.
Describe Alternatives You've Considered
- Persisted session Events: capture function calls, responses, and confirmation actions with timestamps, but not the authority a tool ran under, the rule behind a refusal, or selection rationale. The ledger is the decision-semantics layer above the event log.
- BigQuery Agent Analytics plugin: the closest existing component, registered once at the app level and structured, with dedicated HITL confirmation event types. It does not capture tool-selection rationale, logs tool_origin rather than the agent-auth/user-auth principal, and records generic TOOL_ERROR rather than policy refusals with rules.
- Model Armor logging to Cloud Logging / SCC: records which policy a blocked request triggered, but only for Model Armor's own screenings, in a sink disconnected from the agent trace.
- Cloud Audit Logs: capture the principal for GCP API calls only; nothing for arbitrary tools, and no framework semantics.
- Third-party attestation tools (signed receipts, hash-chained audit trails, policy evaluators): a real ecosystem exists, with several incompatible schemas, and all of them attest records the framework never emits. They can sign what happened; only the framework sees why. This request is for the emission point; the existing ecosystem can consume it.
- Per-team custom plugins: possible today via before/after tool callbacks, confirmation payloads, and declared per-tool auth, which is the argument for making it first-class: every production team re-derives the same schema and none of them interoperate.
- Prior art in other frameworks, none complete: AWS Bedrock Agents record per-step rationale and guardrail assessments in an ephemeral trace with identity held elsewhere; the Claude Agent SDK computes structured allow/deny/ask permission decisions without persisting them; LangGraph persists human-interrupt approvals in checkpoint history.
Proposed API / Implementation
Registration, following the existing runner-level plugin pattern:
from google.adk.plugins import DecisionLedgerPlugin # proposed
runner = InMemoryRunner(
agent=root_agent,
app_name="my_app",
plugins=[
DecisionLedgerPlugin(
sink=..., # BigQuery, Cloud Logging, file
record_model_rationale=True, # always typed untrusted
),
],
)
One ledger entry per decision point:
{
"invocation_id": "...",
"step": 3,
"decision": "tool_call | tool_refused | confirmation_required | confirmation_resolved",
"tool": "...",
"authority": {"mode": "agent-auth | user-auth", "principal": "..."},
"policy": {"source": "plugin | tool_context | confirmation", "rule": "...", "outcome": "allow | deny | escalate"},
"confirmation": {"required": true, "response_payload": "..."},
"model_rationale": {"text": "...", "trust": "untrusted_generated"},
"timestamp": "..."
}
Implementation sketch: the plugin populates entries from surfaces that already exist: before/after tool callbacks (tool, args, step), the declared per-tool auth config (authority), plugin/callback block decisions (policy source, rule, outcome), tool confirmation responses (confirmation payload), and the model's stated reasoning from the model response that produced the tool call (rationale, typed untrusted).
Additional Context
Related prior items, scoped against deliberately: #5202, #5201, #5164 (audit trails and signed receipts of what was called, all closed) and open Discussion #5090 (execution provenance exporter, which covers principal and approval references but not selection rationale, refusal rules, or a unified schema). This request is for the decision-semantics layer those items stop short of.
Is your feature request related to a specific problem?
Yes. ADK's observability answers what happened: OpenTelemetry spans for agent invocation, model calls, and tool execution; persisted session Events for function calls, responses, and confirmation actions; the BigQuery Agent
Analytics plugin streaming structured events at the runner level.
Incident response and compliance for production agents need a different artifact, what was decided:
Each of those decisions passes through an ADK surface (plugin callbacks, tool confirmation, per-tool auth config), but no component writes a unified record of them. A team with all five security layers configured still cannot reconstruct, from framework output alone, the moment an agent crossed from suggestion into action.
Credit to Alex Shev for naming this in a community discussion of indirect prompt injection defenses: the layer most often missing is the decision ledger between policy and action.
Describe the Solution You'd Like
An opt-in, runner-level decision ledger plugin (so every agent on a runner inherits it) that emits one structured, persistable entry per decision point: tool call, refusal, confirmation request, confirmation resolution. Deterministic fields (authority, policy outcome, confirmation payload) are attested by the framework; the model's stated rationale is recorded but typed as untrusted, since a hijacked agent will narrate a plausible justification. Schema and pseudo-code below.
To my knowledge, no framework persists this unified record today. In ADK all four decision surfaces already exist, so this is integration work, not new machinery.
Impact on your work
I am a Google Developer Expert for AI producing developer education on ADK agent security. The recurring question from production teams in those discussions is not "how do I block the attack" but "how do I reconstruct what my agent decided when something gets through." Today my honest answer is that every team must build its own ledger from plugin callbacks, and none of those one-off schemas interoperate. A first-class answer would close the most common gap I see between teams configuring ADK's security layers and teams being able to audit them. No hard timeline; this is an ecosystem maturity request, not a personal blocker.
Willingness to contribute
Yes. I am willing to contribute the plugin implementation and a schema RFC if maintainers are open to the direction.
Describe Alternatives You've Considered
Proposed API / Implementation
Registration, following the existing runner-level plugin pattern:
One ledger entry per decision point:
{ "invocation_id": "...", "step": 3, "decision": "tool_call | tool_refused | confirmation_required | confirmation_resolved", "tool": "...", "authority": {"mode": "agent-auth | user-auth", "principal": "..."}, "policy": {"source": "plugin | tool_context | confirmation", "rule": "...", "outcome": "allow | deny | escalate"}, "confirmation": {"required": true, "response_payload": "..."}, "model_rationale": {"text": "...", "trust": "untrusted_generated"}, "timestamp": "..." }Implementation sketch: the plugin populates entries from surfaces that already exist: before/after tool callbacks (tool, args, step), the declared per-tool auth config (authority), plugin/callback block decisions (policy source, rule, outcome), tool confirmation responses (confirmation payload), and the model's stated reasoning from the model response that produced the tool call (rationale, typed untrusted).
Additional Context
Related prior items, scoped against deliberately: #5202, #5201, #5164 (audit trails and signed receipts of what was called, all closed) and open Discussion #5090 (execution provenance exporter, which covers principal and approval references but not selection rationale, refusal rules, or a unified schema). This request is for the decision-semantics layer those items stop short of.