Skip to main content
New to the adapters? Start with the wrapping overview and comparison table.
Compatible versions: see the compatibility table for the verified google-adk range.
The Google ADK wrapper exposes its own HexgateRunner. It’s constructed up front with the agent, app name, and session service (mirroring the ADK Runner constructor) — the underlying ADK Runner is built once and reused since role resolution happens at call time. run / run_async then yield ADK events.

Under the hood

  • At construction, HexgateRunner calls wrap_google_agent, which builds a PolicySet, constructs one PolicyEnforcer, and returns agent.model_copy(update={"tools": guarded_tools}) — your original agent is untouched.
  • Each tool is normalized first: bare callables in agent.tools are wrapped into FunctionTool (matching what ADK does internally) so the guard has a stable BaseTool surface. Each tool is then copy.copy’d and its run_async replaced with an enforcer-gated version.
  • Each run / run_async call opens a HexgateContext scope (hexgate_context.sync_scope() / async with hexgate_context:) and dispatches to the cached underlying Runner. On non-allow, the guard returns decision.as_error_message() so the ADK runtime forwards it to the model as the tool output instead of aborting the run.
  • Observability is set up lazily on each call: GoogleADKInstrumentor().instrument() plus nest_asyncio.apply() (ADK’s runner spins its own loop), and the run executes inside propagate_attributes(user_id=..., session_id=..., metadata={"user_roles": ...}, tags=["google.runner.run.<agent_name>"]).

Agent-level enforcement

The Google runner has the fullest agent-level coverage, because ADK expresses both delegation modes as tool calls. It enforces admission at run entry, and reach at before_tool_callback for both transfer_to_agent (handoff, agent.handoff:<name>) and AgentTool (agent-as-tool, agent.tool:<name>) — a denied delegation is refused before it runs. Set HexgateRunner(max_handoff_depth=N) to cap a runaway handoff chain.

Skills

ADK ships agent skills through SkillToolset, and HexgateRunner discovers and gates them with no extra wiring:

What registration records

For each skill in a SkillToolset, the manifest records its name, description, the reference, asset and script files it ships, and a hash of its SKILL.md body. The skill’s allowed-tools frontmatter is recorded too. A skill whose frontmatter declares metadata.adk_additional_tools enlarges the agent’s callable tool surface once it is activated: ADK only exposes those tools to the model after the skill loads. Hexgate records them in the manifest’s tool list as well — under the name the model will call, toolset prefix included — so the generated policy covers them and they are gated like any other tool. A skill that declares additional tools is treated like one that ships scripts in the starter policy.

What is gated

Each SkillToolset tool maps to one level: list_skills is left alone on purpose: the skill catalog is already in the system prompt, so gating it would only hide what exists. The mapping is by tool class, so a tool_name_prefix on the toolset does not change it, and a tool of your own named load_skill is gated as an ordinary tool. A run_skill_script decision sees one spelling of the path — scripts/x and x both arrive as args.file_path == "scripts/x" — and the script’s invocation as args.script_args, args.short_options and args.positional_args, so a constraint can fence what a script is run with. Until the policy lists a skill under skills:, these are ordinary tool calls, decided under their tool names.

What a denial looks like

A denied or held skill comes back to the model as the tool result, marked like any other ADK denial:
The held wording names the level: before it is loaded (instructions), before its resource is read, before its script runs. The closing sentence is deliberate. Without it a model tends to improvise the procedure from memory — dropping exactly the guardrails the skill encoded.
How reliably models honour that sentence has not yet been observed and written up. Treat it as a nudge, not a guarantee: the tools the skill would have steered toward are still gated only by their own rules.

Caveats

  • Requires google-adk >= 1.25.0, where SkillToolset was added. Hexgate itself supports google-adk >= 1.14; on an older ADK there are no skills to discover and nothing changes.
  • SkillToolset is experimental upstream. Hexgate reaches some ADK internals to hash a skill’s body at decision time; if a future ADK moves them, the hash is omitted — and a pinned skill denies rather than passes.
  • Not containment. A denied load_skill hides the instructions; it does not stop the model calling the agent’s other tools. See Agent skills.

Runnable example

examples/devops_google.py — HexgateRunner (Google ADK) end-to-end with InMemorySessionService.