agentrust-py 0.0.3a1__tar.gz → 0.0.4__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.
Files changed (73) hide show
  1. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/PKG-INFO +4 -4
  2. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/__init__.py +1 -1
  3. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/adapters/crewai.py +5 -5
  4. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/adapters/langgraph.py +6 -3
  5. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/autoload.py +1 -1
  6. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/client.py +43 -63
  7. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/config.py +1 -42
  8. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/models.py +8 -0
  9. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/pyproject.toml +3 -3
  10. agentrust_py-0.0.4/tests/_cfg.py +32 -0
  11. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/tests/conftest.py +14 -17
  12. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/tests/test_adapters.py +33 -28
  13. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/tests/test_async_advanced.py +71 -63
  14. agentrust_py-0.0.4/tests/test_callback_url.py +184 -0
  15. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/tests/test_chaos.py +17 -15
  16. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/tests/test_client.py +16 -15
  17. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/tests/test_config.py +1 -1
  18. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/tests/test_decorator.py +4 -2
  19. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/tests/test_embedded.py +10 -3
  20. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/tests/test_hooks.py +41 -41
  21. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/.gitignore +0 -0
  22. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/CHANGELOG.md +0 -0
  23. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/LICENSE +0 -0
  24. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/README.md +0 -0
  25. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust/__init__.py +0 -0
  26. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/_evaluations.py +0 -0
  27. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/_real_validator.py +0 -0
  28. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/adapters/__init__.py +0 -0
  29. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/adapters/autogen.py +0 -0
  30. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/adapters/claude_agents.py +0 -0
  31. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/adapters/mcp.py +0 -0
  32. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/adapters/openai_agents.py +0 -0
  33. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/audit/__init__.py +0 -0
  34. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/audit/logger.py +0 -0
  35. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/auth.py +0 -0
  36. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/auto.py +0 -0
  37. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/classifier/__init__.py +0 -0
  38. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/classifier/engine.py +0 -0
  39. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/classifier/profiles.py +0 -0
  40. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/classifier/system_prompt.md +0 -0
  41. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/cli.py +0 -0
  42. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/cli_certify.py +0 -0
  43. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/decorator.py +0 -0
  44. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/embedded.py +0 -0
  45. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/golden_suite.py +0 -0
  46. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/governance/__init__.py +0 -0
  47. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/governance/hitl.py +0 -0
  48. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/governance/kernel.py +0 -0
  49. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/governance/kill_switch.py +0 -0
  50. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/governance/modes.py +0 -0
  51. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/hooks.py +0 -0
  52. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/manifest.py +0 -0
  53. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/notifications/__init__.py +0 -0
  54. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/notifications/discord.py +0 -0
  55. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/notifications/dispatcher.py +0 -0
  56. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/notifications/slack.py +0 -0
  57. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/policy/__init__.py +0 -0
  58. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/policy/backends/__init__.py +0 -0
  59. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/policy/backends/native.py +0 -0
  60. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/policy/backends/opa.py +0 -0
  61. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/policy/engine.py +0 -0
  62. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/policy/loader.py +0 -0
  63. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/py.typed +0 -0
  64. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/queue_replay.py +0 -0
  65. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/tiers.py +0 -0
  66. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/tool_tracking.py +0 -0
  67. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/version_negotiation.py +0 -0
  68. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/agentrust_sdk/webhooks.py +0 -0
  69. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/tests/__init__.py +0 -0
  70. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/tests/test_cli_certify.py +0 -0
  71. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/tests/test_golden_suite.py +0 -0
  72. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/tests/test_manifest.py +0 -0
  73. {agentrust_py-0.0.3a1 → agentrust_py-0.0.4}/tests/test_webhooks.py +0 -0
@@ -1,11 +1,11 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: agentrust-py
3
- Version: 0.0.3a1
3
+ Version: 0.0.4
4
4
  Summary: AgentTrust Edge — Full Governance Platform. Policy Engine + MAD Classifier + HITL + Kill Switch.
5
5
  Project-URL: Homepage, https://agentrust.io
6
6
  Project-URL: Documentation, https://docs.agentrust.io
7
- Project-URL: Repository, https://github.com/agentrust/agentrust
8
- Project-URL: Changelog, https://github.com/agentrust/agentrust/blob/main/CHANGELOG.md
7
+ Project-URL: Repository, https://github.com/agent-trust-os/agent-trust-sdk
8
+ Project-URL: Changelog, https://github.com/agent-trust-os/agent-trust-sdk/blob/main/CHANGELOG.md
9
9
  License: Apache-2.0
10
10
  License-File: LICENSE
11
11
  Keywords: agents,ai,audit,compliance,governance,llm,safety,trust
@@ -145,4 +145,4 @@ __all__ = [
145
145
  "validate",
146
146
  ]
147
147
 
148
- __version__ = "0.0.3a1"
148
+ __version__ = "0.0.4"
@@ -8,9 +8,6 @@ from __future__ import annotations
8
8
  import logging
9
9
  from typing import Any, Callable
10
10
 
11
- from agentrust_sdk.client import AgentTrustClient
12
- from agentrust_sdk.decorator import BlockedError, _check_decision
13
-
14
11
  logger = logging.getLogger(__name__)
15
12
 
16
13
 
@@ -51,7 +48,7 @@ class AgentTrustCallback:
51
48
  base_url: str = "http://localhost:8000",
52
49
  api_key: str | None = None,
53
50
  user: str = "crewai",
54
- block_on_block: bool = False,
51
+ block_on_block: bool = True,
55
52
  block_on_review: bool = False,
56
53
  framework: str = "CrewAI",
57
54
  ) -> None:
@@ -66,6 +63,9 @@ class AgentTrustCallback:
66
63
  self._last_result: Any = None
67
64
 
68
65
  def on_task_complete(self, output: Any) -> Any:
66
+ from agentrust_sdk.client import AgentTrustClient
67
+ from agentrust_sdk.decorator import BlockedError, _check_decision
68
+
69
69
  raw = output
70
70
  if hasattr(output, "raw"):
71
71
  raw = output.raw
@@ -80,8 +80,8 @@ class AgentTrustCallback:
80
80
  output=payload,
81
81
  framework=self._framework,
82
82
  )
83
- self._last_result = resp.model_dump()
84
83
  _check_decision(resp, self._block_on_block, self._block_on_review)
84
+ self._last_result = resp
85
85
  except BlockedError:
86
86
  raise
87
87
  except Exception as exc:
@@ -8,9 +8,6 @@ from __future__ import annotations
8
8
  import logging
9
9
  from typing import Any
10
10
 
11
- from agentrust_sdk.client import AgentTrustClient, AsyncAgentTrustClient
12
- from agentrust_sdk.decorator import BlockedError, _check_decision
13
-
14
11
  logger = logging.getLogger(__name__)
15
12
 
16
13
 
@@ -68,6 +65,9 @@ class AgentTrustNode:
68
65
  self._framework = framework
69
66
 
70
67
  def __call__(self, state: dict[str, Any]) -> dict[str, Any]:
68
+ from agentrust_sdk.client import AgentTrustClient
69
+ from agentrust_sdk.decorator import BlockedError, _check_decision
70
+
71
71
  output = state.get(self._output_key, {})
72
72
  if not isinstance(output, dict):
73
73
  output = {"result": str(output)}
@@ -90,6 +90,9 @@ class AgentTrustNode:
90
90
  return state
91
91
 
92
92
  async def ainvoke(self, state: dict[str, Any]) -> dict[str, Any]:
93
+ from agentrust_sdk.client import AsyncAgentTrustClient
94
+ from agentrust_sdk.decorator import BlockedError, _check_decision
95
+
93
96
  output = state.get(self._output_key, {})
94
97
  if not isinstance(output, dict):
95
98
  output = {"result": str(output)}
@@ -28,7 +28,7 @@ Three ways to activate without editing application code
28
28
 
29
29
  **5. `[autoload]` setuptools extra:**
30
30
 
31
- pip install agentrust-sdk[autoload]
31
+ pip install agentrust-py[autoload]
32
32
 
33
33
  Then in your app's own `sitecustomize.py` or startup:
34
34
  import agentrust_sdk.autoload
@@ -64,6 +64,7 @@ except ImportError:
64
64
  try:
65
65
  from tenacity import (
66
66
  retry,
67
+ retry_if_exception_type,
67
68
  stop_after_attempt,
68
69
  wait_exponential,
69
70
  )
@@ -123,14 +124,16 @@ def _build_gateway_payload(req: ValidateRequest) -> dict[str, Any]:
123
124
  "sdk_version": SDK_CONFIG.sdk_version,
124
125
  "sdk_min_gateway_version": SDK_CONFIG.min_gateway_version,
125
126
  }
127
+ if req.callback_url:
128
+ payload["callback_url"] = req.callback_url
126
129
  return payload
127
130
 
128
131
 
129
132
  def _build_pre_check_payload(
130
133
  agent_id: str, action: str, params: dict[str, Any], user: str, input: str,
131
- framework: str, model: str,
134
+ framework: str, model: str, callback_url: str | None = None,
132
135
  ) -> dict[str, Any]:
133
- return {
136
+ payload = {
134
137
  "agent_id": agent_id,
135
138
  "framework": framework,
136
139
  "action": action,
@@ -139,11 +142,15 @@ def _build_pre_check_payload(
139
142
  "input": input,
140
143
  "model": model,
141
144
  }
145
+ if callback_url:
146
+ payload["callback_url"] = callback_url
147
+ return payload
142
148
 
143
149
 
144
150
  def _build_post_check_payload(
145
151
  pre_check_envelope_id: str, output: dict[str, Any], model: str,
146
152
  tools_called: list[dict[str, Any]], latency_ms: float, tokens: int,
153
+ callback_url: str | None = None,
147
154
  ) -> dict[str, Any]:
148
155
  tool_results = [
149
156
  {
@@ -155,7 +162,7 @@ def _build_post_check_payload(
155
162
  for t in tools_called
156
163
  if t.get("result") is not None or t.get("error") is not None
157
164
  ]
158
- return {
165
+ payload = {
159
166
  "pre_check_envelope_id": pre_check_envelope_id,
160
167
  "execution": {
161
168
  "model": model,
@@ -167,6 +174,9 @@ def _build_post_check_payload(
167
174
  "output": output,
168
175
  "sdk_version": SDK_CONFIG.sdk_version,
169
176
  }
177
+ if callback_url:
178
+ payload["callback_url"] = callback_url
179
+ return payload
170
180
 
171
181
 
172
182
  def _warn_tier(capability: Capability) -> None:
@@ -175,7 +185,7 @@ def _warn_tier(capability: Capability) -> None:
175
185
 
176
186
 
177
187
  def _noop_response() -> ValidateResponse:
178
- """Return a pass-through ValidateResponse used when SDK is disabled (kill-switch)."""
188
+ """Return a pass-through ValidateResponse used when SDK is disabled."""
179
189
  from .models import DecisionResult, RiskResult, ValidationResult
180
190
  return ValidateResponse(
181
191
  envelope_id="disabled",
@@ -187,19 +197,6 @@ def _noop_response() -> ValidateResponse:
187
197
  )
188
198
 
189
199
 
190
- def _unavailable_response() -> ValidateResponse:
191
- """Return a pass-through ValidateResponse used when gateway is unreachable."""
192
- from .models import DecisionResult, RiskResult, ValidationResult
193
- return ValidateResponse(
194
- envelope_id="unavailable",
195
- validation=ValidationResult(schema_score=100.0, final_confidence=100.0, failures=[]),
196
- risk=RiskResult(),
197
- decision=DecisionResult(outcome="approve", reason="AgentTrust gateway unreachable — fail-open"),
198
- latency_ms=0.0,
199
- tier_info="unavailable",
200
- )
201
-
202
-
203
200
  # ---------------------------------------------------------------------------
204
201
  # Local queue for failure_mode=queue
205
202
  # ---------------------------------------------------------------------------
@@ -259,7 +256,7 @@ def drain_queue(
259
256
 
260
257
  headers: dict[str, str] = {
261
258
  "Content-Type": "application/json",
262
- "X-Agentrust-SDK-Version": SDK_CONFIG.sdk_version,
259
+ "X-AgentTrust-SDK-Version": SDK_CONFIG.sdk_version,
263
260
  }
264
261
  if _key:
265
262
  headers["X-AgentTrust-Token"] = _key
@@ -288,22 +285,12 @@ def drain_queue(
288
285
  # Retry helper
289
286
  # ---------------------------------------------------------------------------
290
287
 
291
- def _is_retryable_error(exc: Exception) -> bool:
292
- """Return True for transport/timeout errors and 5xx HTTP status errors."""
293
- if isinstance(exc, (httpx.TransportError, httpx.TimeoutException)):
294
- return True
295
- if isinstance(exc, httpx.HTTPStatusError) and exc.response.status_code >= 500:
296
- return True
297
- return False
298
-
299
-
300
288
  def _make_sync_retry():
301
289
  """Return a tenacity retry decorator for sync HTTP calls, or identity if tenacity missing."""
302
290
  if not _HAS_TENACITY:
303
291
  return lambda f: f
304
- from tenacity import retry_if_exception
305
292
  return retry(
306
- retry=retry_if_exception(_is_retryable_error),
293
+ retry=retry_if_exception_type((httpx.TransportError, httpx.TimeoutException)),
307
294
  stop=stop_after_attempt(SDK_CONFIG.retry_attempts),
308
295
  wait=wait_exponential(multiplier=SDK_CONFIG.retry_backoff_sec, min=0.1, max=30),
309
296
  reraise=True,
@@ -313,9 +300,8 @@ def _make_sync_retry():
313
300
  def _make_async_retry():
314
301
  if not _HAS_TENACITY:
315
302
  return lambda f: f
316
- from tenacity import retry_if_exception
317
303
  return retry(
318
- retry=retry_if_exception(_is_retryable_error),
304
+ retry=retry_if_exception_type((httpx.TransportError, httpx.TimeoutException)),
319
305
  stop=stop_after_attempt(SDK_CONFIG.retry_attempts),
320
306
  wait=wait_exponential(multiplier=SDK_CONFIG.retry_backoff_sec, min=0.1, max=30),
321
307
  reraise=True,
@@ -378,7 +364,7 @@ class AgentTrustClient:
378
364
 
379
365
  headers = {
380
366
  "Content-Type": "application/json",
381
- "X-Agentrust-SDK-Version": SDK_CONFIG.sdk_version,
367
+ "X-AgentTrust-SDK-Version": SDK_CONFIG.sdk_version,
382
368
  }
383
369
  if self._key_info.key:
384
370
  headers["X-AgentTrust-Token"] = self._key_info.key
@@ -458,12 +444,12 @@ class AgentTrustClient:
458
444
  framework: str = "REST",
459
445
  model: str = "unknown",
460
446
  tools_called: list[dict] | None = None,
461
- tool_results: list[dict] | None = None,
462
447
  latency_ms: float = 0.0,
463
448
  tokens: int = 0,
464
449
  parent_envelope_id: str | None = None,
465
450
  session_id: str | None = None,
466
451
  metadata: dict[str, Any] | None = None,
452
+ callback_url: str | None = None,
467
453
  ) -> ValidateResponse:
468
454
  # ── kill-switch ──────────────────────────────────────────────────────
469
455
  if not SDK_CONFIG.enabled:
@@ -484,15 +470,13 @@ class AgentTrustClient:
484
470
  latency_ms=latency_ms, tokens=tokens,
485
471
  parent_envelope_id=parent_envelope_id,
486
472
  session_id=session_id, metadata=metadata or {},
473
+ callback_url=callback_url,
487
474
  )
488
475
 
489
476
  if self._key_info.tier == Tier.OSS:
490
477
  return _oss_schema_only(req)
491
478
 
492
479
  payload = _build_gateway_payload(req)
493
- # If caller provided explicit tool_results, override the auto-derived ones.
494
- if tool_results is not None:
495
- payload["execution"]["tool_results"] = tool_results
496
480
  result = self._call_with_resilience(payload, req)
497
481
 
498
482
  # ── Webhook fan-out (Team tier and above) ────────────────────────────
@@ -526,6 +510,7 @@ class AgentTrustClient:
526
510
  input: str = "",
527
511
  framework: str = "REST",
528
512
  model: str = "unknown",
513
+ callback_url: str | None = None,
529
514
  ) -> ValidateResponse:
530
515
  """"Can we call this?" — deterministic pre-check, before the real call executes.
531
516
 
@@ -538,7 +523,7 @@ class AgentTrustClient:
538
523
  return _noop_response()
539
524
 
540
525
  payload = _build_pre_check_payload(
541
- agent_id, action, params or {}, user, input, framework, model
526
+ agent_id, action, params or {}, user, input, framework, model, callback_url
542
527
  )
543
528
 
544
529
  @_make_sync_retry()
@@ -547,7 +532,7 @@ class AgentTrustClient:
547
532
  if resp.status_code == 426:
548
533
  raise GatewayVersionError(
549
534
  f"Gateway requires SDK upgrade. Current SDK: {SDK_CONFIG.sdk_version}. "
550
- f"Upgrade: pip install --upgrade agentrust-sdk"
535
+ f"Upgrade: pip install --upgrade agentrust-py"
551
536
  )
552
537
  resp.raise_for_status()
553
538
  return ValidateResponse.model_validate(resp.json())
@@ -569,6 +554,7 @@ class AgentTrustClient:
569
554
  tools_called: list[dict[str, Any]] | None = None,
570
555
  latency_ms: float = 0.0,
571
556
  tokens: int = 0,
557
+ callback_url: str | None = None,
572
558
  ) -> ValidateResponse:
573
559
  """"Is this output safe to release?" — governs release only; it
574
560
  cannot undo whatever the pre-check already approved."""
@@ -576,7 +562,8 @@ class AgentTrustClient:
576
562
  return _noop_response()
577
563
 
578
564
  payload = _build_post_check_payload(
579
- pre_check_envelope_id, output or {}, model, tools_called or [], latency_ms, tokens
565
+ pre_check_envelope_id, output or {}, model, tools_called or [], latency_ms, tokens,
566
+ callback_url,
580
567
  )
581
568
 
582
569
  @_make_sync_retry()
@@ -585,7 +572,7 @@ class AgentTrustClient:
585
572
  if resp.status_code == 426:
586
573
  raise GatewayVersionError(
587
574
  f"Gateway requires SDK upgrade. Current SDK: {SDK_CONFIG.sdk_version}. "
588
- f"Upgrade: pip install --upgrade agentrust-sdk"
575
+ f"Upgrade: pip install --upgrade agentrust-py"
589
576
  )
590
577
  resp.raise_for_status()
591
578
  return ValidateResponse.model_validate(resp.json())
@@ -607,17 +594,13 @@ class AgentTrustClient:
607
594
  def _do_request() -> ValidateResponse:
608
595
  @_make_sync_retry()
609
596
  def _inner():
610
- resp = self._http.post(
611
- _VALIDATE_PATH,
612
- json=payload,
613
- headers={"X-Agent-ID": req.agent_id},
614
- )
597
+ resp = self._http.post(_VALIDATE_PATH, json=payload)
615
598
  # 426 = version mismatch
616
599
  if resp.status_code == 426:
617
600
  raise GatewayVersionError(
618
601
  f"Gateway requires SDK upgrade. "
619
602
  f"Current SDK: {SDK_CONFIG.sdk_version}. "
620
- f"Upgrade: pip install --upgrade agentrust-sdk"
603
+ f"Upgrade: pip install --upgrade agentrust-py"
621
604
  )
622
605
  resp.raise_for_status()
623
606
  return ValidateResponse.model_validate(resp.json())
@@ -662,7 +645,7 @@ class AgentTrustClient:
662
645
  _enqueue_failed_request(payload)
663
646
  else: # open (default)
664
647
  logger.warning("[AgentTrust] Gateway unreachable (fail-open): %s", exc)
665
- return _unavailable_response()
648
+ return _noop_response()
666
649
 
667
650
  def close(self) -> None:
668
651
  self._http.close()
@@ -715,7 +698,7 @@ class AsyncAgentTrustClient:
715
698
 
716
699
  headers = {
717
700
  "Content-Type": "application/json",
718
- "X-Agentrust-SDK-Version": SDK_CONFIG.sdk_version,
701
+ "X-AgentTrust-SDK-Version": SDK_CONFIG.sdk_version,
719
702
  }
720
703
  if self._key_info.key:
721
704
  headers["X-AgentTrust-Token"] = self._key_info.key
@@ -763,12 +746,12 @@ class AsyncAgentTrustClient:
763
746
  framework: str = "REST",
764
747
  model: str = "unknown",
765
748
  tools_called: list[dict] | None = None,
766
- tool_results: list[dict] | None = None,
767
749
  latency_ms: float = 0.0,
768
750
  tokens: int = 0,
769
751
  parent_envelope_id: str | None = None,
770
752
  session_id: str | None = None,
771
753
  metadata: dict[str, Any] | None = None,
754
+ callback_url: str | None = None,
772
755
  ) -> ValidateResponse:
773
756
  if not SDK_CONFIG.enabled:
774
757
  return _noop_response()
@@ -788,15 +771,13 @@ class AsyncAgentTrustClient:
788
771
  latency_ms=latency_ms, tokens=tokens,
789
772
  parent_envelope_id=parent_envelope_id,
790
773
  session_id=session_id, metadata=metadata or {},
774
+ callback_url=callback_url,
791
775
  )
792
776
 
793
777
  if self._key_info.tier == Tier.OSS:
794
778
  return _oss_schema_only(req)
795
779
 
796
780
  payload = _build_gateway_payload(req)
797
- # If caller provided explicit tool_results, override the auto-derived ones.
798
- if tool_results is not None:
799
- payload["execution"]["tool_results"] = tool_results
800
781
  result = await self._call_with_resilience(payload, req)
801
782
 
802
783
  # ── Async webhook fan-out (Team tier and above) ───────────────────────
@@ -831,13 +812,14 @@ class AsyncAgentTrustClient:
831
812
  input: str = "",
832
813
  framework: str = "REST",
833
814
  model: str = "unknown",
815
+ callback_url: str | None = None,
834
816
  ) -> ValidateResponse:
835
817
  """Async counterpart of AgentTrustClient.pre_check — see that docstring."""
836
818
  if not SDK_CONFIG.enabled:
837
819
  return _noop_response()
838
820
 
839
821
  payload = _build_pre_check_payload(
840
- agent_id, action, params or {}, user, input, framework, model
822
+ agent_id, action, params or {}, user, input, framework, model, callback_url
841
823
  )
842
824
 
843
825
  @_make_async_retry()
@@ -846,7 +828,7 @@ class AsyncAgentTrustClient:
846
828
  if resp.status_code == 426:
847
829
  raise GatewayVersionError(
848
830
  f"Gateway requires SDK upgrade. Current SDK: {SDK_CONFIG.sdk_version}. "
849
- f"Upgrade: pip install --upgrade agentrust-sdk"
831
+ f"Upgrade: pip install --upgrade agentrust-py"
850
832
  )
851
833
  resp.raise_for_status()
852
834
  return ValidateResponse.model_validate(resp.json())
@@ -868,13 +850,15 @@ class AsyncAgentTrustClient:
868
850
  tools_called: list[dict[str, Any]] | None = None,
869
851
  latency_ms: float = 0.0,
870
852
  tokens: int = 0,
853
+ callback_url: str | None = None,
871
854
  ) -> ValidateResponse:
872
855
  """Async counterpart of AgentTrustClient.post_check — see that docstring."""
873
856
  if not SDK_CONFIG.enabled:
874
857
  return _noop_response()
875
858
 
876
859
  payload = _build_post_check_payload(
877
- pre_check_envelope_id, output or {}, model, tools_called or [], latency_ms, tokens
860
+ pre_check_envelope_id, output or {}, model, tools_called or [], latency_ms, tokens,
861
+ callback_url,
878
862
  )
879
863
 
880
864
  @_make_async_retry()
@@ -883,7 +867,7 @@ class AsyncAgentTrustClient:
883
867
  if resp.status_code == 426:
884
868
  raise GatewayVersionError(
885
869
  f"Gateway requires SDK upgrade. Current SDK: {SDK_CONFIG.sdk_version}. "
886
- f"Upgrade: pip install --upgrade agentrust-sdk"
870
+ f"Upgrade: pip install --upgrade agentrust-py"
887
871
  )
888
872
  resp.raise_for_status()
889
873
  return ValidateResponse.model_validate(resp.json())
@@ -904,15 +888,11 @@ class AsyncAgentTrustClient:
904
888
  async def _do_request() -> ValidateResponse:
905
889
  @_make_async_retry()
906
890
  async def _inner():
907
- resp = await self._http.post(
908
- _VALIDATE_PATH,
909
- json=payload,
910
- headers={"X-Agent-ID": req.agent_id},
911
- )
891
+ resp = await self._http.post(_VALIDATE_PATH, json=payload)
912
892
  if resp.status_code == 426:
913
893
  raise GatewayVersionError(
914
894
  f"Gateway requires SDK upgrade. Current: {SDK_CONFIG.sdk_version}. "
915
- f"Upgrade: pip install --upgrade agentrust-sdk"
895
+ f"Upgrade: pip install --upgrade agentrust-py"
916
896
  )
917
897
  resp.raise_for_status()
918
898
  return ValidateResponse.model_validate(resp.json())
@@ -956,7 +936,7 @@ class AsyncAgentTrustClient:
956
936
  _enqueue_failed_request(payload)
957
937
  else:
958
938
  logger.warning("[AgentTrust] Async gateway unreachable (fail-open): %s", exc)
959
- return _unavailable_response()
939
+ return _noop_response()
960
940
 
961
941
  async def close(self) -> None:
962
942
  await self._http.aclose()
@@ -47,20 +47,9 @@ from pathlib import Path
47
47
  logger = logging.getLogger(__name__)
48
48
 
49
49
  _VALID_FAILURE_MODES = frozenset({"open", "closed", "queue"})
50
- _SDK_VERSION = "0.0.3a1"
50
+ _SDK_VERSION = "0.0.4"
51
51
  _MIN_GATEWAY_VERSION = "0.0.1a1"
52
52
 
53
- # Keys that tests may override via SDK_CONFIG.__dict__["key"] = value.
54
- # __getattribute__ checks instance __dict__ first for these keys so that
55
- # direct dict assignment works as an in-process override (tests and runtime
56
- # overrides) without needing to mutate os.environ.
57
- _OVERRIDABLE_KEYS = frozenset({
58
- "enabled", "gateway_url", "api_key", "timeout_sec", "retry_attempts",
59
- "retry_backoff_sec", "failure_mode", "queue_db",
60
- "webhook_url", "webhook_events", "webhook_db", "webhook_timeout",
61
- "webhook_auto_dispatch",
62
- })
63
-
64
53
 
65
54
  def _bool(key: str, default: bool) -> bool:
66
55
  val = os.environ.get(key, "").lower()
@@ -102,12 +91,6 @@ class _SDKConfig:
102
91
  on each attribute access so runtime mutations (secrets managers, test
103
92
  fixtures) take effect without a process restart.
104
93
 
105
- In-process overrides (for tests or runtime hot-patching) are supported by
106
- directly writing to the instance dict:
107
-
108
- SDK_CONFIG.__dict__["failure_mode"] = "closed" # overrides env var
109
- del SDK_CONFIG.__dict__["failure_mode"] # restore env-var reading
110
-
111
94
  Call reload() to invalidate any cached state (currently a no-op since
112
95
  nothing is cached, but provided for forward-compat and test ergonomics).
113
96
  """
@@ -116,31 +99,10 @@ class _SDKConfig:
116
99
  sdk_version: str = _SDK_VERSION
117
100
  min_gateway_version: str = _MIN_GATEWAY_VERSION
118
101
 
119
- # ---------------------------------------------------------------------------
120
- # Instance-dict override support
121
- # ---------------------------------------------------------------------------
122
-
123
- def __getattribute__(self, name: str): # type: ignore[override]
124
- # For the known config keys, honour a value stored directly in the
125
- # instance __dict__ first (written by tests or runtime overrides via
126
- # SDK_CONFIG.__dict__["key"] = value). This lets test code bypass
127
- # env-var mutation while keeping the singleton's lazy-env behaviour for
128
- # every key that has NOT been explicitly overridden.
129
- if name in _OVERRIDABLE_KEYS:
130
- instance_dict = object.__getattribute__(self, "__dict__")
131
- if name in instance_dict:
132
- return instance_dict[name]
133
- return object.__getattribute__(self, name)
134
-
135
102
  @property
136
103
  def enabled(self) -> bool:
137
104
  return _bool("AGENTRUST_ENABLED", True)
138
105
 
139
- @enabled.setter
140
- def enabled(self, value: bool) -> None:
141
- # Honour direct attribute assignment (used by monkeypatch.setattr).
142
- os.environ["AGENTRUST_ENABLED"] = "true" if value else "false"
143
-
144
106
  @property
145
107
  def gateway_url(self) -> str:
146
108
  return os.environ.get("AGENTRUST_GATEWAY_URL", "http://localhost:8000")
@@ -241,6 +203,3 @@ class _SDKConfig:
241
203
 
242
204
  # Module-level singleton — import as `from .config import SDK_CONFIG`
243
205
  SDK_CONFIG = _SDKConfig()
244
-
245
- # Public alias (some tests import `SDKConfig` without the leading underscore).
246
- SDKConfig = _SDKConfig
@@ -35,6 +35,11 @@ class DecisionResult(BaseModel):
35
35
  outcome: str = "pending" # "pending" = not computed (tier too low)
36
36
  reason: str = ""
37
37
  policy_version: str = ""
38
+ # Set by newer gateways; absent (None / False) from older ones.
39
+ mode: Optional[str] = None # alert | hitl | block, when the agent's mode applied
40
+ original_outcome: Optional[str] = None # what the engine decided before the mode changed it
41
+ reason_code: Optional[str] = None # e.g. low_confidence, tool_trust, mode_review
42
+ hard_gate: bool = False # True: no mode can soften this block
38
43
 
39
44
 
40
45
  class ValidateRequest(BaseModel):
@@ -51,6 +56,9 @@ class ValidateRequest(BaseModel):
51
56
  tokens: int = 0
52
57
  session_id: Optional[str] = None
53
58
  metadata: Dict[str, Any] = Field(default_factory=dict)
59
+ # Where the gateway should send one signed message if a person later decides
60
+ # on the review case this check creates. Optional.
61
+ callback_url: Optional[str] = None
54
62
 
55
63
 
56
64
  class ValidateResponse(BaseModel):
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agentrust-py"
7
- version = "0.0.3a1"
7
+ version = "0.0.4"
8
8
  description = "AgentTrust Edge — Full Governance Platform. Policy Engine + MAD Classifier + HITL + Kill Switch."
9
9
  readme = "README.md"
10
10
  license = { text = "Apache-2.0" }
@@ -75,8 +75,8 @@ agentrust = "agentrust_sdk.cli:main"
75
75
  [project.urls]
76
76
  Homepage = "https://agentrust.io"
77
77
  Documentation = "https://docs.agentrust.io"
78
- Repository = "https://github.com/agentrust/agentrust"
79
- Changelog = "https://github.com/agentrust/agentrust/blob/main/CHANGELOG.md"
78
+ Repository = "https://github.com/agent-trust-os/agent-trust-sdk"
79
+ Changelog = "https://github.com/agent-trust-os/agent-trust-sdk/blob/main/CHANGELOG.md"
80
80
 
81
81
  [tool.hatch.build.targets.wheel]
82
82
  packages = ["agentrust_sdk", "agentrust"]
@@ -0,0 +1,32 @@
1
+ """Helpers to override SDK_CONFIG values.
2
+
3
+ SDK_CONFIG properties are read-only and resolved from environment variables on
4
+ each access, so overrides go through os.environ. The autouse ``reset_env``
5
+ fixture in conftest.py restores the keys it manages; ``restore_env`` (autouse,
6
+ below in conftest) restores the rest.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from pathlib import Path
12
+
13
+ _ENV = {
14
+ "enabled": "AGENTRUST_ENABLED",
15
+ "failure_mode": "AGENTRUST_FAILURE_MODE",
16
+ "retry_attempts": "AGENTRUST_RETRY_ATTEMPTS",
17
+ "queue_db": "AGENTRUST_QUEUE_DB",
18
+ }
19
+
20
+
21
+ def set_cfg(name: str, value) -> None:
22
+ key = _ENV[name]
23
+ if value is None:
24
+ os.environ.pop(key, None)
25
+ elif isinstance(value, bool):
26
+ os.environ[key] = "true" if value else "false"
27
+ else:
28
+ os.environ[key] = str(value)
29
+
30
+
31
+ def unset_cfg(name: str) -> None:
32
+ os.environ.pop(_ENV[name], None)
@@ -4,18 +4,9 @@ from __future__ import annotations
4
4
  import os
5
5
  import pytest
6
6
 
7
- # Config keys that tests may override via SDK_CONFIG.__dict__ — cleared before
8
- # each test so no state leaks between tests.
9
- _SDK_CONFIG_OVERRIDE_KEYS = (
10
- "enabled", "gateway_url", "api_key", "timeout_sec", "retry_attempts",
11
- "retry_backoff_sec", "failure_mode", "queue_db",
12
- "webhook_url", "webhook_events", "webhook_db", "webhook_timeout",
13
- "webhook_auto_dispatch",
14
- )
15
-
16
7
 
17
8
  @pytest.fixture(autouse=True)
18
- def reset_env(monkeypatch):
9
+ def reset_env(monkeypatch, tmp_path_factory):
19
10
  """Ensure each test starts with a clean environment."""
20
11
  for key in (
21
12
  "AGENTRUST_ENABLED",
@@ -24,15 +15,15 @@ def reset_env(monkeypatch):
24
15
  "AGENTRUST_RETRY_ATTEMPTS",
25
16
  "AGENTRUST_GATEWAY_URL",
26
17
  "AGENTRUST_KEY",
18
+ "AGENTRUST_QUEUE_DB",
19
+ "AGENTRUST_RETRY_BACKOFF",
27
20
  ):
28
21
  monkeypatch.delenv(key, raising=False)
29
-
30
- # Clear any in-process SDK_CONFIG overrides left by previous tests.
31
- # These are set via SDK_CONFIG.__dict__["key"] = value in test bodies.
32
- from agentrust_sdk.config import SDK_CONFIG
33
- for key in _SDK_CONFIG_OVERRIDE_KEYS:
34
- SDK_CONFIG.__dict__.pop(key, None)
35
-
22
+ # Isolate from any developer key in ~/.agentrust/config.yaml or a project
23
+ # .agentrust/config.yaml so key-less (OSS) behaviour is deterministic.
24
+ from agentrust_sdk import auth
25
+ monkeypatch.setattr(auth, "_GLOBAL_CONFIG", tmp_path_factory.mktemp("noconf") / "config.yaml")
26
+ monkeypatch.setattr(auth, "_project_config", lambda: None)
36
27
  yield
37
28
 
38
29
 
@@ -78,3 +69,9 @@ def mock_block_response() -> dict:
78
69
  "confidence_rationale": None,
79
70
  "trust_chain": None,
80
71
  }
72
+
73
+
74
+ @pytest.fixture
75
+ def gateway_key(monkeypatch):
76
+ """A syntactically valid non-OSS key so clients take the HTTP path."""
77
+ monkeypatch.setenv("AGENTRUST_KEY", "at_free_test")