Metadata-Version: 2.5
Name: sondera-harness-adk
Version: 0.1.0.dev1281
Summary: Sondera Harness governance plugin for Google Agent Development Kit (ADK) 2.x agents.
Project-URL: Homepage, https://sondera.ai
Project-URL: Repository, https://github.com/sondera-ai/sondera-harness-python
Project-URL: Documentation, https://docs.sondera.ai
Author-email: Sondera <eng@sondera.ai>
License-Expression: MIT
License-File: LICENSE
Keywords: adk,agents,ai,google-adk,governance,guardrails,observability
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: google-adk>=2.2.0
Requires-Dist: google-genai>=1.0.0
Requires-Dist: sondera-harness-client>=0.2.0.dev0
Description-Content-Type: text/markdown

# sondera-harness-adk

Sondera Harness governance plugin for [Google ADK](https://google.github.io/adk-docs/)
2.x agents. Wraps every ADK callback point (user message, model, tool, run
lifecycle) with policy adjudication against the Sondera Platform.

The package's public surface is deliberately narrow: only
`SonderaHarnessPlugin` is exported from the top-level package.

## Installation

```bash
uv add sondera-harness-adk
```

## Quick Start

```python
from google.adk import Agent
from google.adk.runners import Runner

from sondera_harness_adk import SonderaHarnessPlugin
from sondera_harness_adk.harness import SonderaRemoteHarness

# Endpoint/token resolve from SONDERA_HARNESS_ENDPOINT / SONDERA_API_TOKEN
# (or ~/.sondera/env) when not passed explicitly.
harness = SonderaRemoteHarness()
plugin = SonderaHarnessPlugin(harness=harness)

agent = Agent(name="my-agent", model="gemini-2.5-flash", ...)
runner = Runner(agent=agent, app_name="my-app", plugins=[plugin])
```

`SonderaHarnessPlugin` accepts any `sondera_harness_adk.abc.Harness`
implementation, so custom harnesses (e.g. a local policy engine) can be
substituted for `SonderaRemoteHarness`. A custom harness must implement
`new_scope()` or be supplied as `harness_factory=...` so concurrent web
sessions never share mutable trajectory state.

`SonderaRemoteHarness.new_scope()` shallow-copies the configured harness so
subclass behavior, instance configuration, and the transport client carry into
each session while the trajectory identity is reset. Subclasses that add other
mutable per-trajectory fields must override `new_scope()` to reset them, or use
`harness_factory=...` to construct fully isolated scopes.

## Tool classification

A tool call is adjudicated as the narrowest action it fits. A tool whose name
and arguments identify a file operation, a shell command, or a web fetch is sent
as `FileOperation` / `ShellCommand` / `WebFetch`, so the policies written for
that class apply to it; its result is reported as the matching observation.
Everything else stays a generic `ToolCall`, which reaches Cedar only as
`Sondera::Action::"PreToolUse"`.

Reclassification requires the identifying argument as well as the name — a path
(or a patch body), a command string, or a URL. An ADK tool is an arbitrary
Python callable, so the name sets have to include bare verbs like `read` and
`create`; without the argument requirement an issue-tracker `create` would have
the file-write signature policies run against its body, which on a fail-closed
gate is a spurious deny rather than a mislabelled log line. Shell names are kept
deliberately narrow (`bash`, `shell`, `terminal`, `run_shell_command`, …)
because ADK has no canonical shell tool.

See `sondera_harness_adk.tool` for the full name and argument sets.

## ADK web and sub-agents

`adk web` constructs and caches the Runner, so register the plugin by exporting
an ADK `App` from `agents/<app_name>/agent.py`:

```python
app = App(
    name="support_triage",
    root_agent=root_agent,
    plugins=[SonderaHarnessPlugin(harness=SonderaRemoteHarness())],
)
```

The plugin analyzes the complete agent tree. The root card keeps the canonical
tool inventory used by policy enforcement and exposes typed flat sub-agent
descriptors. Events keep the root `Agent` as the policy principal and record the
currently executing agent separately as observational attribution.

One trajectory scope is retained per `(app, user, session)`. Fully consumed
Runner, FastAPI `/run`, and ADK Web `/run_sse` requests release their invocation
lease; shutdown then finalizes every idle scope. Direct Runner streams abandoned
before ADK runs its finalizer remain active and prevent finalization. Transport
disconnect behavior belongs to ADK's stream cleanup; the plugin only releases a
lease after receiving `after_run_callback`, so a missing callback stays visible
as an active fail-closed residual.

If initialization or finalization fails after a trajectory may have been
created or completed remotely, that session scope is quarantined. It cannot
adjudicate or initialize again; eviction or shutdown must successfully clean it
up before the capacity can be reused.

The remote adapter publishes a pending trajectory ID before sending `Started`
or `Resumed`. Transport errors cannot prove whether the server received the
event, so even a pre-send failure is conservatively quarantined and targeted by
the normal terminal cleanup path.

## License

MIT — see [LICENSE](LICENSE).
