codesec-agentguard 1.0.0__py3-none-any.whl

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.
agentguard/__init__.py ADDED
@@ -0,0 +1,31 @@
1
+ """AgentGuard Python SDK."""
2
+
3
+ from .client import AgentGuard
4
+ from .exceptions import (
5
+ AgentGuardAuthenticationError,
6
+ AgentGuardError,
7
+ AgentGuardForbiddenError,
8
+ AgentGuardGatewayError,
9
+ AgentGuardNotFoundError,
10
+ AgentGuardRateLimitError,
11
+ AgentGuardTimeoutError,
12
+ AgentGuardValidationError,
13
+ )
14
+ from .models import ApprovalRecord, EvaluationResult, PromptScanResult
15
+
16
+ __all__ = [
17
+ "AgentGuard",
18
+ "AgentGuardError",
19
+ "AgentGuardAuthenticationError",
20
+ "AgentGuardForbiddenError",
21
+ "AgentGuardValidationError",
22
+ "AgentGuardNotFoundError",
23
+ "AgentGuardRateLimitError",
24
+ "AgentGuardTimeoutError",
25
+ "AgentGuardGatewayError",
26
+ "EvaluationResult",
27
+ "ApprovalRecord",
28
+ "PromptScanResult",
29
+ ]
30
+
31
+ __version__ = "1.0.0"
agentguard/client.py ADDED
@@ -0,0 +1,278 @@
1
+ """AgentGuard Official Python Client."""
2
+
3
+ import json
4
+ import socket
5
+ import time
6
+ import urllib.error
7
+ import urllib.parse
8
+ import urllib.request
9
+ from typing import Any, Dict, Optional, Union
10
+
11
+ from .exceptions import (
12
+ AgentGuardAuthenticationError,
13
+ AgentGuardError,
14
+ AgentGuardForbiddenError,
15
+ AgentGuardGatewayError,
16
+ AgentGuardNotFoundError,
17
+ AgentGuardRateLimitError,
18
+ AgentGuardTimeoutError,
19
+ AgentGuardValidationError,
20
+ )
21
+ from .models import ApprovalRecord, EvaluationResult, PromptScanResult
22
+
23
+
24
+ class AgentGuard:
25
+ """Official Python Client for the AgentGuard AI Agent Security Gateway.
26
+
27
+ Intercepts and authorizes agent tool calls, financial actions, and prompt inputs
28
+ using deterministic policy enforcement and adversarial heuristic scanning.
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ base_url: str,
34
+ api_key: str,
35
+ timeout: float = 5.0,
36
+ max_retries: int = 2,
37
+ headers: Optional[Dict[str, str]] = None,
38
+ workspace_id: Optional[str] = None,
39
+ ):
40
+ if not base_url or not isinstance(base_url, str):
41
+ raise AgentGuardError("AgentGuard base_url is required and must be a non-empty string.")
42
+ if not api_key or not isinstance(api_key, str):
43
+ raise AgentGuardError("AgentGuard api_key is required and must be a non-empty string.")
44
+
45
+ self.base_url = base_url.rstrip("/")
46
+ self.api_key = api_key.strip()
47
+ self.timeout = float(timeout)
48
+ self.max_retries = int(max_retries)
49
+ self.workspace_id = workspace_id
50
+
51
+ self.default_headers = {
52
+ "Content-Type": "application/json",
53
+ "Accept": "application/json",
54
+ "Authorization": f"Bearer {self.api_key}",
55
+ "X-API-Key": self.api_key,
56
+ "User-Agent": "agentguard-sdk-python/1.0.0",
57
+ }
58
+ if headers:
59
+ self.default_headers.update(headers)
60
+ if self.workspace_id:
61
+ self.default_headers["X-Workspace-Id"] = self.workspace_id
62
+
63
+ def __enter__(self) -> "AgentGuard":
64
+ return self
65
+
66
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
67
+ self.close()
68
+
69
+ def close(self) -> None:
70
+ """Explicit resource cleanup if required."""
71
+ pass
72
+
73
+ def evaluate(
74
+ self,
75
+ action: Optional[Union[str, Dict[str, Any]]] = None,
76
+ amount: Optional[Union[float, int, str]] = None,
77
+ currency: str = "USD",
78
+ recipient: Optional[Union[str, Dict[str, Any]]] = None,
79
+ description: Optional[str] = None,
80
+ agent_id: Optional[str] = None,
81
+ tool: Optional[str] = None,
82
+ operation: Optional[str] = None,
83
+ prompt_context: Optional[str] = None,
84
+ human_approval_token: Optional[str] = None,
85
+ metadata: Optional[Dict[str, Any]] = None,
86
+ idempotency_key: Optional[str] = None,
87
+ **extra_kwargs: Any,
88
+ ) -> EvaluationResult:
89
+ """Pre-flight policy evaluation for autonomous AI agents and backend actions.
90
+
91
+ Supports both dictionary style (evaluate({"action": "payment.create", ...}))
92
+ and keyword-argument style (evaluate(action="payment.create", amount=1500)).
93
+ """
94
+ payload: Dict[str, Any] = {}
95
+
96
+ if isinstance(action, dict):
97
+ payload = dict(action)
98
+ elif isinstance(action, str):
99
+ payload["action"] = action
100
+ if amount is not None:
101
+ payload["amount"] = amount
102
+ if currency:
103
+ payload["currency"] = currency
104
+ if recipient is not None:
105
+ payload["recipient"] = recipient
106
+ if description is not None:
107
+ payload["description"] = description
108
+ if agent_id is not None:
109
+ payload["agentId"] = agent_id
110
+ if tool is not None:
111
+ payload["tool"] = tool
112
+ if operation is not None:
113
+ payload["operation"] = operation
114
+ if prompt_context is not None:
115
+ payload["promptContext"] = prompt_context
116
+ if human_approval_token is not None:
117
+ payload["humanApprovalToken"] = human_approval_token
118
+ if metadata is not None:
119
+ payload["metadata"] = metadata
120
+ if idempotency_key is not None:
121
+ payload["idempotencyKey"] = idempotency_key
122
+ if extra_kwargs:
123
+ payload.update(extra_kwargs)
124
+ else:
125
+ raise AgentGuardValidationError("First argument must be either an action string or a payload dictionary.")
126
+
127
+ if not payload.get("action"):
128
+ raise AgentGuardValidationError("Evaluation request must specify an 'action'.")
129
+
130
+ request_id = payload.get("idempotencyKey") or f"py_sdk_{int(time.time() * 1000)}"
131
+ headers = {"X-Request-Id": request_id}
132
+
133
+ status_code, body = self._request("POST", "/api/v1/evaluate", payload=payload, extra_headers=headers)
134
+
135
+ # Policy decision returns structured evaluation even on HTTP 202 (HOLD) or 403 (BLOCK)
136
+ if isinstance(body, dict) and body.get("status") in ("ALLOW", "HOLD", "BLOCK"):
137
+ return EvaluationResult.from_dict(body)
138
+
139
+ if status_code in (200, 201, 202):
140
+ return EvaluationResult.from_dict(body)
141
+
142
+ raise self._parse_error(status_code, body, request_id)
143
+
144
+ def evaluate_safely(
145
+ self,
146
+ action: Optional[Union[str, Dict[str, Any]]] = None,
147
+ **kwargs: Any,
148
+ ) -> EvaluationResult:
149
+ """Safe evaluation wrapper with strict fail-closed guarantee.
150
+
151
+ Never raises exceptions for network failures or gateway outages;
152
+ strictly returns an authoritative BLOCK decision.
153
+ """
154
+ try:
155
+ return self.evaluate(action, **kwargs)
156
+ except Exception as exc: # pylint: disable=broad-except
157
+ reason = str(exc)
158
+ return EvaluationResult(
159
+ status="BLOCK",
160
+ decision="BLOCK",
161
+ is_allowed=False,
162
+ is_held=False,
163
+ is_blocked=True,
164
+ reason=f"FAIL_CLOSED: {reason}",
165
+ violations=[f"Gateway communication error: {reason}"],
166
+ request_id=f"fail_closed_{int(time.time() * 1000)}",
167
+ timestamp=time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
168
+ )
169
+
170
+ def get_approval(self, approval_id: str) -> ApprovalRecord:
171
+ """Fetch human review approval record by ID."""
172
+ if not approval_id or not isinstance(approval_id, str):
173
+ raise AgentGuardError("approval_id is required.")
174
+
175
+ encoded_id = urllib.parse.quote(approval_id)
176
+ status_code, body = self._request("GET", f"/api/v1/approvals/{encoded_id}")
177
+
178
+ if status_code == 200 and isinstance(body, dict):
179
+ return ApprovalRecord.from_dict(body)
180
+
181
+ raise self._parse_error(status_code, body)
182
+
183
+ def wait_for_approval(
184
+ self,
185
+ approval_id: str,
186
+ poll_interval: float = 1.0,
187
+ timeout: float = 30.0,
188
+ ) -> ApprovalRecord:
189
+ """Poll a held transaction until approved, rejected, or timed out."""
190
+ start_time = time.time()
191
+ while time.time() - start_time < timeout:
192
+ record = self.get_approval(approval_id)
193
+ if record.status != "PENDING":
194
+ return record
195
+ time.sleep(poll_interval)
196
+
197
+ raise AgentGuardTimeoutError(
198
+ f"Timed out waiting for approval on '{approval_id}' after {timeout} seconds."
199
+ )
200
+
201
+ def scan_prompt(self, input_text: str) -> PromptScanResult:
202
+ """Scan prompt context for adversarial prompt injection heuristics."""
203
+ status_code, body = self._request("POST", "/api/v1/scan", payload={"input": input_text})
204
+ if status_code == 200 and isinstance(body, dict):
205
+ return PromptScanResult.from_dict(body)
206
+ raise self._parse_error(status_code, body)
207
+
208
+ def _request(
209
+ self,
210
+ method: str,
211
+ path: str,
212
+ payload: Optional[Dict[str, Any]] = None,
213
+ extra_headers: Optional[Dict[str, str]] = None,
214
+ ) -> tuple[int, Any]:
215
+ url = f"{self.base_url}{path}"
216
+ headers = dict(self.default_headers)
217
+ if extra_headers:
218
+ headers.update(extra_headers)
219
+
220
+ data = json.dumps(payload).encode("utf-8") if payload is not None else None
221
+ req = urllib.request.Request(url, data=data, headers=headers, method=method)
222
+
223
+ try:
224
+ with urllib.request.urlopen(req, timeout=self.timeout) as response:
225
+ status_code = response.status
226
+ response_text = response.read().decode("utf-8")
227
+ try:
228
+ return status_code, json.loads(response_text)
229
+ except Exception:
230
+ return status_code, {"raw": response_text}
231
+ except urllib.error.HTTPError as exc:
232
+ status_code = exc.code
233
+ try:
234
+ error_body = json.loads(exc.read().decode("utf-8"))
235
+ except Exception:
236
+ error_body = {"error": exc.reason}
237
+ return status_code, error_body
238
+ except urllib.error.URLError as exc:
239
+ reason = str(exc.reason)
240
+ if "timed out" in reason.lower() or isinstance(getattr(exc, "reason", None), (socket.timeout, TimeoutError)):
241
+ raise AgentGuardTimeoutError(
242
+ f"AgentGuard request timed out after {self.timeout}s: {reason}"
243
+ ) from exc
244
+ raise AgentGuardGatewayError(
245
+ f"Failed to connect to AgentGuard gateway at {self.base_url}: {reason}",
246
+ status_code=503,
247
+ ) from exc
248
+ except (socket.timeout, TimeoutError) as exc:
249
+ raise AgentGuardTimeoutError(
250
+ f"AgentGuard request timed out after {self.timeout}s: {exc}"
251
+ ) from exc
252
+ except Exception as exc:
253
+ raise AgentGuardGatewayError(f"Unexpected error communicating with AgentGuard: {exc}") from exc
254
+
255
+ def _parse_error(self, status_code: int, body: Any, request_id: Optional[str] = None) -> AgentGuardError:
256
+ message = (
257
+ body.get("message")
258
+ or body.get("error")
259
+ or f"AgentGuard request failed with HTTP {status_code}"
260
+ if isinstance(body, dict)
261
+ else f"AgentGuard request failed with HTTP {status_code}"
262
+ )
263
+ details = body.get("details") if isinstance(body, dict) else None
264
+
265
+ if status_code == 401:
266
+ return AgentGuardAuthenticationError(message, request_id=request_id, details=details)
267
+ if status_code == 403:
268
+ return AgentGuardForbiddenError(message, request_id=request_id, details=details)
269
+ if status_code == 400:
270
+ return AgentGuardValidationError(message, request_id=request_id, details=details)
271
+ if status_code == 404:
272
+ return AgentGuardNotFoundError(message, request_id=request_id, details=details)
273
+ if status_code == 429:
274
+ return AgentGuardRateLimitError(message, request_id=request_id, details=details)
275
+ if status_code >= 500:
276
+ return AgentGuardGatewayError(message, status_code=status_code, request_id=request_id, details=details)
277
+
278
+ return AgentGuardError(message, status_code=status_code, request_id=request_id, details=details)
@@ -0,0 +1,120 @@
1
+ """AgentGuard Python SDK Exception Hierarchy."""
2
+
3
+ from typing import Any, Optional
4
+
5
+
6
+ class AgentGuardError(Exception):
7
+ """Base exception for all AgentGuard errors."""
8
+
9
+ def __init__(
10
+ self,
11
+ message: str,
12
+ status_code: Optional[int] = None,
13
+ error_code: Optional[str] = None,
14
+ request_id: Optional[str] = None,
15
+ details: Optional[Any] = None,
16
+ ):
17
+ super().__init__(message)
18
+ self.message = message
19
+ self.status_code = status_code
20
+ self.error_code = error_code
21
+ self.request_id = request_id
22
+ self.details = details
23
+
24
+ @property
25
+ def status(self) -> Optional[int]:
26
+ """HTTP status code alias."""
27
+ return self.status_code
28
+
29
+ @property
30
+ def code(self) -> Optional[str]:
31
+ """Error code alias."""
32
+ return self.error_code
33
+
34
+ def __str__(self) -> str:
35
+ req_suffix = f" [Request ID: {self.request_id}]" if self.request_id else ""
36
+ return f"{self.message}{req_suffix}"
37
+
38
+
39
+ class AgentGuardAuthenticationError(AgentGuardError):
40
+ """Raised when authentication fails (HTTP 401: missing, expired, or revoked API key)."""
41
+
42
+ def __init__(
43
+ self,
44
+ message: str = "Authentication failed. Please verify your AgentGuard API key.",
45
+ request_id: Optional[str] = None,
46
+ details: Optional[Any] = None,
47
+ ):
48
+ super().__init__(message, status_code=401, error_code="AUTHENTICATION_FAILED", request_id=request_id, details=details)
49
+
50
+
51
+ class AgentGuardForbiddenError(AgentGuardError):
52
+ """Raised when permissions are insufficient (HTTP 403: API key lacks required scope)."""
53
+
54
+ def __init__(
55
+ self,
56
+ message: str = "Permission denied. The API key lacks the necessary permissions.",
57
+ request_id: Optional[str] = None,
58
+ details: Optional[Any] = None,
59
+ ):
60
+ super().__init__(message, status_code=403, error_code="FORBIDDEN", request_id=request_id, details=details)
61
+
62
+
63
+ class AgentGuardValidationError(AgentGuardError):
64
+ """Raised when request parameters fail schema validation (HTTP 400)."""
65
+
66
+ def __init__(
67
+ self,
68
+ message: str = "Invalid request parameters.",
69
+ request_id: Optional[str] = None,
70
+ details: Optional[Any] = None,
71
+ ):
72
+ super().__init__(message, status_code=400, error_code="VALIDATION_ERROR", request_id=request_id, details=details)
73
+
74
+
75
+ class AgentGuardNotFoundError(AgentGuardError):
76
+ """Raised when a requested resource (e.g., approval ID) is not found (HTTP 404)."""
77
+
78
+ def __init__(
79
+ self,
80
+ message: str = "Requested resource not found.",
81
+ request_id: Optional[str] = None,
82
+ details: Optional[Any] = None,
83
+ ):
84
+ super().__init__(message, status_code=404, error_code="NOT_FOUND", request_id=request_id, details=details)
85
+
86
+
87
+ class AgentGuardRateLimitError(AgentGuardError):
88
+ """Raised when the gateway rate limit is exceeded (HTTP 429)."""
89
+
90
+ def __init__(
91
+ self,
92
+ message: str = "Rate limit exceeded. Please back off before retrying.",
93
+ request_id: Optional[str] = None,
94
+ details: Optional[Any] = None,
95
+ ):
96
+ super().__init__(message, status_code=429, error_code="RATE_LIMIT_EXCEEDED", request_id=request_id, details=details)
97
+
98
+
99
+ class AgentGuardTimeoutError(AgentGuardError):
100
+ """Raised when an HTTP request or approval wait times out."""
101
+
102
+ def __init__(
103
+ self,
104
+ message: str = "AgentGuard evaluation request timed out.",
105
+ request_id: Optional[str] = None,
106
+ ):
107
+ super().__init__(message, status_code=408, error_code="TIMEOUT", request_id=request_id)
108
+
109
+
110
+ class AgentGuardGatewayError(AgentGuardError):
111
+ """Raised when the AgentGuard gateway is unreachable or returns HTTP 5xx."""
112
+
113
+ def __init__(
114
+ self,
115
+ message: str = "AgentGuard gateway error or service unavailable.",
116
+ status_code: int = 500,
117
+ request_id: Optional[str] = None,
118
+ details: Optional[Any] = None,
119
+ ):
120
+ super().__init__(message, status_code=status_code, error_code="GATEWAY_ERROR", request_id=request_id, details=details)
agentguard/models.py ADDED
@@ -0,0 +1,122 @@
1
+ """AgentGuard Python SDK Data Models."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Dict, List, Optional
5
+
6
+
7
+ @dataclass
8
+ class EvaluationResult:
9
+ """Authoritative decision returned by the AgentGuard security gateway."""
10
+
11
+ status: str
12
+ decision: str
13
+ is_allowed: bool
14
+ is_held: bool
15
+ is_blocked: bool
16
+ reason: str
17
+ violations: List[str] = field(default_factory=list)
18
+ request_id: str = ""
19
+ policy_id: Optional[str] = None
20
+ policy_version: Optional[int] = None
21
+ pending_approval_id: Optional[str] = None
22
+ authorization_token: Optional[str] = None
23
+ amount_formatted: Optional[str] = None
24
+ amount_cents: Optional[str] = None
25
+ currency: Optional[str] = None
26
+ timestamp: str = ""
27
+ metadata: Optional[Dict[str, Any]] = None
28
+
29
+ @classmethod
30
+ def from_dict(cls, data: Dict[str, Any]) -> "EvaluationResult":
31
+ status = data.get("status") or data.get("decision", "BLOCK")
32
+ return cls(
33
+ status=status,
34
+ decision=status,
35
+ is_allowed=(status == "ALLOW"),
36
+ is_held=(status == "HOLD"),
37
+ is_blocked=(status == "BLOCK"),
38
+ reason=data.get("reason", "No reason provided by gateway"),
39
+ violations=data.get("violations", []),
40
+ request_id=data.get("requestId", ""),
41
+ policy_id=data.get("policyId"),
42
+ policy_version=data.get("policyVersion"),
43
+ pending_approval_id=data.get("pendingApprovalId"),
44
+ authorization_token=data.get("authorizationToken"),
45
+ amount_formatted=data.get("amountFormatted"),
46
+ amount_cents=data.get("amountCents"),
47
+ currency=data.get("currency"),
48
+ timestamp=data.get("timestamp", ""),
49
+ metadata=data.get("metadata"),
50
+ )
51
+
52
+
53
+ @dataclass
54
+ class ApprovalRecord:
55
+ """Human-in-the-Loop approval record for held transactions."""
56
+
57
+ id: str
58
+ request_id: str
59
+ agent_id: str
60
+ policy_id: str
61
+ policy_version: int
62
+ tool: str
63
+ operation: str
64
+ amount_formatted: str
65
+ amount_cents: str
66
+ currency: str
67
+ merchant: str
68
+ description: str
69
+ status: str
70
+ risk_reasons: List[str] = field(default_factory=list)
71
+ server_authorization_token: Optional[str] = None
72
+ token_expires_at: Optional[str] = None
73
+ reviewed_by: Optional[str] = None
74
+ reviewed_at: Optional[str] = None
75
+ rejection_reason: Optional[str] = None
76
+ created_at: str = ""
77
+
78
+ @classmethod
79
+ def from_dict(cls, data: Dict[str, Any]) -> "ApprovalRecord":
80
+ return cls(
81
+ id=data.get("id", ""),
82
+ request_id=data.get("requestId", ""),
83
+ agent_id=data.get("agentId", ""),
84
+ policy_id=data.get("policyId", ""),
85
+ policy_version=data.get("policyVersion", 1),
86
+ tool=data.get("tool", ""),
87
+ operation=data.get("operation", ""),
88
+ amount_formatted=data.get("amountFormatted", ""),
89
+ amount_cents=data.get("amountCents", "0"),
90
+ currency=data.get("currency", "USD"),
91
+ merchant=str(data.get("merchant", "")),
92
+ description=data.get("description", ""),
93
+ status=data.get("status", "PENDING"),
94
+ risk_reasons=data.get("riskReasons", []),
95
+ server_authorization_token=data.get("serverAuthorizationToken"),
96
+ token_expires_at=data.get("tokenExpiresAt"),
97
+ reviewed_by=data.get("reviewedBy"),
98
+ reviewed_at=data.get("reviewedAt"),
99
+ rejection_reason=data.get("rejectionReason"),
100
+ created_at=data.get("createdAt", ""),
101
+ )
102
+
103
+
104
+ @dataclass
105
+ class PromptScanResult:
106
+ """Result of prompt injection heuristic scanning."""
107
+
108
+ is_threat_detected: bool
109
+ risk_score: float
110
+ threat_level: str
111
+ findings: List[Dict[str, Any]] = field(default_factory=list)
112
+ sanitized_input: str = ""
113
+
114
+ @classmethod
115
+ def from_dict(cls, data: Dict[str, Any]) -> "PromptScanResult":
116
+ return cls(
117
+ is_threat_detected=bool(data.get("isThreatDetected", False)),
118
+ risk_score=float(data.get("riskScore", 0.0)),
119
+ threat_level=data.get("threatLevel", "NONE"),
120
+ findings=data.get("findings", []),
121
+ sanitized_input=data.get("sanitizedInput", ""),
122
+ )
agentguard/py.typed ADDED
@@ -0,0 +1 @@
1
+ # Marker file for PEP 561. The agentguard package uses inline type annotations.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CodeSec AI / AgentGuard Authors
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,96 @@
1
+ Metadata-Version: 2.1
2
+ Name: codesec-agentguard
3
+ Version: 1.0.0
4
+ Summary: Official Python SDK for the AgentGuard AI Agent Security Gateway
5
+ Home-page: https://github.com/altafpasha/agentguard
6
+ Author: CodeSec AI / AgentGuard Security Team
7
+ Author-email: security@agentguard.dev
8
+ License: MIT
9
+ Platform: UNKNOWN
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+
14
+ # codesec-agentguard (Python SDK)
15
+
16
+ Official Python SDK for the **AgentGuard** AI Agent Security Gateway.
17
+
18
+ AgentGuard intercepts, evaluates, and verifies autonomous AI agent actions against deterministic spending limits, currency controls, capability whitelists, and adversarial prompt injection heuristic scans before downstream payment APIs (such as Razorpay or PayPal) are executed.
19
+
20
+ ---
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ pip install codesec-agentguard
26
+ ```
27
+
28
+ Or install locally in editable mode:
29
+
30
+ ```bash
31
+ pip install -e packages/python-sdk
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Quickstart
37
+
38
+ ```python
39
+ import os
40
+ from agentguard import AgentGuard
41
+
42
+ # Initialize client
43
+ guard = AgentGuard(
44
+ base_url=os.environ.get("AGENTGUARD_URL", "http://localhost:3001"),
45
+ api_key=os.environ["AGENTGUARD_API_KEY"],
46
+ timeout=5.0,
47
+ )
48
+
49
+ # Pre-flight evaluation
50
+ decision = guard.evaluate({
51
+ "action": "payment.create",
52
+ "amount": 1200.0,
53
+ "currency": "INR",
54
+ "recipient": "merchant@store.com",
55
+ "description": "Autonomous grocery restocking order",
56
+ "prompt_context": "User prompt: Restock organic coffee beans.",
57
+ })
58
+
59
+ if decision.status == "ALLOW":
60
+ print(f"Action permitted! Policy version: {decision.policy_version}")
61
+ # Safe to initiate Razorpay order
62
+ elif decision.status == "HOLD":
63
+ print(f"Action held for human review: {decision.pending_approval_id}")
64
+ # Wait for operator in AgentGuard dashboard
65
+ else:
66
+ print(f"Blocked by AgentGuard: {decision.reason}")
67
+ for violation in decision.violations:
68
+ print(f" - {violation}")
69
+ ```
70
+
71
+ ### Context Manager Usage
72
+
73
+ ```python
74
+ with AgentGuard(base_url="http://localhost:3001", api_key="ag_live_...") as guard:
75
+ decision = guard.evaluate(action="payment.create", amount=25.0)
76
+ if decision.is_allowed:
77
+ pass
78
+ ```
79
+
80
+ ### Strict Fail-Closed Guarantee
81
+
82
+ ```python
83
+ # evaluate_safely catches all timeouts, DNS failures, and gateway crashes,
84
+ # strictly returning an authoritative BLOCK decision so your code never fails open.
85
+ decision = guard.evaluate_safely(action="payment.create", amount=50.0)
86
+ if not decision.is_allowed:
87
+ print(f"Transaction denied: {decision.reason}")
88
+ ```
89
+
90
+ ---
91
+
92
+ ## License
93
+
94
+ MIT © AgentGuard Security Team
95
+
96
+
@@ -0,0 +1,10 @@
1
+ agentguard/__init__.py,sha256=vp0-ZYB4knyzevaZOYP5IJB65ivk5KXI58qwGvH7GVc,766
2
+ agentguard/client.py,sha256=wmBVsmqtZ6kwE-JsYAHLHzgkOtJao4gzNVmmyA2UsRI,11338
3
+ agentguard/exceptions.py,sha256=yWHSVNmWXa95x2ZLEiCb03G4UYs4IxRko4aNJjYpzPg,4108
4
+ agentguard/models.py,sha256=6F9iFPIpOgGvFH97GNej98uVKvOAqXQjMR4wSj74qC8,4277
5
+ agentguard/py.typed,sha256=jKVcLoqUzJAhSwO-U3ztJHFft9oHMcj4681w24jTnac,80
6
+ codesec_agentguard-1.0.0.dist-info/LICENSE,sha256=wrpoyW6mMh07hQj-FUo87mBKRJMC-ekqVTR5t7NmMEc,1088
7
+ codesec_agentguard-1.0.0.dist-info/METADATA,sha256=yoFYTbByf70_Jw_EGO5TN9lv8lEQBSxgDyXf4_cIsK4,2561
8
+ codesec_agentguard-1.0.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
9
+ codesec_agentguard-1.0.0.dist-info/top_level.txt,sha256=rEW5sAnjxXRs63ja2psmsMRMJJfi_3EAxf7crl1nHyw,11
10
+ codesec_agentguard-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.37.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ agentguard