agentgovern 2.0.2__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentgovern-2.0.2 → agentgovern-2.2.0}/.gitignore +10 -0
- agentgovern-2.2.0/CHANGELOG.md +174 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/PKG-INFO +3 -3
- agentgovern-2.2.0/agentgovern/_version.py +1 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/exceptions.py +111 -111
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/gate2.py +202 -202
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/hook/cli.py +8 -2
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/hook/data/hook.py +538 -42
- agentgovern-2.2.0/agentgovern/hook/drift.py +234 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/hook/installer.py +146 -22
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/hook/settings.py +20 -1
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/instrumentors/__init__.py +4 -4
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/instrumentors/_base.py +545 -545
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/instrumentors/langchain.py +411 -411
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/instrumentors/langgraph.py +684 -684
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/types.py +114 -114
- {agentgovern-2.0.2 → agentgovern-2.2.0}/examples/langgraph_credit_decision_agent.py +189 -189
- {agentgovern-2.0.2 → agentgovern-2.2.0}/pyproject.toml +2 -2
- agentgovern-2.2.0/tests/hook/test_drift.py +235 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/hook/test_hook_v2_context_snapshot.py +3 -2
- agentgovern-2.2.0/tests/hook/test_hook_v2_outbox_queue.py +791 -0
- agentgovern-2.2.0/tests/hook/test_hook_v2_payload_extraction.py +66 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/hook/test_hook_v2_payloads.py +116 -0
- agentgovern-2.2.0/tests/hook/test_hook_v2_remediation_message.py +134 -0
- agentgovern-2.2.0/tests/hook/test_hook_v2_timing_log.py +70 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/hook/test_installer.py +229 -7
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_base_instrumentor.py +388 -388
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_client.py +210 -210
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_gate2_full_path.py +255 -255
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_gate2_latency.py +86 -86
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_gate2_verdict_handler.py +258 -258
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_instrument_langchain.py +131 -131
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_langchain.py +193 -193
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_langchain_gate1.py +155 -155
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_langchain_gate2_callbacks.py +293 -293
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_langgraph.py +686 -686
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_output_capture_contract.py +276 -276
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_payload_schema.py +231 -231
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_timestamp.py +81 -81
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_version_consistency.py +34 -34
- agentgovern-2.0.2/CHANGELOG.md +0 -67
- agentgovern-2.0.2/agentgovern/_version.py +0 -1
- {agentgovern-2.0.2 → agentgovern-2.2.0}/LICENSE +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/README.md +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/RELEASING.md +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/__init__.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/__main__.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/client.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/hook/__init__.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentgovern/hook/data/__init__.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentshield/__init__.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentshield/client.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/agentshield/types.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/__init__.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/hook/__init__.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/hook/conftest.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/hook/test_bundled_hook.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/hook/test_hook_v2_dispatch.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/hook/test_hook_v2_transcript_reading.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/hook/test_hook_v2_turn_index.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/hook/test_hook_v2_warn_verdict.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/hook/test_settings.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_evaluate_input.py +0 -0
- {agentgovern-2.0.2 → agentgovern-2.2.0}/tests/test_rate_limit_handling.py +0 -0
|
@@ -34,3 +34,13 @@ test-results/
|
|
|
34
34
|
.claude/settings.local.json
|
|
35
35
|
|
|
36
36
|
.vercel
|
|
37
|
+
|
|
38
|
+
# Ad-hoc test fixtures with decoy/fake secret-shaped content (never real).
|
|
39
|
+
decoy.env
|
|
40
|
+
decoy_notebook.ipynb
|
|
41
|
+
decoy_multiedit.md
|
|
42
|
+
|
|
43
|
+
.env*
|
|
44
|
+
|
|
45
|
+
# Demo scaffolding (fixtures, decoy secrets for demo scripts) — not product code.
|
|
46
|
+
demo-fixtures/
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2.2.0 — 2026-08-11
|
|
4
|
+
|
|
5
|
+
### Fix — capture-only hook events (UserPromptSubmit/PostToolUse/Stop) no longer block on a discarded synchronous verdict; async audit-event parity (NAGT-137)
|
|
6
|
+
|
|
7
|
+
`AGENTGOVERN_HOOK_MODE`'s effect is now per-event, not global:
|
|
8
|
+
|
|
9
|
+
- **`UserPromptSubmit`/`PostToolUse`/`Stop`** are capture-only — the hook
|
|
10
|
+
never reads their ingest response for control flow — and now default to
|
|
11
|
+
`evaluation_mode: async` regardless of `AGENTGOVERN_HOOK_MODE`, so they no
|
|
12
|
+
longer pay the policy-engine's synchronous round trip (previously up to
|
|
13
|
+
the full `AGENTGOVERN_HOOK_TIMEOUT_MS`/shell timeout on every turn
|
|
14
|
+
boundary and every tool call). Set `AGENTGOVERN_HOOK_MODE=sync` explicitly
|
|
15
|
+
to force a synchronous verdict on these three as well (debugging).
|
|
16
|
+
- **`PreToolUse` always requests `sync` now** and always enforces the
|
|
17
|
+
verdict it gets back — it is the only event that gates whether a tool
|
|
18
|
+
runs, so its verdict was never safe to discard. **Behavior change:**
|
|
19
|
+
`AGENTGOVERN_HOOK_MODE=async` can no longer make `PreToolUse`
|
|
20
|
+
non-blocking; it could in 2.1.0 and earlier. This was documented (not
|
|
21
|
+
merely latent) — `packages/claude-hook/README.md`'s env-var reference
|
|
22
|
+
table and its rate-limit troubleshooting note both described `async` as
|
|
23
|
+
applying uniformly to all events — both updated in this release to
|
|
24
|
+
reflect the new per-event behavior.
|
|
25
|
+
- **Server-side (`apps/web`):** `evaluateInteractiveAsync` — already the
|
|
26
|
+
default path for all SDK-originated traffic (`evaluation_mode` defaults
|
|
27
|
+
to `async`) — previously never parsed the policy engine's top-level
|
|
28
|
+
`verdict`/`review_id` fields, so any tenant with an enforce-mode policy
|
|
29
|
+
got a generic `interactive_action.failed` audit event with no
|
|
30
|
+
`review_id` and no block-vs-escalate distinction, for every finding.
|
|
31
|
+
`evaluateInteractiveAsync` now fires the same `interactive_action.blocked`
|
|
32
|
+
/`.warned` (with `review_id`) events `evaluateInteractiveSyncAndRespond`
|
|
33
|
+
already did. **Behavior change:** `interactive_action.failed` no longer
|
|
34
|
+
fires for a verdict-bearing block outcome under async — it's retained
|
|
35
|
+
only as the fallback for a malformed/unparseable engine response.
|
|
36
|
+
Consumers filtering the audit-event stream on `.failed` specifically will
|
|
37
|
+
see fewer of them; those findings now surface as `.blocked`.
|
|
38
|
+
- Hook script version (`HOOK_VERSION`) bumped 2.1.0 → 2.2.0 in lockstep.
|
|
39
|
+
|
|
40
|
+
Requires `agentgovern hook install --force` (or a fresh install) to pick up
|
|
41
|
+
the updated hook script — existing deployed hooks do not auto-update.
|
|
42
|
+
|
|
43
|
+
Full design and build record: `docs/audit-reports/nagt-137-hook-sync-blocking-2026-08-03.md`.
|
|
44
|
+
|
|
45
|
+
### Fix — `langgraph` extra had no upper version bound (NAGT-169)
|
|
46
|
+
|
|
47
|
+
`create_react_agent` is deprecated as of LangGraph v1.0 and scheduled for
|
|
48
|
+
removal in v2.0. The `langgraph` extra's dependency (`langgraph>=0.2.0`) had
|
|
49
|
+
no ceiling, so a fresh `pip install agentgovern[langgraph]` after LangGraph
|
|
50
|
+
2.0 ships would have pulled it unpinned — breaking this SDK's own
|
|
51
|
+
`test_langgraph.py` integration tests and the `create_react_agent`-based
|
|
52
|
+
onboarding sample on first run. Pinned to `langgraph>=0.2.0,<2.0.0`. The
|
|
53
|
+
`langchain` extra's `langchain-core` dependency was checked and left
|
|
54
|
+
unbounded — the instrumentor only uses the foundational, non-deprecated
|
|
55
|
+
`BaseCallbackHandler`, and `langchain-core` has no announced 2.0.
|
|
56
|
+
|
|
57
|
+
### Fix — `agentgovern hook status`/`hook test`/`hook install` crash on Windows under the default `cp1252` console codepage (NAGT-090, NAGT-169)
|
|
58
|
+
|
|
59
|
+
Several CLI status/error messages wrote non-ASCII characters (`✓`, `✗`, `→`)
|
|
60
|
+
that `cp1252` — the default console codepage on an unconfigured Windows
|
|
61
|
+
machine — cannot encode, raising `UnicodeEncodeError` in place of the
|
|
62
|
+
intended output. Replaced with ASCII equivalents (`OK`/`MISSING`, `->`) at
|
|
63
|
+
all five confirmed call sites: `hook status`'s per-event detail line and
|
|
64
|
+
`hook test`'s coverage banner (the two originally filed under NAGT-090),
|
|
65
|
+
`hook test`'s invalid-API-key/actor-ID error hints, and the
|
|
66
|
+
`Right-click -> Properties -> Security -> ...` permission-fix hint raised
|
|
67
|
+
when `~/.claude/settings.json` isn't writable — found by an exhaustive
|
|
68
|
+
non-cp1252-character sweep of the installer/CLI/instrumentor surface
|
|
69
|
+
while fixing the two originally-filed sites, not by a new bug report.
|
|
70
|
+
|
|
71
|
+
Full record: `docs/audit-reports/nagt-169-sdk-packaging-hygiene-2026-08-11.md`.
|
|
72
|
+
|
|
73
|
+
## 2.1.0 — 2026-07-20
|
|
74
|
+
|
|
75
|
+
### Feature — durable local outbox retry queue for the Claude Code hook (NAGT-095)
|
|
76
|
+
|
|
77
|
+
Fire-and-forget hook sends (`UserPromptSubmit`/`PostToolUse`/`Stop`) that fail
|
|
78
|
+
at the network level are no longer silently lost. A failed send is now queued
|
|
79
|
+
to a local durable outbox (`~/.agentgovern/outbox/`, one file per event) and
|
|
80
|
+
retried opportunistically on a later hook invocation — bounded so a backlog
|
|
81
|
+
can never meaningfully delay the invocation performing the retry (at most 5
|
|
82
|
+
items per invocation, a 1.5s wall-clock budget, a 60s per-file cooldown).
|
|
83
|
+
Permanently-abandoned events (48h TTL, or a 500-file size cap) are recorded to
|
|
84
|
+
`~/.agentgovern/outbox-dead/` instead of disappearing without a trace.
|
|
85
|
+
|
|
86
|
+
- **Swallowed-exception capture**: the network exception behind a failed send
|
|
87
|
+
is now captured into the queued record itself, turning the next real-world
|
|
88
|
+
occurrence into inspectable diagnostic data instead of a silent, unlogged
|
|
89
|
+
drop.
|
|
90
|
+
- **`client_event_id`**: every fire-and-forget payload now carries a
|
|
91
|
+
client-generated UUID v4, captured (not yet enforced) server-side —
|
|
92
|
+
positions a future dedup fix to act retroactively on rows this release
|
|
93
|
+
ever writes. Delivery remains at-least-once, not exactly-once, until that
|
|
94
|
+
follow-on ships.
|
|
95
|
+
- **Directory hardening**: `~/.agentgovern/outbox(-dead)/` are created
|
|
96
|
+
owner-only (`chmod 0700` on POSIX, an `icacls` ACL lockdown on Windows).
|
|
97
|
+
- Hook script version (`HOOK_VERSION`, distinct from this package version)
|
|
98
|
+
bumped 2.0.0 → 2.1.0 in lockstep.
|
|
99
|
+
|
|
100
|
+
No API or behavior change for installs that never experience a send failure.
|
|
101
|
+
Requires `agentgovern hook install --force` (or a fresh install) to pick up
|
|
102
|
+
the updated hook script — existing deployed hooks do not auto-update.
|
|
103
|
+
|
|
104
|
+
Full design and build record: `docs/audit-reports/nagt-095-async-prompt-drop-2026-07-20.md`.
|
|
105
|
+
|
|
106
|
+
## 2.0.2 — 2026-07-02
|
|
107
|
+
|
|
108
|
+
### Fix — README quickstart correctness (NAGT-019)
|
|
109
|
+
|
|
110
|
+
Two documentation corrections so the published quickstart actually works for a
|
|
111
|
+
first-time customer. No code or API change.
|
|
112
|
+
|
|
113
|
+
- **Install line** now reads `pip install "agentgovern[langchain]"`. The base
|
|
114
|
+
`pip install agentgovern` installs no runtime dependencies (they moved to the
|
|
115
|
+
`[sdk]`/`[langchain]` extras in 0.2.0), so the LangChain quickstart that
|
|
116
|
+
follows it could not run as written.
|
|
117
|
+
- **API-key prefix** in the Claude Code Hook env-var example corrected from
|
|
118
|
+
`as_prod_` to `ag_prod_`, matching the prefix the server actually issues and
|
|
119
|
+
validates (`ag_prod_`/`ag_stg_`/`ag_dev_`).
|
|
120
|
+
|
|
121
|
+
Surfaced by the IAG-007 customer-docs Phase 1 audit (PR #237).
|
|
122
|
+
|
|
123
|
+
## 2.0.1 — 2026-06-29
|
|
124
|
+
|
|
125
|
+
### Fix — Gate 1 wire-shape contract
|
|
126
|
+
|
|
127
|
+
`evaluate_input()` now nests `context` and `metadata` inside the `input` field of
|
|
128
|
+
the request body, matching the engine's `EvaluateInputRequest` schema. Previously
|
|
129
|
+
these were sent at the top level, where the engine silently dropped them — so input
|
|
130
|
+
policies that depend on `context`/`metadata` were evaluated against missing data.
|
|
131
|
+
The public Python signature is unchanged; only the on-the-wire serialization is
|
|
132
|
+
corrected. Surfaced by the SEC-002 pre-flight contract check.
|
|
133
|
+
|
|
134
|
+
## 0.2.0 — 2026-06-11
|
|
135
|
+
|
|
136
|
+
### Breaking change — optional SDK dependencies
|
|
137
|
+
|
|
138
|
+
`httpx`, `pydantic`, and `python-dotenv` are no longer installed by default.
|
|
139
|
+
They have moved to the `[sdk]` optional extra.
|
|
140
|
+
|
|
141
|
+
**Affected users:** anyone who installed `agentgovern` and used the agent SDK
|
|
142
|
+
(`init()`, `track_action()`, `evaluate_input()`, framework instrumentors).
|
|
143
|
+
|
|
144
|
+
**Migration:**
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# Before
|
|
148
|
+
pip install agentgovern
|
|
149
|
+
|
|
150
|
+
# After — agent SDK usage
|
|
151
|
+
pip install agentgovern[sdk]
|
|
152
|
+
|
|
153
|
+
# After — LangChain (sdk is pulled in automatically)
|
|
154
|
+
pip install agentgovern[langchain]
|
|
155
|
+
|
|
156
|
+
# Hook-only users (Claude Code governance) — no change needed
|
|
157
|
+
pip install agentgovern
|
|
158
|
+
agentgovern hook install
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Errata (2026-07-10):** this entry originally read "Claude Code / Cursor
|
|
162
|
+
governance" — Cursor was never shipped as a hook target; corrected to
|
|
163
|
+
Claude Code, the only host with a working client.
|
|
164
|
+
|
|
165
|
+
### Why
|
|
166
|
+
|
|
167
|
+
The hook CLI (`agentgovern hook install/uninstall/status/test`) is pure Python
|
|
168
|
+
standard library and does not need httpx or pydantic. Moving these to an
|
|
169
|
+
optional extra reduces the installation footprint for hook-only deployments
|
|
170
|
+
from ~45 MB to ~1 MB.
|
|
171
|
+
|
|
172
|
+
## 0.1.1b15 — prior
|
|
173
|
+
|
|
174
|
+
Initial beta releases. See git history.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: agentgovern
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Compliance-as-code middleware for agentic AI workflows.
|
|
5
5
|
Project-URL: Homepage, https://agentgovern.zirahn.com
|
|
6
6
|
Project-URL: Repository, https://github.com/ahmedkhan-zirahn/agentgovern
|
|
@@ -42,7 +42,7 @@ Requires-Dist: python-dotenv>=1.0.0; extra == 'langchain'
|
|
|
42
42
|
Provides-Extra: langgraph
|
|
43
43
|
Requires-Dist: httpx>=0.27.0; extra == 'langgraph'
|
|
44
44
|
Requires-Dist: langchain-core>=0.3.0; extra == 'langgraph'
|
|
45
|
-
Requires-Dist: langgraph
|
|
45
|
+
Requires-Dist: langgraph<2.0.0,>=0.2.0; extra == 'langgraph'
|
|
46
46
|
Requires-Dist: pydantic>=2.10.0; extra == 'langgraph'
|
|
47
47
|
Requires-Dist: python-dotenv>=1.0.0; extra == 'langgraph'
|
|
48
48
|
Provides-Extra: openai
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.2.0"
|
|
@@ -1,111 +1,111 @@
|
|
|
1
|
-
"""AgentGovern SDK exceptions."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
6
|
-
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from agentgovern.types import InputEvaluation
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
# noqa: N818 — name is part of stable SDK public API; renaming would break import contracts for existing users.
|
|
12
|
-
class PolicyViolation(Exception): # noqa: N818
|
|
13
|
-
"""
|
|
14
|
-
Raised by Gate 1 when enforcement_mode=enforce and the input is blocked.
|
|
15
|
-
|
|
16
|
-
Gate 2 (async ingest) never raises — it fails silently per the fail-open guarantee.
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
def __init__(self, evaluation: InputEvaluation) -> None:
|
|
20
|
-
first = evaluation.violations[0] if evaluation.violations else None
|
|
21
|
-
msg = first.reason if first else "Input blocked by compliance policy."
|
|
22
|
-
super().__init__(msg)
|
|
23
|
-
self.evaluation = evaluation
|
|
24
|
-
|
|
25
|
-
@property
|
|
26
|
-
def rule_code(self) -> str | None:
|
|
27
|
-
return self.evaluation.violations[0].rule_code if self.evaluation.violations else None
|
|
28
|
-
|
|
29
|
-
@property
|
|
30
|
-
def reason(self) -> str:
|
|
31
|
-
return str(self.args[0])
|
|
32
|
-
|
|
33
|
-
@property
|
|
34
|
-
def framework_ref(self) -> str | None:
|
|
35
|
-
return self.evaluation.violations[0].framework_ref if self.evaluation.violations else None
|
|
36
|
-
|
|
37
|
-
def __repr__(self) -> str:
|
|
38
|
-
return (
|
|
39
|
-
f"PolicyViolation(rule_code={self.rule_code!r}, "
|
|
40
|
-
f"reason={self.reason!r}, "
|
|
41
|
-
f"framework_ref={self.framework_ref!r})"
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
# noqa: N818 — name is part of stable SDK public API; renaming would break import contracts for existing users.
|
|
46
|
-
class GateUnavailable(Exception): # noqa: N818
|
|
47
|
-
"""
|
|
48
|
-
Raised when Gate 1 is unreachable and fail_closed=True.
|
|
49
|
-
|
|
50
|
-
With the default fail_closed=False, Gate 1 errors are silently treated as
|
|
51
|
-
allowed=True so the agent continues unaffected.
|
|
52
|
-
"""
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class OutputBlocked(Exception): # noqa: N818
|
|
56
|
-
"""
|
|
57
|
-
Raised by Gate 2 when the agent's output is blocked in enforce mode.
|
|
58
|
-
|
|
59
|
-
The output is never returned to the caller. Catch this to handle a blocked
|
|
60
|
-
response (e.g. show an error message, fall back to a safe default, log to
|
|
61
|
-
your own audit trail).
|
|
62
|
-
"""
|
|
63
|
-
|
|
64
|
-
def __init__(self, reason: str, *, evaluation_id: str | None = None) -> None:
|
|
65
|
-
super().__init__(reason)
|
|
66
|
-
self.reason = reason
|
|
67
|
-
self.evaluation_id = evaluation_id
|
|
68
|
-
|
|
69
|
-
def __repr__(self) -> str:
|
|
70
|
-
return (
|
|
71
|
-
f"OutputBlocked(reason={self.reason!r}, "
|
|
72
|
-
f"evaluation_id={self.evaluation_id!r})"
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
class OutputEscalated(Exception): # noqa: N818
|
|
77
|
-
"""
|
|
78
|
-
Raised by Gate 2 when the agent's output is escalated for human review.
|
|
79
|
-
|
|
80
|
-
A review_queue row has been created. The output is held until a human
|
|
81
|
-
approves or rejects it. Catch this to notify the caller that a review
|
|
82
|
-
is in progress and surface the review_id so they can poll for a decision.
|
|
83
|
-
"""
|
|
84
|
-
|
|
85
|
-
def __init__(self, review_id: str, *, evaluation_id: str | None = None) -> None:
|
|
86
|
-
super().__init__(f"Output escalated for human review: {review_id}")
|
|
87
|
-
self.review_id = review_id
|
|
88
|
-
self.evaluation_id = evaluation_id
|
|
89
|
-
|
|
90
|
-
def __repr__(self) -> str:
|
|
91
|
-
return (
|
|
92
|
-
f"OutputEscalated(review_id={self.review_id!r}, "
|
|
93
|
-
f"evaluation_id={self.evaluation_id!r})"
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
class ClientError(Exception):
|
|
98
|
-
"""
|
|
99
|
-
Raised on 4xx responses from the AgentGovern API.
|
|
100
|
-
|
|
101
|
-
This always propagates regardless of fail_silently — a 4xx indicates a
|
|
102
|
-
configuration problem (wrong API key, invalid payload) that the caller
|
|
103
|
-
should fix, not a transient network issue.
|
|
104
|
-
"""
|
|
105
|
-
|
|
106
|
-
def __init__(self, message: str, *, status_code: int) -> None:
|
|
107
|
-
super().__init__(message)
|
|
108
|
-
self.status_code = status_code
|
|
109
|
-
|
|
110
|
-
def __repr__(self) -> str:
|
|
111
|
-
return f"ClientError(status_code={self.status_code}, message={str(self.args[0])!r})"
|
|
1
|
+
"""AgentGovern SDK exceptions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from agentgovern.types import InputEvaluation
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# noqa: N818 — name is part of stable SDK public API; renaming would break import contracts for existing users.
|
|
12
|
+
class PolicyViolation(Exception): # noqa: N818
|
|
13
|
+
"""
|
|
14
|
+
Raised by Gate 1 when enforcement_mode=enforce and the input is blocked.
|
|
15
|
+
|
|
16
|
+
Gate 2 (async ingest) never raises — it fails silently per the fail-open guarantee.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, evaluation: InputEvaluation) -> None:
|
|
20
|
+
first = evaluation.violations[0] if evaluation.violations else None
|
|
21
|
+
msg = first.reason if first else "Input blocked by compliance policy."
|
|
22
|
+
super().__init__(msg)
|
|
23
|
+
self.evaluation = evaluation
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def rule_code(self) -> str | None:
|
|
27
|
+
return self.evaluation.violations[0].rule_code if self.evaluation.violations else None
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def reason(self) -> str:
|
|
31
|
+
return str(self.args[0])
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def framework_ref(self) -> str | None:
|
|
35
|
+
return self.evaluation.violations[0].framework_ref if self.evaluation.violations else None
|
|
36
|
+
|
|
37
|
+
def __repr__(self) -> str:
|
|
38
|
+
return (
|
|
39
|
+
f"PolicyViolation(rule_code={self.rule_code!r}, "
|
|
40
|
+
f"reason={self.reason!r}, "
|
|
41
|
+
f"framework_ref={self.framework_ref!r})"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# noqa: N818 — name is part of stable SDK public API; renaming would break import contracts for existing users.
|
|
46
|
+
class GateUnavailable(Exception): # noqa: N818
|
|
47
|
+
"""
|
|
48
|
+
Raised when Gate 1 is unreachable and fail_closed=True.
|
|
49
|
+
|
|
50
|
+
With the default fail_closed=False, Gate 1 errors are silently treated as
|
|
51
|
+
allowed=True so the agent continues unaffected.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class OutputBlocked(Exception): # noqa: N818
|
|
56
|
+
"""
|
|
57
|
+
Raised by Gate 2 when the agent's output is blocked in enforce mode.
|
|
58
|
+
|
|
59
|
+
The output is never returned to the caller. Catch this to handle a blocked
|
|
60
|
+
response (e.g. show an error message, fall back to a safe default, log to
|
|
61
|
+
your own audit trail).
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def __init__(self, reason: str, *, evaluation_id: str | None = None) -> None:
|
|
65
|
+
super().__init__(reason)
|
|
66
|
+
self.reason = reason
|
|
67
|
+
self.evaluation_id = evaluation_id
|
|
68
|
+
|
|
69
|
+
def __repr__(self) -> str:
|
|
70
|
+
return (
|
|
71
|
+
f"OutputBlocked(reason={self.reason!r}, "
|
|
72
|
+
f"evaluation_id={self.evaluation_id!r})"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class OutputEscalated(Exception): # noqa: N818
|
|
77
|
+
"""
|
|
78
|
+
Raised by Gate 2 when the agent's output is escalated for human review.
|
|
79
|
+
|
|
80
|
+
A review_queue row has been created. The output is held until a human
|
|
81
|
+
approves or rejects it. Catch this to notify the caller that a review
|
|
82
|
+
is in progress and surface the review_id so they can poll for a decision.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def __init__(self, review_id: str, *, evaluation_id: str | None = None) -> None:
|
|
86
|
+
super().__init__(f"Output escalated for human review: {review_id}")
|
|
87
|
+
self.review_id = review_id
|
|
88
|
+
self.evaluation_id = evaluation_id
|
|
89
|
+
|
|
90
|
+
def __repr__(self) -> str:
|
|
91
|
+
return (
|
|
92
|
+
f"OutputEscalated(review_id={self.review_id!r}, "
|
|
93
|
+
f"evaluation_id={self.evaluation_id!r})"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class ClientError(Exception):
|
|
98
|
+
"""
|
|
99
|
+
Raised on 4xx responses from the AgentGovern API.
|
|
100
|
+
|
|
101
|
+
This always propagates regardless of fail_silently — a 4xx indicates a
|
|
102
|
+
configuration problem (wrong API key, invalid payload) that the caller
|
|
103
|
+
should fix, not a transient network issue.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def __init__(self, message: str, *, status_code: int) -> None:
|
|
107
|
+
super().__init__(message)
|
|
108
|
+
self.status_code = status_code
|
|
109
|
+
|
|
110
|
+
def __repr__(self) -> str:
|
|
111
|
+
return f"ClientError(status_code={self.status_code}, message={str(self.args[0])!r})"
|