Skip to content

Repository files navigation

LoopGrid × Google ADK

loopgrid-google-adk is a native Google Agent Development Kit plugin for recording signed, independently verifiable evidence around consequential AI-agent decisions.

It uses ADK's current BasePlugin lifecycle and App(plugins=[...]) integration model. LoopGrid records evidence; it does not execute refunds, payments, account mutations, or other external business actions.

Install

pip install loopgrid-google-adk

For local development:

pip install -e ".[dev]"

Why this is ADK-native

The plugin maps current ADK lifecycle surfaces into LoopGrid evidence:

ADK before_run_callback
    → decision_created

ADK after_model_callback
    → model_completed
    → policy_evaluated (only explicit application policy)

ADK before_tool_callback
    → tool_requested

ADK after_tool_callback
    → tool_result

application confirms external execution
    → plugin.record_action_executed(...)
    → tool_executed

application observes authoritative business result
    → plugin.observe_outcome(...)
    → outcome_observed

Why after_tool_callback is tool_result, not automatically tool_executed

ADK permits plugins and agent callbacks to short-circuit a tool call by returning a response before the underlying tool body is invoked. Therefore an after_tool_callback proves that ADK observed a tool result, but by itself does not prove how that result was produced.

LoopGrid intentionally records that framework fact as tool_result. When your application has authoritative evidence that the consequential action actually executed, call record_action_executed(...) explicitly.

This avoids manufacturing execution evidence.

Basic usage

from google.adk.agents import Agent
from google.adk.apps import App
from google.adk.runners import InMemoryRunner

from loopgrid_google_adk import LoopGridADKPlugin

plugin = LoopGridADKPlugin(
    authority={
        "scope": "refunds",
        "max_amount": 50,
        "currency": "USD",
    },
    policy={
        "policy_id": "refund-policy-v3",
        "decision": "auto_allowed",
    },
    proposed_action={
        "type": "refund",
        "amount": 25,
        "currency": "USD",
    },
    context={"prompt_version": "support-v7"},
)

root_agent = Agent(
    name="support_agent",
    model="gemini-2.5-flash",
    tools=[...],
)

app = App(
    name="support_app",
    root_agent=root_agent,
    plugins=[plugin],
)
runner = InMemoryRunner(app=app)

Per-invocation evidence

Prefer an explicit run binding:

run_id = plugin.bind_run(
    "case-123",
    authority={"scope": "refunds", "max_amount": 50},
    policy={"policy_id": "refund-policy-v3", "decision": "auto_allowed"},
    proposed_action={"type": "refund", "amount": 25, "currency": "USD"},
)

async for event in runner.run_async(
    user_id="customer-42",
    session_id=session.id,
    invocation_id=run_id,
    new_message=user_message,
):
    ...

Using the same value for bind_run(...) and ADK's invocation_id keeps application-owned evidence correlated without putting sensitive authority or policy values into model context.

ADK RunConfig.custom_metadata["loopgrid"] is also supported for non-secret metadata, but ADK attaches custom metadata to invocation/event state. Do not use that channel for credentials or secrets.

Explicit action and outcome

After your application has authoritative evidence that an external action ran:

await plugin.record_action_executed(
    run_id,
    {
        "tool": "refund-service",
        "external_ref": "refund_123",
        "sandbox": True,
        "real_money_moved": False,
    },
)

After the downstream system returns the authoritative result:

await plugin.observe_outcome(
    run_id,
    {
        "status": "sandbox_refunded",
        "external_ref": "refund_123",
        "sandbox": True,
        "real_money_moved": False,
    },
)

Privacy defaults

By default:

capture_content=False

Raw user content, model responses, tool arguments, tool results, and exception messages are not stored by this integration. SHA-256 commitments plus bounded metadata are recorded instead.

Raw content capture is explicit opt-in:

LoopGridADKPlugin(capture_content=True)

Failure mode

The default is fail-closed:

fail_open=False

If evidence cannot be recorded, the integration surfaces the error rather than silently running an evidence-required path without evidence.

For low-risk telemetry-only use cases, applications may opt into:

fail_open=True

Deterministic runtime validation

The repository includes a real ADK runtime test using a deterministic custom BaseLlm. It exercises:

real ADK App
→ real InMemoryRunner
→ real BasePlugin callbacks
→ custom deterministic BaseLlm
→ real function tool call
→ LoopGrid plugin lifecycle
→ final model turn

No Gemini key, Google Cloud project, paid billing account, or credit card is required for that runtime test.

Run:

python -m pytest -v
python -m compileall -q src examples tests

Then, with LoopGrid Core running:

python examples/refund_gate.py

The release gate is:

evidence_complete
100% applicable evidence coverage
verify.valid = true

The included refund example is sandbox-only and explicitly records real_money_moved=false.

Security and evidence boundaries

See:

License

Apache-2.0.

About

LoopGrid Google ADK Plugin — record and verify signed, tamper-evident evidence for consequential AI-agent decisions.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages