Repository navigation
Integrations Google ADK
Experimental: this works and is tested, but may change and gets less hardening than the stable core.
tracekit.integrations.google_adk.Tracekit is a Google ADK plugin that
gates every tool call of every agent its Runner runs, sub-agents and agents run as tools included: its
before_tool_callback asks the Tracekit signer for a decision before the tool runs (an ask waits for a person), and
its after_tool_callback and on_tool_error_callback record the outcome. ADK's own code is unchanged.
pip install tracekit-ai "google-adk>=2.12,<2.13"Tested with Google ADK 2.12, which needs Python 3.11+.
from google.adk.agents import LlmAgent
from google.adk.apps import App
from google.adk.runners import InMemoryRunner
from tracekit.integrations.google_adk import Tracekit
from tracekit.sdk.client import Client
with Client().run(agent="support-agent") as run:
agent = LlmAgent(name="support", model="gemini-flash-latest", tools=[close_account])
runner = InMemoryRunner(app=App(name="support", root_agent=agent, plugins=[Tracekit(run)]))
... # runner.run_async(...) as usualConstruct the plugin yourself: tracekit.instrument() doesn't add it. An AsyncRunHandle works as well as a
RunHandle. List it first in plugins (a warning says so when it isn't): ADK runs plugin callbacks in that order and
stops at the first that answers. Then tracekit last exports and verifies the run. A runnable offline example (a
scripted model, no API key): examples/v2/google_adk.
-
Decides the model's arguments, under ADK's call id. The signer decides the call's arguments as the model sent
them (
{}for none) undertool_context.function_call_id(the model's own id, or theadk-...id ADK gives a call without one). An approval is bound to those arguments and used once. Under the executor (T2), a call whose approved arguments are not the model's is refused, not run with either. -
Decides before the agent's own callbacks. ADK runs the plugins'
before_tool_callbackin order, then the agent's. One listed after Tracekit that changes the arguments in place does it after the decision: the call runs, and is recorded asstatus: error, "the call's arguments changed after the decision". One that answers the call instead of the tool (a cache) is recorded with its answer. -
Never runs a refused call. A deny, a rejected or unanswered approval, a refused consume, a signer refusal, an
outage under a closed fail mode and any error of Tracekit's own all answer the call with
{"error": <reason>}, so ADK skips the tool (and its own tool confirmation): the model reads Tracekit's reason as the tool's result and the run goes on. Under an open fail mode a call runs unrecorded during an outage, with a warning. -
Records the result the model reads. A tool's return is recorded as the JSON ADK sends the model (a dict as it
is, anything else as
{"result": ...}, a model or dataclass as its fields), so an untrusted tool's values reach provenance. A tool that raises is recorded asstatus: errorand its exception reaches ADK unchanged, soon_tool_error_callbackhandlers andReflectAndRetryToolPluginwork as before; the model's retry is a new call id and a new decision. -
Covers ADK's tool paths. Function tools (sync and async, parallel calls), sub-agents (the
transfer_to_agentcall that hands over to one, and the sub-agent's own tool calls), agents run as tools (AgentTool: the call and the agent's own tool calls, since it inherits the Runner's plugins), MCP toolsets (under ADK's tool name), long-running tools, workflow tool nodes (under the id ADK gives each run of the node) and a tool the model made up (recorded as an error). Handing a task to a task-mode sub-agent is the exception: see Not covered. -
Waits for people. An
askwaits up to 5 minutes (the signer's cap for one wait) before the call; then it is skipped with the approval's state, and an unanswered approval is withdrawn. So is one whose agent run is cancelled while it waits. -
Keeps ADK's own pauses. A call ADK pauses for the user, for its tool confirmation (
require_confirmation,tool_context.request_confirmation()) or for credentials (tool_context.request_credential()), is not completed. Once the user answers, ADK runs it again under the same call id and Tracekit decides it again as the call's nextattempt; the first attempt shows asdecided_not_completedonrun.final. A call the user rejects in ADK's tool confirmation, which ADK answers without running the tool, is recorded asstatus: error, "rejected in ADK's tool confirmation"; a tool that asks for confirmation itself and runs on a rejection is recorded with its result.
The plugin records each model response event (on_event_callback), once ADK has given its function calls their
ids: thought parts (summaries, with include_thoughts=True in the agent's ThinkingConfig), each thought_signature
(sealed), text, and the function calls under the ids the plugin decides them by (not ADK's own adk_request_* calls).
The message itself is kept only when the signer's content vault is on; without it the record holds a commitment. A message that can't be recorded only warns. Which frameworks and providers return readable reasoning, and how to turn it on: reasoning.
- Handing a task to a task-mode sub-agent (
LlmAgent(mode="task")in a chat agent'ssub_agents) is not decided: ADK runs the sub-agent itself, outside the tool callbacks, so a policy rule on the sub-agent's name doesn't apply. The sub-agent's own tool calls are decided. A plugin can't refuse it cleanly: an answer frombefore_agent_callbackends the turn with the hand-off unanswered, and ADK gives every later turn back to the refused sub-agent. - The agent's own
before_tool_callbackruns after the decision: arguments it rewrites in place run undecided (the call is recorded asstatus: error, "the call's arguments changed after the decision"), and an answer it returns instead of the tool is recorded asok, as if the tool ran. - ADK's tool confirmation is not a Tracekit approval: the signer records only its effect (the call runs again, or is
recorded as rejected). A tool under both a Tracekit
askand ADK's confirmation is decided at each of its two runs, so a person approves it in Tracekit twice. Use one, Tracekit'saskfor an approval in the evidence. - The attempt count lives in the plugin: a paused call that another process resumes (a new Runner over the same
session) is decided as attempt 0 again. An allowed call runs; one under an
askis refused, its approval used. - Code the agent's
code_executorruns, and builtin tools the model provider runs (Google Search, URL context, code execution on the provider's side), never reach the tool callbacks: they are not decided. -
AgentTool(agent, include_plugins=False)runs its agent without the Runner's plugins: that agent's tools are not gated. - A long-running tool is recorded with what its function returned (its pending status): the final result your app
sends later is not a tool run. Likewise a streaming tool in a live run (
run_live) is recorded with the pending status ADK gives the model, not what it streams after. - A plugin listed before Tracekit can change a call's arguments before the decision, answer its error (recorded as an
okresult) or replace its result (the call then readsdecided_not_completed). A result a later plugin's or the agent'safter_tool_callbackreplaces is recorded as the tool returned it. - A tool that changes its own arguments in place (appends to a list it was given) is recorded as changed after the
decision.
ComputerUseTooldoes this to every coordinate action: it rescalesx,y,destination_xanddestination_yin its arguments. - The
payloada user sends with ADK's tool confirmation (tool_context.tool_confirmation.payload) is input the tool reads that Tracekit never decided. - With the gateway's verified tool results, a result is recorded as the tool
callbacks get it (
{"result": ...}around anything but a dict), not as each model class sends it: underAnthropicLlmevery result but a dict (and a dict with acontentkey or onlyresult) reads asresult_mismatch, underLiteLlma model or dataclass return (ADK sends its Python repr). Gemini requests, ADK's default, don't pass through the gateway, so nothing is checked. - Each signer call, and an
ask's wait, holds a thread of asyncio's default executor; many asks waiting at once can starve it.
Generated from docs/ at v1.4.1. Edit the docs in the repository, not here.
Get started
- Quickstart: the v2 signer
- Quickstarts
- Quickstart: Claude Agent SDK
- Quickstart: a custom agent
- Quickstart: LangChain and LangGraph
- Quickstart: MCP client
- Quickstart: OpenAI Agents SDK
- Coding agents other than Claude Code
- tracekit doctor
- Walkthrough: Tracekit end to end
How it works
- How Tracekit works
- Overview
- The life of a tool call
- The evidence format
- Verification and assurance
- Witnesses and monitoring
- Policy
- Approvals
- Detection and containment
- Capture and completeness
- Custody
- Provenance and tracekit why
- Deployment and isolation
- Storage, durability and performance
- Integrations
- Security model
- Architecture
- tracekit why: architecture
Integrations
- Integrations
- Adapters
- OpenTelemetry
- Remote clients
- Kubernetes agent-sandbox
- agentgateway
- agentsh and Tracekit
- Browser Use
- Claude Agent SDK
- Claude Code's sandbox
- Codex's sandbox
- CrewAI
- Docker MCP Gateway
- E2B
- Falco
- Gemini CLI's sandbox
- Google ADK
- Haystack
- LangChain v1
- LiteLLM
- LlamaIndex
- MCP client
- microsandbox
- Microsoft Agent Framework
- OpenAI Agents SDK
- NVIDIA OpenShell
- Pydantic AI
- sandbox-runtime's proxy on its own
- Strands Agents
- Tetragon
- Vercel AI SDK
- Wassette
Policy and approvals
- Policy reference
- Approvals
- Findings
- Reading the model's reasoning
- Incidents and containment
- Webhooks
Deploy and operate
- Deploying the signer
- The signer as a container
- The signer on Kubernetes
- The compose stack
- Identity
- Tool executor
- MCP gateway
- LLM gateway
- Egress observation
- Witnesses
- Log monitor
- Record-key issuer
- Signer observability
- Viewer
- tracekit why: causal investigation and replay
- Migrating
- Platforms
Verify the evidence
- Reading a v2 verify report
- Evidence format v2
- Auditor guide
- Sample evidence bundle
- Signing
- What Tracekit guarantees
Security and privacy
- Threat model: the v2 signer
- Threat model: the v1 laptop signer
- HTTP security checklist
- Threat-model review packet
- Privacy
- Known limits and trade-offs
- What's stable
Compliance
- Compliance mapping
- EU AI Act
- ISO/IEC 42001
- NIST AI RMF and the Generative AI Profile
- OWASP Top 10 lists for LLM and agentic applications
Evaluation
Demos
- Demos
- Agent approvers within bounds
- Completeness and tamper evidence
- Detection and containment
- Network coverage: an egress observer
- Guardrails: allow, deny, or ask a person
- MCP tools, gated and recorded
- Prompt injection held by provenance
- Why: which input caused the action
Help