agentguard-governance-sdk 0.1.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 AgentGuard Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentguard-governance-sdk
3
+ Version: 0.1.0
4
+ Summary: Developer SDK for AgentGuard runtime governance and policy enforcement layer
5
+ Author: AgentGuard Team
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/agentguard/agentguard
8
+ Project-URL: Documentation, https://github.com/agentguard/agentguard/tree/main/sdk
9
+ Project-URL: Bug Tracker, https://github.com/agentguard/agentguard/issues
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Topic :: Security
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: httpx>=0.24.0
23
+ Requires-Dist: pydantic>=2.0.0
24
+ Dynamic: license-file
25
+
26
+ # agentguard-governance-sdk
27
+
28
+ Official Python Developer SDK for the **AgentGuard** runtime governance and policy enforcement layer.
29
+
30
+ AgentGuard intercepts proposed AI agent tool calls before dispatch, evaluates them against organizational RBAC, risk policies, token/rate limits, and cost budgets, and logs decisions to a cryptographic tamper-evident audit vault.
31
+
32
+ ---
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install agentguard-governance-sdk
38
+ ```
39
+
40
+ **Requirements:** Python 3.9+ with `httpx` and `pydantic`.
41
+
42
+
43
+ ---
44
+
45
+ ## Core Usage
46
+
47
+ ### Standard Exception-Driven Pattern
48
+
49
+ By default, `client.guard(...)` returns a `GuardResult` on `ALLOW`, and raises structured exceptions when an action is blocked or queued for approval:
50
+
51
+ ```python
52
+ from agentguard import (
53
+ AgentGuard,
54
+ AgentGuardDenied,
55
+ AgentGuardPending,
56
+ AgentGuardAuthenticationError,
57
+ AgentGuardTimeout,
58
+ AgentGuardServerError,
59
+ )
60
+
61
+ # Initialize client
62
+ client = AgentGuard(
63
+ api_key="ag_agent_your_key_here",
64
+ base_url="http://localhost:8000/api/v1", # Core Engine Gateway URL
65
+ )
66
+
67
+ try:
68
+ result = client.guard(
69
+ tool="read_customer",
70
+ parameters={"customer_id": "CUST-1001"},
71
+ estimated_tokens=50,
72
+ estimated_cost=0.001,
73
+ )
74
+ if result.allowed:
75
+ print(f"Action allowed! Request ID: {result.request_id}")
76
+ # Safely execute the real tool call here
77
+
78
+ except AgentGuardDenied as e:
79
+ # Action was rejected by policy (e.g. CRITICAL risk, unpermitted tool, rate/budget exceeded)
80
+ print(f"Action DENIED: {e.reason} (request_id={e.request_id})")
81
+
82
+ except AgentGuardPending as e:
83
+ # Action requires human-in-the-loop (HITL) approval
84
+ print(f"Action paused for human approval: {e.reason} (request_id={e.request_id})")
85
+
86
+ except AgentGuardAuthenticationError as e:
87
+ print(f"Authentication failed (HTTP {e.status_code}): {e.detail}")
88
+
89
+ except AgentGuardTimeout as e:
90
+ print(f"Connection to AgentGuard timed out or was refused: {e}")
91
+
92
+ except AgentGuardServerError as e:
93
+ print(f"AgentGuard backend error (HTTP {e.status_code}): {e.detail}")
94
+
95
+ finally:
96
+ client.close()
97
+ ```
98
+
99
+ ---
100
+
101
+ ### Context Manager Support
102
+
103
+ `AgentGuard` instances can be managed automatically via a `with` block:
104
+
105
+ ```python
106
+ from agentguard import AgentGuard
107
+
108
+ with AgentGuard(api_key="ag_agent_...") as client:
109
+ result = client.guard(tool="send_email", parameters={"to": "user@example.com"})
110
+ print("Allowed:", result.allowed)
111
+ ```
112
+
113
+ ---
114
+
115
+ ### Non-Raising / Boolean Return Mode
116
+
117
+ Pass `raise_for_status=False` to inspect decisions via the returned `GuardResult` without catching exceptions:
118
+
119
+ ```python
120
+ with AgentGuard(api_key="ag_agent_...") as client:
121
+ result = client.guard(
122
+ tool="process_refund",
123
+ parameters={"amount": 5000},
124
+ raise_for_status=False,
125
+ )
126
+
127
+ if result.allowed:
128
+ print("Proceeding with refund...")
129
+ elif result.decision == "PENDING":
130
+ print(f"Queued for human review. Request ID: {result.request_id}")
131
+ elif result.decision == "DENY":
132
+ print(f"Refund denied: {result.reason}")
133
+ ```
134
+
135
+ ---
136
+
137
+ ## API Reference
138
+
139
+ ### `AgentGuard` Constructor
140
+
141
+ ```python
142
+ AgentGuard(
143
+ api_key: str,
144
+ base_url: str = "http://localhost:8000/api/v1",
145
+ agent_id: Optional[Union[uuid.UUID, str]] = None,
146
+ timeout: float = 5.0,
147
+ max_retries: int = 1,
148
+ retry_backoff: float = 0.2,
149
+ http_client: Optional[httpx.Client] = None,
150
+ )
151
+ ```
152
+
153
+ ### `client.guard(...)` Parameters
154
+
155
+ | Parameter | Type | Default | Description |
156
+ |---|---|---|---|
157
+ | `tool` | `str` | *required* | Name of the tool to be executed (e.g. `"read_customer"`). |
158
+ | `parameters` | `dict` | `{}` | Key-value dictionary of arguments for the tool. |
159
+ | `action` | `str` | `"execute"` | Action verb associated with the tool call. |
160
+ | `agent_id` | `UUID \| str` | `None` | Optional override for the requesting agent UUID. |
161
+ | `estimated_tokens` | `int` | `0` | Estimated token usage for sliding-window token rate limiting. |
162
+ | `estimated_cost` | `float` | `0.0` | Estimated monetary spend for budget enforcement. |
163
+ | `metadata` | `dict` | `None` | Custom context or telemetry dictionary attached to audit log. |
164
+ | `timeout` | `float` | `None` | Per-request timeout override in seconds. |
165
+ | `max_retries` | `int` | `None` | Per-request network retry attempts (safe network failures only). |
166
+ | `raise_for_status` | `bool` | `True` | If `True`, raises `AgentGuardDenied` or `AgentGuardPending`. If `False`, returns `GuardResult` with `.allowed=False`. |
167
+
168
+ ### `GuardResult` Object Attributes
169
+
170
+ - `allowed` (`bool`): `True` if permitted, `False` otherwise.
171
+ - `decision` (`str`): `"ALLOW"`, `"DENY"`, or `"PENDING"`.
172
+ - `request_id` (`str`): Server-generated UUID uniquely tracking this evaluation in the audit vault.
173
+ - `reason` (`str`): Human-readable explanation of policy decision.
174
+ - `raw_response` (`dict`): Raw JSON response dictionary from the gateway.
175
+
176
+ ### Exception Hierarchy
177
+
178
+ ```
179
+ AgentGuardError (Base Exception)
180
+ ├── AgentGuardDenied (Action blocked by policy)
181
+ │ ├── reason: str
182
+ │ └── request_id: Optional[str]
183
+ ├── AgentGuardPending (Action paused for human approval)
184
+ │ ├── reason: str
185
+ │ └── request_id: Optional[str]
186
+ ├── AgentGuardAuthenticationError (HTTP 401/403)
187
+ │ ├── status_code: int
188
+ │ └── detail: Optional[str]
189
+ ├── AgentGuardTimeout (Unreachable / network dropped)
190
+ │ └── message: str
191
+ └── AgentGuardServerError (HTTP 5xx / unexpected response)
192
+ ├── status_code: int
193
+ ├── detail: Optional[str]
194
+ └── raw_response: Optional[str]
195
+ ```
196
+
@@ -0,0 +1,171 @@
1
+ # agentguard-governance-sdk
2
+
3
+ Official Python Developer SDK for the **AgentGuard** runtime governance and policy enforcement layer.
4
+
5
+ AgentGuard intercepts proposed AI agent tool calls before dispatch, evaluates them against organizational RBAC, risk policies, token/rate limits, and cost budgets, and logs decisions to a cryptographic tamper-evident audit vault.
6
+
7
+ ---
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install agentguard-governance-sdk
13
+ ```
14
+
15
+ **Requirements:** Python 3.9+ with `httpx` and `pydantic`.
16
+
17
+
18
+ ---
19
+
20
+ ## Core Usage
21
+
22
+ ### Standard Exception-Driven Pattern
23
+
24
+ By default, `client.guard(...)` returns a `GuardResult` on `ALLOW`, and raises structured exceptions when an action is blocked or queued for approval:
25
+
26
+ ```python
27
+ from agentguard import (
28
+ AgentGuard,
29
+ AgentGuardDenied,
30
+ AgentGuardPending,
31
+ AgentGuardAuthenticationError,
32
+ AgentGuardTimeout,
33
+ AgentGuardServerError,
34
+ )
35
+
36
+ # Initialize client
37
+ client = AgentGuard(
38
+ api_key="ag_agent_your_key_here",
39
+ base_url="http://localhost:8000/api/v1", # Core Engine Gateway URL
40
+ )
41
+
42
+ try:
43
+ result = client.guard(
44
+ tool="read_customer",
45
+ parameters={"customer_id": "CUST-1001"},
46
+ estimated_tokens=50,
47
+ estimated_cost=0.001,
48
+ )
49
+ if result.allowed:
50
+ print(f"Action allowed! Request ID: {result.request_id}")
51
+ # Safely execute the real tool call here
52
+
53
+ except AgentGuardDenied as e:
54
+ # Action was rejected by policy (e.g. CRITICAL risk, unpermitted tool, rate/budget exceeded)
55
+ print(f"Action DENIED: {e.reason} (request_id={e.request_id})")
56
+
57
+ except AgentGuardPending as e:
58
+ # Action requires human-in-the-loop (HITL) approval
59
+ print(f"Action paused for human approval: {e.reason} (request_id={e.request_id})")
60
+
61
+ except AgentGuardAuthenticationError as e:
62
+ print(f"Authentication failed (HTTP {e.status_code}): {e.detail}")
63
+
64
+ except AgentGuardTimeout as e:
65
+ print(f"Connection to AgentGuard timed out or was refused: {e}")
66
+
67
+ except AgentGuardServerError as e:
68
+ print(f"AgentGuard backend error (HTTP {e.status_code}): {e.detail}")
69
+
70
+ finally:
71
+ client.close()
72
+ ```
73
+
74
+ ---
75
+
76
+ ### Context Manager Support
77
+
78
+ `AgentGuard` instances can be managed automatically via a `with` block:
79
+
80
+ ```python
81
+ from agentguard import AgentGuard
82
+
83
+ with AgentGuard(api_key="ag_agent_...") as client:
84
+ result = client.guard(tool="send_email", parameters={"to": "user@example.com"})
85
+ print("Allowed:", result.allowed)
86
+ ```
87
+
88
+ ---
89
+
90
+ ### Non-Raising / Boolean Return Mode
91
+
92
+ Pass `raise_for_status=False` to inspect decisions via the returned `GuardResult` without catching exceptions:
93
+
94
+ ```python
95
+ with AgentGuard(api_key="ag_agent_...") as client:
96
+ result = client.guard(
97
+ tool="process_refund",
98
+ parameters={"amount": 5000},
99
+ raise_for_status=False,
100
+ )
101
+
102
+ if result.allowed:
103
+ print("Proceeding with refund...")
104
+ elif result.decision == "PENDING":
105
+ print(f"Queued for human review. Request ID: {result.request_id}")
106
+ elif result.decision == "DENY":
107
+ print(f"Refund denied: {result.reason}")
108
+ ```
109
+
110
+ ---
111
+
112
+ ## API Reference
113
+
114
+ ### `AgentGuard` Constructor
115
+
116
+ ```python
117
+ AgentGuard(
118
+ api_key: str,
119
+ base_url: str = "http://localhost:8000/api/v1",
120
+ agent_id: Optional[Union[uuid.UUID, str]] = None,
121
+ timeout: float = 5.0,
122
+ max_retries: int = 1,
123
+ retry_backoff: float = 0.2,
124
+ http_client: Optional[httpx.Client] = None,
125
+ )
126
+ ```
127
+
128
+ ### `client.guard(...)` Parameters
129
+
130
+ | Parameter | Type | Default | Description |
131
+ |---|---|---|---|
132
+ | `tool` | `str` | *required* | Name of the tool to be executed (e.g. `"read_customer"`). |
133
+ | `parameters` | `dict` | `{}` | Key-value dictionary of arguments for the tool. |
134
+ | `action` | `str` | `"execute"` | Action verb associated with the tool call. |
135
+ | `agent_id` | `UUID \| str` | `None` | Optional override for the requesting agent UUID. |
136
+ | `estimated_tokens` | `int` | `0` | Estimated token usage for sliding-window token rate limiting. |
137
+ | `estimated_cost` | `float` | `0.0` | Estimated monetary spend for budget enforcement. |
138
+ | `metadata` | `dict` | `None` | Custom context or telemetry dictionary attached to audit log. |
139
+ | `timeout` | `float` | `None` | Per-request timeout override in seconds. |
140
+ | `max_retries` | `int` | `None` | Per-request network retry attempts (safe network failures only). |
141
+ | `raise_for_status` | `bool` | `True` | If `True`, raises `AgentGuardDenied` or `AgentGuardPending`. If `False`, returns `GuardResult` with `.allowed=False`. |
142
+
143
+ ### `GuardResult` Object Attributes
144
+
145
+ - `allowed` (`bool`): `True` if permitted, `False` otherwise.
146
+ - `decision` (`str`): `"ALLOW"`, `"DENY"`, or `"PENDING"`.
147
+ - `request_id` (`str`): Server-generated UUID uniquely tracking this evaluation in the audit vault.
148
+ - `reason` (`str`): Human-readable explanation of policy decision.
149
+ - `raw_response` (`dict`): Raw JSON response dictionary from the gateway.
150
+
151
+ ### Exception Hierarchy
152
+
153
+ ```
154
+ AgentGuardError (Base Exception)
155
+ ├── AgentGuardDenied (Action blocked by policy)
156
+ │ ├── reason: str
157
+ │ └── request_id: Optional[str]
158
+ ├── AgentGuardPending (Action paused for human approval)
159
+ │ ├── reason: str
160
+ │ └── request_id: Optional[str]
161
+ ├── AgentGuardAuthenticationError (HTTP 401/403)
162
+ │ ├── status_code: int
163
+ │ └── detail: Optional[str]
164
+ ├── AgentGuardTimeout (Unreachable / network dropped)
165
+ │ └── message: str
166
+ └── AgentGuardServerError (HTTP 5xx / unexpected response)
167
+ ├── status_code: int
168
+ ├── detail: Optional[str]
169
+ └── raw_response: Optional[str]
170
+ ```
171
+
@@ -0,0 +1,27 @@
1
+ """AgentGuard Python SDK.
2
+
3
+ Framework-Agnostic Runtime Governance Layer for Autonomous AI Agents.
4
+ """
5
+ from agentguard.client import AgentGuard
6
+ from agentguard.exceptions import (
7
+ AgentGuardAuthenticationError,
8
+ AgentGuardDenied,
9
+ AgentGuardError,
10
+ AgentGuardPending,
11
+ AgentGuardServerError,
12
+ AgentGuardTimeout,
13
+ )
14
+ from agentguard.models import GuardRequest, GuardResult
15
+
16
+ __all__ = [
17
+ "AgentGuard",
18
+ "GuardRequest",
19
+ "GuardResult",
20
+ "AgentGuardError",
21
+ "AgentGuardDenied",
22
+ "AgentGuardPending",
23
+ "AgentGuardAuthenticationError",
24
+ "AgentGuardTimeout",
25
+ "AgentGuardServerError",
26
+ ]
27
+ __version__ = "0.1.0"
@@ -0,0 +1,120 @@
1
+ """AgentGuard client for governing autonomous agent tool calls over HTTP."""
2
+ import uuid
3
+ from typing import Any, Optional, Union
4
+ import httpx
5
+
6
+ from agentguard.guard import evaluate_guard
7
+ from agentguard.models import GuardResult
8
+
9
+
10
+ class AgentGuard:
11
+ """Synchronous client for the AgentGuard Core Engine.
12
+
13
+ Usage:
14
+ client = AgentGuard(api_key="ag_live_...")
15
+ result = client.guard(tool="read_customer", parameters={"customer_id": "123"})
16
+ if result.allowed:
17
+ # execute tool
18
+ pass
19
+
20
+ By default, blocked decisions raise structured exceptions (AgentGuardDenied,
21
+ AgentGuardPending). Pass `raise_for_status=False` to handle decisions via
22
+ the boolean `.allowed` attribute instead.
23
+ """
24
+
25
+ def __init__(
26
+ self,
27
+ api_key: str,
28
+ base_url: str = "http://localhost:8000/api/v1",
29
+ agent_id: Optional[Union[uuid.UUID, str]] = None,
30
+ timeout: float = 5.0,
31
+ max_retries: int = 1,
32
+ retry_backoff: float = 0.2,
33
+ http_client: Optional[httpx.Client] = None,
34
+ ) -> None:
35
+ self.api_key = api_key
36
+ self.base_url = base_url.rstrip("/")
37
+ self.agent_id = agent_id
38
+ self.timeout = timeout
39
+ self.max_retries = max_retries
40
+ self.retry_backoff = retry_backoff
41
+
42
+ self._headers = {
43
+ "X-API-Key": self.api_key,
44
+ "Content-Type": "application/json",
45
+ "User-Agent": "agentguard-python-sdk/0.1.0",
46
+ }
47
+
48
+ self._client = http_client or httpx.Client(timeout=timeout)
49
+ self._owns_client = http_client is None
50
+
51
+ def guard(
52
+ self,
53
+ tool: str,
54
+ parameters: Optional[dict[str, Any]] = None,
55
+ *,
56
+ action: str = "execute",
57
+ agent_id: Optional[Union[uuid.UUID, str]] = None,
58
+ estimated_tokens: int = 0,
59
+ estimated_cost: float = 0.0,
60
+ metadata: Optional[dict[str, Any]] = None,
61
+ timeout: Optional[float] = None,
62
+ max_retries: Optional[int] = None,
63
+ raise_for_status: bool = True,
64
+ ) -> GuardResult:
65
+ """Evaluate a proposed tool call through AgentGuard.
66
+
67
+ Args:
68
+ tool: Name of the tool to be executed (e.g. "read_customer").
69
+ parameters: Key-value parameters intended for the tool.
70
+ action: Action verb (default: "execute").
71
+ agent_id: Optional override for the calling agent UUID.
72
+ estimated_tokens: Estimated token usage for cost enforcement.
73
+ estimated_cost: Estimated monetary cost for budget caps.
74
+ metadata: Custom context metadata attached to the request.
75
+ timeout: Per-request timeout in seconds (overrides client default).
76
+ max_retries: Per-request network retry attempts.
77
+ raise_for_status: If True (default), raises AgentGuardDenied or
78
+ AgentGuardPending. If False, returns GuardResult with .allowed=False.
79
+
80
+ Returns:
81
+ GuardResult with .allowed=True, .decision, .request_id, and .reason.
82
+
83
+ Raises:
84
+ AgentGuardDenied: If policy blocks the action.
85
+ AgentGuardPending: If action requires human review (HITL).
86
+ AgentGuardAuthenticationError: If API key is invalid or revoked.
87
+ AgentGuardTimeout: If the backend is unreachable or times out.
88
+ AgentGuardServerError: If the backend encounters an internal error.
89
+ """
90
+ effective_agent_id = agent_id or self.agent_id
91
+ effective_timeout = timeout if timeout is not None else self.timeout
92
+ effective_retries = max_retries if max_retries is not None else self.max_retries
93
+
94
+ return evaluate_guard(
95
+ client=self._client,
96
+ base_url=self.base_url,
97
+ headers=self._headers,
98
+ tool=tool,
99
+ parameters=parameters or {},
100
+ action=action,
101
+ agent_id=effective_agent_id,
102
+ estimated_tokens=estimated_tokens,
103
+ estimated_cost=estimated_cost,
104
+ metadata=metadata,
105
+ timeout=effective_timeout,
106
+ max_retries=effective_retries,
107
+ retry_backoff=self.retry_backoff,
108
+ raise_for_status=raise_for_status,
109
+ )
110
+
111
+ def close(self) -> None:
112
+ """Close the underlying HTTP client if owned by this instance."""
113
+ if self._owns_client and not self._client.is_closed:
114
+ self._client.close()
115
+
116
+ def __enter__(self) -> "AgentGuard":
117
+ return self
118
+
119
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
120
+ self.close()
@@ -0,0 +1,95 @@
1
+ """Structured exceptions for the AgentGuard SDK.
2
+
3
+ All SDK exceptions inherit from AgentGuardError so callers can catch broadly or specifically.
4
+ """
5
+ from typing import Optional
6
+
7
+
8
+ class AgentGuardError(Exception):
9
+ """Base exception for all AgentGuard SDK errors."""
10
+ pass
11
+
12
+
13
+ class AgentGuardDenied(AgentGuardError):
14
+ """Raised when a proposed tool call is blocked (DENIED) by AgentGuard governance.
15
+
16
+ Attributes:
17
+ reason: Human-readable explanation of why the action was denied.
18
+ request_id: Server-assigned UUID tracking the decision in the audit vault.
19
+ """
20
+ def __init__(self, reason: str, request_id: Optional[str] = None) -> None:
21
+ self.reason = reason
22
+ self.request_id = request_id
23
+ msg = f"AgentGuard denied tool call: {reason}"
24
+ if request_id:
25
+ msg += f" (request_id={request_id})"
26
+ super().__init__(msg)
27
+
28
+
29
+ class AgentGuardPending(AgentGuardError):
30
+ """Raised when a proposed tool call requires Human-in-the-Loop (HITL) approval (PENDING).
31
+
32
+ This is not an unexpected failure; it indicates the agent must pause until a human
33
+ reviews the request. The request_id can be used to track or poll approval status.
34
+
35
+ Attributes:
36
+ reason: Policy explanation detailing why human review was flagged.
37
+ request_id: Server-assigned UUID representing the pending HITL request.
38
+ """
39
+ def __init__(self, reason: str, request_id: Optional[str] = None) -> None:
40
+ self.reason = reason
41
+ self.request_id = request_id
42
+ msg = f"AgentGuard queued action for human approval: {reason}"
43
+ if request_id:
44
+ msg += f" (request_id={request_id})"
45
+ super().__init__(msg)
46
+
47
+
48
+ class AgentGuardAuthenticationError(AgentGuardError):
49
+ """Raised when the backend rejects client credentials (HTTP 401 Unauthorized or 403 Forbidden).
50
+
51
+ Attributes:
52
+ status_code: HTTP status code received (typically 401 or 403).
53
+ detail: Error details returned by the backend.
54
+ """
55
+ def __init__(self, status_code: int = 401, detail: Optional[str] = None) -> None:
56
+ self.status_code = status_code
57
+ self.detail = detail
58
+ msg = f"AgentGuard authentication failed (HTTP {status_code})"
59
+ if detail:
60
+ msg += f": {detail}"
61
+ super().__init__(msg)
62
+
63
+
64
+ class AgentGuardTimeout(AgentGuardError):
65
+ """Raised when AgentGuard cannot be reached within the configured timeout.
66
+
67
+ NOTE: Raised for both connection timeouts and connection refusals — treat as
68
+ 'could not reach the backend', not literally 'timed out'.
69
+ """
70
+ def __init__(self, message: str = "Request to AgentGuard timed out or connection refused") -> None:
71
+ self.message = message
72
+ super().__init__(message)
73
+
74
+
75
+ class AgentGuardServerError(AgentGuardError):
76
+ """Raised when the AgentGuard backend returns an unexpected 5xx response or unparseable payload.
77
+
78
+ Attributes:
79
+ status_code: HTTP response status code.
80
+ detail: Backend error detail message if available.
81
+ raw_response: Raw response body string for debugging.
82
+ """
83
+ def __init__(
84
+ self,
85
+ status_code: int,
86
+ detail: Optional[str] = None,
87
+ raw_response: Optional[str] = None,
88
+ ) -> None:
89
+ self.status_code = status_code
90
+ self.detail = detail
91
+ self.raw_response = raw_response
92
+ msg = f"AgentGuard backend error (HTTP {status_code})"
93
+ if detail:
94
+ msg += f": {detail}"
95
+ super().__init__(msg)