watchlight-claude-agent
Governed Claude Agent SDK agents with Watchlight: every tool call and every sub-agent launch is authorized before it runs, and refused on any doubt.
pip install watchlight-claude-agent
Independent third-party plugin. This is an independent integration built by Watchlight AI. It is not affiliated with, endorsed by, or sponsored by Anthropic.
Claude Agent SDKand related names are trademarks of Anthropic, used here nominatively only to describe compatibility.
What it enforces
The Python Claude Agent SDK has no plugin loader, so the plugin ships its enforcement as a hook map. plugin.governed_options(handle, options) (or plugin.governance_hooks(handle)) adds these hooks to your ClaudeAgentOptions:
- Every tool call goes through a
PreToolUsegate. The call is authorized on the handle of the agent that made it: a sub-agent's own handle for a sub-agent's call, the root handle otherwise. A deny, a needs-approval decision, an unreachable backend or any other error returns adenydecision, so the tool does not run. Only argument names and value types are sent for authorization, never argument values. - Sub-agent launches (the
Task/Agenttool) perform the governed spawn in the samePreToolUsegate, before the tool runs. The child gets its own credential with a scope that the server narrows to a subset of the parent's. When the spawn is refused, the sub-agent type is not a registered agent, or anything else goes wrong, the tool call is denied: the sub-agent never starts and never gets a model turn. - A sub-agent that starts without a governed spawn, or a tool call from an agent the plugin cannot identify while a sub-agent is running, is denied.
- A starting sub-agent binds to its governed spawn by tool-use id when the SDK provides one, otherwise by sub-agent type. The SDK's
SubagentStartinput carries no tool-use id, so a second launch of a type that is still starting is denied (the two could not be told apart), including when both launches are gated at the same moment: the first one reserves its slot before the gate does any work. A launch that never starts (a later hook or a permission prompt refused it) is closed onPostToolUse/PostToolUseFailure, atStop, or after 120 seconds. - Each
PreToolUsedecision has a 45-second deadline, inside the 60-second timeout the plugin sets on its hook matchers; a decision that is not made in time is a deny. - Run start fails closed.
start_runraisesAgentNotRegisteredfor an unknown agent andGovernanceUnavailablewhen the backend does not open the session. It also refuses the run, and closes the session it opened, when the backend issues no constraint manifest, unless the operator explicitly setWATCHLIGHT_PLAN_GOVERNANCE_MODE=degrade. - Egress. When a wl-proxy URL is configured (
connect_proxy_url=orWATCHLIGHT_PROXY_URL),start_runinstalls a local CONNECT proxy and setsHTTPS_PROXY/HTTP_PROXYfor this process, so Python HTTP clients that honour those variables go through wl-proxy. If the backend issued no proxy token, or the proxy cannot be installed,start_runraisesGovernanceUnavailableinstead of running with direct egress. Clients that ignoreHTTPS_PROXYare not covered.
What it does not do: model turns themselves are not authorized (only the tools they call), and plan validation (handle.submit_plan) runs only when you call it. Your own hooks are kept, but they run alongside Watchlight's: a deny from any hook stops the call, so they cannot turn a Watchlight deny into an allow.
The policy decisions run on Watchlight's compiled engine, either in-process for local development or against the governed control plane in production.
Quickstart
For local development, the zero-infrastructure Developer Edition runs the compiled engine in-process (requires the watchlight-engine package):
pip install watchlight-claude-agent watchlight-engine
from claude_agent_sdk import ClaudeAgentOptions, query
from watchlight_claude_agent import WatchlightClaudeAgentSDKPlugin
from watchlight_core import InProcessClient
# A Cedar policy: the research agent may execute tools. Anything not
# permitted is denied.
POLICIES = [
{"name": "tools",
"code": 'permit(principal == Agent::"research-agent", action == Action::"execute", resource);'},
]
plugin = WatchlightClaudeAgentSDKPlugin(governance=InProcessClient(POLICIES))
async def run_agent(question: str):
async with await plugin.start_run("research-agent") as handle:
# Every tool call and sub-agent launch now goes through Watchlight.
options = plugin.governed_options(handle, ClaudeAgentOptions())
async for message in query(prompt=question, options=options):
print(message)
Each tool call is authorized as action execute on a resource named after the tool (for example Read, Bash, mcp__server__tool).
Sub-agents must be registered Watchlight agents (the subagent_type of the Task call is resolved to a registered agent). Their requested scope comes from default_subagent_scope= (or WATCHLIGHT_DEFAULT_SUBAGENT_SCOPE); when that is empty the sub-agent starts with no tools and its first tool call is denied.
Attributing runs to an AI application
Every governed run can be attributed to the AI application the agent belongs to. Pass application_id="<application UUID>" to WatchlightClaudeAgentSDKPlugin(...), or per run to start_run(...), or set WATCHLIGHT_APPLICATION_ID in the environment — an explicit argument wins over the env var. The id is sent on session create and bound to the run credential and to every sub-agent the run spawns; wl-apdp refuses the session if the agent is not a member of that application. An agent in exactly one application needs nothing here. The id is explicit configuration, never inferred from prompts or names, and it is an Enterprise feature: the in-process Developer Edition engine refuses it.
Two backends, same code
| Backend | Runs | |
|---|---|---|
| Developer Edition | InProcessClient |
The compiled engine, in-process — no server, no network |
| Enterprise | ApdpClient |
The governed control plane — signed lineage, drift detection, fleet-wide governance |
Moving from local to production is a one-line change:
import os
from watchlight_core import ApdpClient
# Read the API key from the environment or your secret manager; never
# write it into source code.
plugin = WatchlightClaudeAgentSDKPlugin(
governance=ApdpClient(
"https://apdp.your-company.example",
api_key=os.environ["WATCHLIGHT_API_KEY"],
)
)
Links
- Documentation: https://docs.watchlight.ai/de
- Website: https://watchlight.ai
License
Apache-2.0
Metadata
Release files for watchlight-claude-agent 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| watchlight_claude_agent-0.2.1.tar.gz | 57.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| watchlight_claude_agent-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 93.6 kB
Release files / watchlight_claude_agent-0.2.1.tar.gz
| Download URL | watchlight_claude_agent-0.2.1.tar.gz |
|---|---|
| Size | 57.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0294d0d516210666fba611215c097e3fae1f65c331d5dcc5217bd545f9cf9e2a
|
|
BLAKE2b-256 checksum How to use checksums |
e0a134ed3a4037e18d35091258b1654ec6eb1afa048b8ba5cb43c9a7c06a07d3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency logRelease files / watchlight_claude_agent-0.2.1-py3-none-any.whl
| Download URL | watchlight_claude_agent-0.2.1-py3-none-any.whl |
|---|---|
| Size | 36.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e156fc6545b10e2fb48fe640ddd9c83b3e6af99f3fea312dd3ed103f5d4b3c27
|
|
BLAKE2b-256 checksum How to use checksums |
09dd2d2ac9dd3c6c44df0957410f62889fe226d96899a40117c6d1aacf06c726
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency log