agenticdome-python-sdk 1.2.41__tar.gz → 1.2.45__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 (100) hide show
  1. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/PKG-INFO +15 -11
  2. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/README.md +11 -7
  3. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_python_sdk.egg-info/PKG-INFO +15 -11
  4. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/__init__.py +2 -1
  5. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/_framework_firewall.py +17 -6
  6. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/catalog_snapshot.json +7 -7
  7. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/client.py +64 -0
  8. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/hook_catalog.py +7 -1
  9. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/lifecycle.py +52 -18
  10. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/mcp_host.py +23 -4
  11. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/customer-onboarding.md +17 -0
  12. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/pyproject.toml +7 -7
  13. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/CONTRIBUTING.md +0 -0
  14. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/LICENSE +0 -0
  15. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/MANIFEST.in +0 -0
  16. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/NOTICE +0 -0
  17. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/SECURITY.md +0 -0
  18. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_python_sdk.egg-info/SOURCES.txt +0 -0
  19. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_python_sdk.egg-info/dependency_links.txt +0 -0
  20. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_python_sdk.egg-info/entry_points.txt +0 -0
  21. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_python_sdk.egg-info/requires.txt +0 -0
  22. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_python_sdk.egg-info/top_level.txt +0 -0
  23. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/_mode.py +0 -0
  24. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/action_path_proof.py +0 -0
  25. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/agno.py +0 -0
  26. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/attestation.py +0 -0
  27. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/autogen.py +0 -0
  28. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/aws_bedrock.py +0 -0
  29. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/claude.py +0 -0
  30. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/connect.py +0 -0
  31. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/copilot_ir.py +0 -0
  32. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/copilot_ir_collector.cjs +0 -0
  33. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/crewai.py +0 -0
  34. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/demo.py +0 -0
  35. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/existing_integration.py +0 -0
  36. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/framework_edits.py +0 -0
  37. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/generic_python.py +0 -0
  38. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/google_adk.py +0 -0
  39. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/guided_integration.py +0 -0
  40. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/identity.py +0 -0
  41. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/langgraph.py +0 -0
  42. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/llamaindex.py +0 -0
  43. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/mcp_http_gateway.py +0 -0
  44. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/mcp_rehearsal_server.py +0 -0
  45. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/mcp_verification.py +0 -0
  46. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/microsoft_agent_framework.py +0 -0
  47. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/microsoft_ai_foundry.py +0 -0
  48. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/onboarding_cli.py +0 -0
  49. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/onboarding_observations.py +0 -0
  50. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/openai_agents.py +0 -0
  51. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/outcomes.py +0 -0
  52. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/proof.py +0 -0
  53. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/pydantic.py +0 -0
  54. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/report_scope.py +0 -0
  55. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/simulation.py +0 -0
  56. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/agenticdome_sdk/smolagents.py +0 -0
  57. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/README.md +0 -0
  58. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/agno.md +0 -0
  59. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/autogen.md +0 -0
  60. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/aws-bedrock.md +0 -0
  61. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/claude-agent-sdk.md +0 -0
  62. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/crewai.md +0 -0
  63. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/custom-python.md +0 -0
  64. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/google-adk.md +0 -0
  65. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/langgraph.md +0 -0
  66. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/llamaindex.md +0 -0
  67. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/microsoft-agent-framework.md +0 -0
  68. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/microsoft-ai-foundry.md +0 -0
  69. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/openai-agents.md +0 -0
  70. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/pydanticai.md +0 -0
  71. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/frameworks/smolagents.md +0 -0
  72. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/mcp-integration.md +0 -0
  73. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/performance-evidence.md +0 -0
  74. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/docs/runtime-deployment.md +0 -0
  75. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/PRODUCTION_INTEGRATION.md +0 -0
  76. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/README.md +0 -0
  77. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/attack_demo.py +0 -0
  78. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/crewai_agenticdome_firewall.py +0 -0
  79. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/README.md +0 -0
  80. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/_shared.py +0 -0
  81. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/agno.py +0 -0
  82. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/autogen.py +0 -0
  83. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/aws_bedrock.py +0 -0
  84. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/claude_agent_sdk.py +0 -0
  85. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/crewai.py +0 -0
  86. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/custom_python.py +0 -0
  87. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/google_adk.py +0 -0
  88. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/langgraph.py +0 -0
  89. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/llamaindex.py +0 -0
  90. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/mcp.py +0 -0
  91. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/microsoft_agent_framework.py +0 -0
  92. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/microsoft_ai_foundry.py +0 -0
  93. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/openai_agents.py +0 -0
  94. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/pydanticai.py +0 -0
  95. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/frameworks/smolagents.py +0 -0
  96. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/langgraph_agenticdome_firewall.py +0 -0
  97. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/local_simulation_gallery.py +0 -0
  98. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/examples/mcp_gateway_action_firewall.py +0 -0
  99. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/setup.cfg +0 -0
  100. {agenticdome_python_sdk-1.2.41 → agenticdome_python_sdk-1.2.45}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agenticdome-python-sdk
3
- Version: 1.2.41
3
+ Version: 1.2.45
4
4
  Summary: Official Python SDK for AgenticDome security guardrails, DLP, tool authorization, and multi-agent delegation enforcement.
5
5
  Author: AgenticDome
6
6
  License-Expression: Apache-2.0
@@ -21,11 +21,11 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
21
  Classifier: Topic :: Internet :: WWW/HTTP
22
22
  Classifier: Operating System :: OS Independent
23
23
  Classifier: Programming Language :: Python :: 3
24
- Classifier: Programming Language :: Python :: 3.9
25
- Classifier: Programming Language :: Python :: 3.10
26
24
  Classifier: Programming Language :: Python :: 3.11
27
25
  Classifier: Programming Language :: Python :: 3.12
28
- Requires-Python: >=3.9
26
+ Classifier: Programming Language :: Python :: 3.13
27
+ Classifier: Programming Language :: Python :: 3.14
28
+ Requires-Python: >=3.11
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
31
  License-File: NOTICE
@@ -113,6 +113,11 @@ Dynamic: license-file
113
113
 
114
114
  # AgenticDome Python SDK
115
115
 
116
+ Python 3.11+ is required from SDK 1.2.44. Release certification gates cover
117
+ CPython 3.11–3.14 using the same built wheel; newer interpreters require a
118
+ separate qualification run. Framework compatibility also depends on that
119
+ framework's supported versions and resolved dependencies.
120
+
116
121
  [![PyPI version](https://img.shields.io/pypi/v/agenticdome-python-sdk.svg)](https://pypi.org/project/agenticdome-python-sdk/)
117
122
  [![Python Versions](https://img.shields.io/pypi/pyversions/agenticdome-python-sdk.svg)](https://pypi.org/project/agenticdome-python-sdk/)
118
123
  [![CI](https://github.com/agenticdome/agenticdome-python-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/agenticdome/agenticdome-python-sdk/actions/workflows/ci.yml)
@@ -573,7 +578,7 @@ pip install agenticdome-python-sdk
573
578
  | PydanticAI | `pip install "agenticdome-python-sdk[pydanticai]"` |
574
579
  | LangGraph / LangChain | `pip install "agenticdome-python-sdk[langgraph]"` |
575
580
  | Microsoft Agent Framework | `pip install "agenticdome-python-sdk[microsoft]"` |
576
- | Microsoft AutoGen AgentChat / Core (Python 3.10+) | `pip install "agenticdome-python-sdk[autogen]"` |
581
+ | Microsoft AutoGen AgentChat / Core (Python 3.11+) | `pip install "agenticdome-python-sdk[autogen]"` |
577
582
  | Microsoft AI Foundry | `pip install "agenticdome-python-sdk[foundry]"` |
578
583
  | OpenAI Agents SDK | `pip install "agenticdome-python-sdk[openai-agents]"` |
579
584
  | Anthropic Claude Agent SDK | `pip install "agenticdome-python-sdk[claude]"` |
@@ -1323,7 +1328,7 @@ from agenticdome_sdk.microsoft_agent_framework import (
1323
1328
  AutoGen is Microsoft's open-source conversational multi-agent framework and is now community-managed in maintenance mode; Microsoft Agent Framework is the recommended successor for new systems. AgenticDome supports current AutoGen AgentChat/Core applications and existing legacy `ConversableAgent` deployments so teams can migrate without losing runtime enforcement.
1324
1329
 
1325
1330
  ```bash
1326
- # AutoGen AgentChat requires Python 3.10+.
1331
+ # This SDK requires Python 3.11+.
1327
1332
  pip install "agenticdome-python-sdk[autogen]"
1328
1333
  ```
1329
1334
 
@@ -2845,10 +2850,7 @@ Plus, for every production deployment:
2845
2850
 
2846
2851
  Use `VerifiedActionReporter` for executable tool calls, function calls, MCP operations and external side effects that should appear in **Verified Actions**. It is not intended for health checks or every policy lookup.
2847
2852
 
2848
- ```bash
2849
- export AGENTICDOME_EVIDENCE_API_BASE="https://your-control-plane.example"
2850
- export AGENTICDOME_EVIDENCE_TOKEN="<portal-token-scoped-to-evidence:write>"
2851
- ```
2853
+ Framework tool wrappers and the MCP host report lifecycle evidence automatically using the existing `AGENTICDOME_API_BASE`, `AGENTICDOME_API_KEY` and `AGENTICDOME_TENANT_ID`. No separate portal credential is required. Upgrade both the SDK and assigned runtime to use automatic reporting. Set `AGENTICDOME_EVIDENCE_ENABLED=false` to opt out.
2852
2854
 
2853
2855
  ```python
2854
2856
  from agenticdome_sdk import VerifiedActionReporter, verified_action
@@ -2865,7 +2867,9 @@ def refresh_customer_record(customer_id: str):
2865
2867
  ...
2866
2868
  ```
2867
2869
 
2868
- The reporter uses a bounded, non-blocking queue and retains hashes plus normalized lifecycle metadata only. It never sends raw arguments, results, prompts, source, or credentials. The dedicated evidence token is separate from the Runtime / SDK key: authorization stays on the assigned sidecar and evidence-delivery failure never changes its allow/deny decision. Integrations built on the shared framework `secure_tool` / `wrap_tool_handler` boundary and the MCP host/gateway wrapper report the same lifecycle automatically when these optional evidence variables are present; other custom boundaries can use the decorator above.
2870
+ The reporter uses a bounded, non-blocking queue and retains hashes plus normalized lifecycle metadata only. It never sends raw arguments, results, prompts, source, or credentials. The runtime queues accepted records in persistent Redis; its delivery worker relays them to the owning control plane using the existing per-sidecar service token. Network and storage failures remain pending for retry. Inspect `evidence.stats()` and call `evidence.flush()` before a short-lived process exits. SDK delivery failures and dropped records are counted and never change the protected operation's allow/deny decision.
2871
+
2872
+ Retained outcomes are labeled `sdk_reported`. Policy lookups alone do not prove execution, and streaming acceptance does not prove final completion. Existing `AGENTICDOME_EVIDENCE_API_BASE` plus `AGENTICDOME_EVIDENCE_TOKEN` remains an explicit legacy portal override. Automatic reporting starts with new wrapped calls; it does not backfill historical telemetry.
2869
2873
 
2870
2874
  ---
2871
2875
 
@@ -1,5 +1,10 @@
1
1
  # AgenticDome Python SDK
2
2
 
3
+ Python 3.11+ is required from SDK 1.2.44. Release certification gates cover
4
+ CPython 3.11–3.14 using the same built wheel; newer interpreters require a
5
+ separate qualification run. Framework compatibility also depends on that
6
+ framework's supported versions and resolved dependencies.
7
+
3
8
  [![PyPI version](https://img.shields.io/pypi/v/agenticdome-python-sdk.svg)](https://pypi.org/project/agenticdome-python-sdk/)
4
9
  [![Python Versions](https://img.shields.io/pypi/pyversions/agenticdome-python-sdk.svg)](https://pypi.org/project/agenticdome-python-sdk/)
5
10
  [![CI](https://github.com/agenticdome/agenticdome-python-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/agenticdome/agenticdome-python-sdk/actions/workflows/ci.yml)
@@ -460,7 +465,7 @@ pip install agenticdome-python-sdk
460
465
  | PydanticAI | `pip install "agenticdome-python-sdk[pydanticai]"` |
461
466
  | LangGraph / LangChain | `pip install "agenticdome-python-sdk[langgraph]"` |
462
467
  | Microsoft Agent Framework | `pip install "agenticdome-python-sdk[microsoft]"` |
463
- | Microsoft AutoGen AgentChat / Core (Python 3.10+) | `pip install "agenticdome-python-sdk[autogen]"` |
468
+ | Microsoft AutoGen AgentChat / Core (Python 3.11+) | `pip install "agenticdome-python-sdk[autogen]"` |
464
469
  | Microsoft AI Foundry | `pip install "agenticdome-python-sdk[foundry]"` |
465
470
  | OpenAI Agents SDK | `pip install "agenticdome-python-sdk[openai-agents]"` |
466
471
  | Anthropic Claude Agent SDK | `pip install "agenticdome-python-sdk[claude]"` |
@@ -1210,7 +1215,7 @@ from agenticdome_sdk.microsoft_agent_framework import (
1210
1215
  AutoGen is Microsoft's open-source conversational multi-agent framework and is now community-managed in maintenance mode; Microsoft Agent Framework is the recommended successor for new systems. AgenticDome supports current AutoGen AgentChat/Core applications and existing legacy `ConversableAgent` deployments so teams can migrate without losing runtime enforcement.
1211
1216
 
1212
1217
  ```bash
1213
- # AutoGen AgentChat requires Python 3.10+.
1218
+ # This SDK requires Python 3.11+.
1214
1219
  pip install "agenticdome-python-sdk[autogen]"
1215
1220
  ```
1216
1221
 
@@ -2732,10 +2737,7 @@ Plus, for every production deployment:
2732
2737
 
2733
2738
  Use `VerifiedActionReporter` for executable tool calls, function calls, MCP operations and external side effects that should appear in **Verified Actions**. It is not intended for health checks or every policy lookup.
2734
2739
 
2735
- ```bash
2736
- export AGENTICDOME_EVIDENCE_API_BASE="https://your-control-plane.example"
2737
- export AGENTICDOME_EVIDENCE_TOKEN="<portal-token-scoped-to-evidence:write>"
2738
- ```
2740
+ Framework tool wrappers and the MCP host report lifecycle evidence automatically using the existing `AGENTICDOME_API_BASE`, `AGENTICDOME_API_KEY` and `AGENTICDOME_TENANT_ID`. No separate portal credential is required. Upgrade both the SDK and assigned runtime to use automatic reporting. Set `AGENTICDOME_EVIDENCE_ENABLED=false` to opt out.
2739
2741
 
2740
2742
  ```python
2741
2743
  from agenticdome_sdk import VerifiedActionReporter, verified_action
@@ -2752,7 +2754,9 @@ def refresh_customer_record(customer_id: str):
2752
2754
  ...
2753
2755
  ```
2754
2756
 
2755
- The reporter uses a bounded, non-blocking queue and retains hashes plus normalized lifecycle metadata only. It never sends raw arguments, results, prompts, source, or credentials. The dedicated evidence token is separate from the Runtime / SDK key: authorization stays on the assigned sidecar and evidence-delivery failure never changes its allow/deny decision. Integrations built on the shared framework `secure_tool` / `wrap_tool_handler` boundary and the MCP host/gateway wrapper report the same lifecycle automatically when these optional evidence variables are present; other custom boundaries can use the decorator above.
2757
+ The reporter uses a bounded, non-blocking queue and retains hashes plus normalized lifecycle metadata only. It never sends raw arguments, results, prompts, source, or credentials. The runtime queues accepted records in persistent Redis; its delivery worker relays them to the owning control plane using the existing per-sidecar service token. Network and storage failures remain pending for retry. Inspect `evidence.stats()` and call `evidence.flush()` before a short-lived process exits. SDK delivery failures and dropped records are counted and never change the protected operation's allow/deny decision.
2758
+
2759
+ Retained outcomes are labeled `sdk_reported`. Policy lookups alone do not prove execution, and streaming acceptance does not prove final completion. Existing `AGENTICDOME_EVIDENCE_API_BASE` plus `AGENTICDOME_EVIDENCE_TOKEN` remains an explicit legacy portal override. Automatic reporting starts with new wrapped calls; it does not backfill historical telemetry.
2756
2760
 
2757
2761
  ---
2758
2762
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agenticdome-python-sdk
3
- Version: 1.2.41
3
+ Version: 1.2.45
4
4
  Summary: Official Python SDK for AgenticDome security guardrails, DLP, tool authorization, and multi-agent delegation enforcement.
5
5
  Author: AgenticDome
6
6
  License-Expression: Apache-2.0
@@ -21,11 +21,11 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
21
  Classifier: Topic :: Internet :: WWW/HTTP
22
22
  Classifier: Operating System :: OS Independent
23
23
  Classifier: Programming Language :: Python :: 3
24
- Classifier: Programming Language :: Python :: 3.9
25
- Classifier: Programming Language :: Python :: 3.10
26
24
  Classifier: Programming Language :: Python :: 3.11
27
25
  Classifier: Programming Language :: Python :: 3.12
28
- Requires-Python: >=3.9
26
+ Classifier: Programming Language :: Python :: 3.13
27
+ Classifier: Programming Language :: Python :: 3.14
28
+ Requires-Python: >=3.11
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
31
  License-File: NOTICE
@@ -113,6 +113,11 @@ Dynamic: license-file
113
113
 
114
114
  # AgenticDome Python SDK
115
115
 
116
+ Python 3.11+ is required from SDK 1.2.44. Release certification gates cover
117
+ CPython 3.11–3.14 using the same built wheel; newer interpreters require a
118
+ separate qualification run. Framework compatibility also depends on that
119
+ framework's supported versions and resolved dependencies.
120
+
116
121
  [![PyPI version](https://img.shields.io/pypi/v/agenticdome-python-sdk.svg)](https://pypi.org/project/agenticdome-python-sdk/)
117
122
  [![Python Versions](https://img.shields.io/pypi/pyversions/agenticdome-python-sdk.svg)](https://pypi.org/project/agenticdome-python-sdk/)
118
123
  [![CI](https://github.com/agenticdome/agenticdome-python-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/agenticdome/agenticdome-python-sdk/actions/workflows/ci.yml)
@@ -573,7 +578,7 @@ pip install agenticdome-python-sdk
573
578
  | PydanticAI | `pip install "agenticdome-python-sdk[pydanticai]"` |
574
579
  | LangGraph / LangChain | `pip install "agenticdome-python-sdk[langgraph]"` |
575
580
  | Microsoft Agent Framework | `pip install "agenticdome-python-sdk[microsoft]"` |
576
- | Microsoft AutoGen AgentChat / Core (Python 3.10+) | `pip install "agenticdome-python-sdk[autogen]"` |
581
+ | Microsoft AutoGen AgentChat / Core (Python 3.11+) | `pip install "agenticdome-python-sdk[autogen]"` |
577
582
  | Microsoft AI Foundry | `pip install "agenticdome-python-sdk[foundry]"` |
578
583
  | OpenAI Agents SDK | `pip install "agenticdome-python-sdk[openai-agents]"` |
579
584
  | Anthropic Claude Agent SDK | `pip install "agenticdome-python-sdk[claude]"` |
@@ -1323,7 +1328,7 @@ from agenticdome_sdk.microsoft_agent_framework import (
1323
1328
  AutoGen is Microsoft's open-source conversational multi-agent framework and is now community-managed in maintenance mode; Microsoft Agent Framework is the recommended successor for new systems. AgenticDome supports current AutoGen AgentChat/Core applications and existing legacy `ConversableAgent` deployments so teams can migrate without losing runtime enforcement.
1324
1329
 
1325
1330
  ```bash
1326
- # AutoGen AgentChat requires Python 3.10+.
1331
+ # This SDK requires Python 3.11+.
1327
1332
  pip install "agenticdome-python-sdk[autogen]"
1328
1333
  ```
1329
1334
 
@@ -2845,10 +2850,7 @@ Plus, for every production deployment:
2845
2850
 
2846
2851
  Use `VerifiedActionReporter` for executable tool calls, function calls, MCP operations and external side effects that should appear in **Verified Actions**. It is not intended for health checks or every policy lookup.
2847
2852
 
2848
- ```bash
2849
- export AGENTICDOME_EVIDENCE_API_BASE="https://your-control-plane.example"
2850
- export AGENTICDOME_EVIDENCE_TOKEN="<portal-token-scoped-to-evidence:write>"
2851
- ```
2853
+ Framework tool wrappers and the MCP host report lifecycle evidence automatically using the existing `AGENTICDOME_API_BASE`, `AGENTICDOME_API_KEY` and `AGENTICDOME_TENANT_ID`. No separate portal credential is required. Upgrade both the SDK and assigned runtime to use automatic reporting. Set `AGENTICDOME_EVIDENCE_ENABLED=false` to opt out.
2852
2854
 
2853
2855
  ```python
2854
2856
  from agenticdome_sdk import VerifiedActionReporter, verified_action
@@ -2865,7 +2867,9 @@ def refresh_customer_record(customer_id: str):
2865
2867
  ...
2866
2868
  ```
2867
2869
 
2868
- The reporter uses a bounded, non-blocking queue and retains hashes plus normalized lifecycle metadata only. It never sends raw arguments, results, prompts, source, or credentials. The dedicated evidence token is separate from the Runtime / SDK key: authorization stays on the assigned sidecar and evidence-delivery failure never changes its allow/deny decision. Integrations built on the shared framework `secure_tool` / `wrap_tool_handler` boundary and the MCP host/gateway wrapper report the same lifecycle automatically when these optional evidence variables are present; other custom boundaries can use the decorator above.
2870
+ The reporter uses a bounded, non-blocking queue and retains hashes plus normalized lifecycle metadata only. It never sends raw arguments, results, prompts, source, or credentials. The runtime queues accepted records in persistent Redis; its delivery worker relays them to the owning control plane using the existing per-sidecar service token. Network and storage failures remain pending for retry. Inspect `evidence.stats()` and call `evidence.flush()` before a short-lived process exits. SDK delivery failures and dropped records are counted and never change the protected operation's allow/deny decision.
2871
+
2872
+ Retained outcomes are labeled `sdk_reported`. Policy lookups alone do not prove execution, and streaming acceptance does not prove final completion. Existing `AGENTICDOME_EVIDENCE_API_BASE` plus `AGENTICDOME_EVIDENCE_TOKEN` remains an explicit legacy portal override. Automatic reporting starts with new wrapped calls; it does not backfill historical telemetry.
2869
2873
 
2870
2874
  ---
2871
2875
 
@@ -1,4 +1,4 @@
1
- from .client import AgenticDomeClient, AgenticDomeError, AgenticDomeHTTPError
1
+ from .client import AgenticDomeClient, AgenticDomeError, AgenticDomeHTTPError, ApprovalRequired
2
2
  from .identity import IDENTITY_CONTEXT_VERSION, canonicalize_identity_context, enrich_policy_context
3
3
  from .proof import create_dpop_proof, generate_rsa_proof_key, jwk_thumbprint
4
4
  from ._mode import LIVE_MODE, LOCAL_SIM_MODE, is_local_sim_mode, resolve_mode
@@ -10,6 +10,7 @@ __all__ = [
10
10
  "AgenticDomeClient",
11
11
  "AgenticDomeError",
12
12
  "AgenticDomeHTTPError",
13
+ "ApprovalRequired",
13
14
  "IDENTITY_CONTEXT_VERSION",
14
15
  "canonicalize_identity_context",
15
16
  "enrich_policy_context",
@@ -15,7 +15,7 @@ from dataclasses import dataclass
15
15
  from threading import Lock, Thread
16
16
  from typing import Any, Awaitable, Callable, Deque, Dict, Optional, Tuple, Type
17
17
 
18
- from .client import AgenticDomeClient
18
+ from .client import AgenticDomeClient, ApprovalRequired
19
19
  from .lifecycle import VerifiedActionReporter
20
20
  from ._mode import credentials_or_local_sim
21
21
 
@@ -202,7 +202,8 @@ class FrameworkFirewallBase:
202
202
  timeout=config.timeout_s,
203
203
  )
204
204
  self.token_store = token_store or build_token_store(config, logger)
205
- self.lifecycle_reporter = VerifiedActionReporter.from_env(tenant_id=config.tenant_id)
205
+ self.lifecycle_reporter = VerifiedActionReporter.from_env(tenant_id=config.tenant_id, api_base=config.api_base, api_key=config.api_key,
206
+ sender=getattr(self.client, "report_action_evidence", None))
206
207
  self.denied_error = denied_error
207
208
  self.label = label
208
209
  self.logger = logger
@@ -334,6 +335,8 @@ class FrameworkFirewallBase:
334
335
  self._circuit_open_until = 0.0
335
336
  return result
336
337
  except Exception as exc:
338
+ if isinstance(exc, ApprovalRequired):
339
+ raise
337
340
  last_error = exc
338
341
  with self._circuit_lock:
339
342
  self._circuit_failures += 1
@@ -345,6 +348,8 @@ class FrameworkFirewallBase:
345
348
  raise last_error
346
349
 
347
350
  def _failure(self, context: str, exc: Exception, fallback: Any) -> Any:
351
+ if isinstance(exc, ApprovalRequired):
352
+ raise exc
348
353
  if isinstance(exc, self.denied_error):
349
354
  raise exc
350
355
  if self.config.fail_closed:
@@ -623,14 +628,17 @@ class FrameworkFirewallBase:
623
628
  decision = await self.aauthorize_tool_call(
624
629
  session_id=sid, agent_id=aid, tool_name=tool_name, tool_args=call_args,
625
630
  tool_platform=tool_platform, text=f"[{self.label}] {aid} calls {tool_name}",
631
+ policy_context={"chain_id": lifecycle.chain_id, "action_id": lifecycle.action_id, "decision_id": lifecycle.action_id},
626
632
  )
627
633
  except Exception:
628
634
  self.lifecycle_reporter.phase(lifecycle, "authorised", "blocked")
629
635
  self.lifecycle_reporter.outcome(lifecycle, "not_attempted")
630
636
  raise
631
- self.lifecycle_reporter.phase(lifecycle, "authorised", "allowed")
632
- self.lifecycle_reporter.phase(lifecycle, "admitted", "admitted")
633
637
  clean = self.sanitized_args(decision, call_args)
638
+ lifecycle = self.lifecycle_reporter.with_arguments(lifecycle, clean)
639
+ if self.verdict(decision) in {"ALLOWED", "REDACTED"} and self.reason(decision) != "fail-open":
640
+ self.lifecycle_reporter.phase(lifecycle, "authorised", "allowed")
641
+ self.lifecycle_reporter.phase(lifecycle, "admitted", "admitted")
634
642
  self.lifecycle_reporter.phase(lifecycle, "attempted", "attempted")
635
643
  try:
636
644
  if kwargs:
@@ -659,14 +667,17 @@ class FrameworkFirewallBase:
659
667
  decision = self.authorize_tool_call(
660
668
  session_id=sid, agent_id=aid, tool_name=tool_name, tool_args=call_args,
661
669
  tool_platform=tool_platform, text=f"[{self.label}] {aid} calls {tool_name}",
670
+ policy_context={"chain_id": lifecycle.chain_id, "action_id": lifecycle.action_id, "decision_id": lifecycle.action_id},
662
671
  )
663
672
  except Exception:
664
673
  self.lifecycle_reporter.phase(lifecycle, "authorised", "blocked")
665
674
  self.lifecycle_reporter.outcome(lifecycle, "not_attempted")
666
675
  raise
667
- self.lifecycle_reporter.phase(lifecycle, "authorised", "allowed")
668
- self.lifecycle_reporter.phase(lifecycle, "admitted", "admitted")
669
676
  clean = self.sanitized_args(decision, call_args)
677
+ lifecycle = self.lifecycle_reporter.with_arguments(lifecycle, clean)
678
+ if self.verdict(decision) in {"ALLOWED", "REDACTED"} and self.reason(decision) != "fail-open":
679
+ self.lifecycle_reporter.phase(lifecycle, "authorised", "allowed")
680
+ self.lifecycle_reporter.phase(lifecycle, "admitted", "admitted")
670
681
  self.lifecycle_reporter.phase(lifecycle, "attempted", "attempted")
671
682
  try:
672
683
  if kwargs:
@@ -2,22 +2,22 @@
2
2
  "schema": "agenticdome.catalog-snapshot.v1",
3
3
  "catalog_schema": "agenticdome.hook-catalog.v1",
4
4
  "verified_at": "2026-10-02",
5
- "python_sdk_version": "1.2.41",
5
+ "python_sdk_version": "1.2.45",
6
6
  "published_packages": {
7
7
  "agenticdome-python-sdk": {
8
8
  "registry": "pypi",
9
- "version": "1.2.41",
10
- "url": "https://pypi.org/project/agenticdome-python-sdk/1.2.41/"
9
+ "version": "1.2.45",
10
+ "url": "https://pypi.org/project/agenticdome-python-sdk/1.2.45/"
11
11
  },
12
12
  "agenticdome-sdk": {
13
13
  "registry": "npm",
14
- "version": "0.6.3",
15
- "url": "https://www.npmjs.com/package/agenticdome-sdk/v/0.6.3"
14
+ "version": "0.6.14",
15
+ "url": "https://www.npmjs.com/package/agenticdome-sdk/v/0.6.14"
16
16
  },
17
17
  "agenticdome-openclaw-security": {
18
18
  "registry": "npm",
19
- "version": "1.0.2",
20
- "url": "https://www.npmjs.com/package/agenticdome-openclaw-security/v/1.0.2"
19
+ "version": "1.0.13",
20
+ "url": "https://www.npmjs.com/package/agenticdome-openclaw-security/v/1.0.13"
21
21
  },
22
22
  "openclaw": {
23
23
  "registry": "npm",
@@ -1,5 +1,7 @@
1
1
  import base64
2
2
  import hashlib
3
+ import json
4
+ import uuid
3
5
  import logging
4
6
  import os
5
7
  import re
@@ -41,6 +43,36 @@ class AgenticDomeHTTPError(AgenticDomeError):
41
43
  super().__init__(f"[{status_code}] {message}")
42
44
 
43
45
 
46
+ class ApprovalRequired(AgenticDomeError):
47
+ """A prepared action is paused. Preserve it and resume only after review."""
48
+
49
+ def __init__(self, response: Dict[str, Any], prepared_request: Dict[str, Any]):
50
+ context = response.get("context") or {}
51
+ self.request_id = str((context.get("approval") or {}).get("id") or "")
52
+ self.approval_url = str(context.get("approval_url") or "")
53
+ self.response = response
54
+ # Freeze the prepared body independently of mutable handler arguments.
55
+ self._prepared_json = json.dumps(prepared_request, sort_keys=True, allow_nan=False)
56
+ super().__init__("Approval required" + (": " + self.request_id if self.request_id else ""))
57
+
58
+ def resume(self, client: "AgenticDomeClient", executor):
59
+ return client.resume_approved_action(self, executor)
60
+
61
+ def export_prepared_action(self):
62
+ """Contains original arguments: encrypt in host storage and never log it."""
63
+ prepared = json.loads(self._prepared_json)
64
+ return {"schema": "agenticdome.sdk-prepared-approval.v1", "approval": {"id": self.request_id, "url": self.approval_url},
65
+ "prepared_request": prepared, "tenant_id": prepared.get("tenant_id")}
66
+
67
+ @classmethod
68
+ def restore_prepared_action(cls, record):
69
+ prepared = record.get("prepared_request") if isinstance(record, dict) else None
70
+ approval = record.get("approval") if isinstance(record, dict) else None
71
+ if not isinstance(record, dict) or record.get("schema") != "agenticdome.sdk-prepared-approval.v1" or not isinstance(prepared, dict) or not prepared.get("tool_name") or not isinstance(approval, dict) or not approval.get("id"):
72
+ raise ValueError("Invalid prepared approval record")
73
+ return cls({"context": {"approval": {"id": approval["id"]}, "approval_url": approval.get("url", "")}}, record["prepared_request"])
74
+
75
+
44
76
  class AgenticDomeClient:
45
77
  """
46
78
  Official Python SDK for the AgenticDome Action Firewall.
@@ -375,6 +407,12 @@ class AgenticDomeClient:
375
407
  if tool_digest is not None and not re.fullmatch(r"sha256:[0-9a-f]{64}", str(tool_digest)):
376
408
  raise ValueError("'tool_digest' must be sha256 followed by 64 lowercase hexadecimal characters")
377
409
 
410
+ def report_action_evidence(self, kind: str, payload: Dict[str, Any]) -> Dict[str, Any]:
411
+ """Use the existing tenant SDK transport; runtime durably relays evidence."""
412
+ if kind not in {"events", "outcomes"}:
413
+ raise ValueError("Evidence kind must be events or outcomes")
414
+ return self._request("POST", "/mesh/evidence/" + kind, json_body=payload, timeout=5)
415
+
378
416
  def _request(
379
417
  self,
380
418
  method: str,
@@ -788,10 +826,14 @@ class AgenticDomeClient:
788
826
  raise ValueError("'workload_id' must be a non-empty SPIFFE ID up to 512 characters")
789
827
  payload["workload_id"] = normalized_workload
790
828
 
829
+ if tool_name:
830
+ payload["policy_context"].setdefault("approval_action_id", str(uuid.uuid4()))
791
831
  path = "/tools/execution/authorize" if broker_enabled else "/tools/guardrail/validate"
792
832
  if broker_enabled and resolved_broker_mode == "policy" and execution_broker is not True:
793
833
  path = "/tools/execution/resolve"
794
834
  response = self._request("POST", path, json_body=payload, tenant_id=tenant_id)
835
+ if str(response.get("verdict") or "").upper() == "APPROVAL_REQUIRED":
836
+ raise ApprovalRequired(response, payload)
795
837
  if broker_enabled:
796
838
  if path == "/tools/execution/resolve":
797
839
  contract = response.get("execution_broker_policy") or {}
@@ -813,6 +855,28 @@ class AgenticDomeClient:
813
855
  )
814
856
  return response
815
857
 
858
+ def resume_approved_action(self, pending: ApprovalRequired, executor):
859
+ """Revalidate and consume one exact-action grant before invoking executor(args).
860
+
861
+ The caller retains the prepared request; the portal never holds payloads.
862
+ Approval does not execute anything until this explicit continuation runs.
863
+ """
864
+ if not isinstance(pending, ApprovalRequired) or not callable(executor):
865
+ raise ValueError("A prepared approval request and executor are required")
866
+ request_id = str(uuid.UUID(pending.request_id))
867
+ payload = json.loads(pending._prepared_json)
868
+ tenant = payload.get("tenant_id")
869
+ grant = self._request("GET", f"/tools/approvals/{request_id}/grant", tenant_id=tenant).get("approval_grant")
870
+ if not isinstance(grant, str) or not grant:
871
+ raise AgenticDomeError("No current approval grant was returned")
872
+ payload["policy_context"] = {**(payload.get("policy_context") or {}), "approval_grant": grant}
873
+ payload.setdefault("boundary_id", "sdk:approval:" + request_id)
874
+ response = self._request("POST", "/tools/execution/authorize", json_body=payload, tenant_id=tenant)
875
+ broker = response.get("broker") or {}
876
+ if response.get("verdict") != "ALLOWED" or not broker.get("verified") or not broker.get("token_consumed") or broker.get("approval_request_id") != request_id or not response.get("execution_receipt"):
877
+ raise AgenticDomeError("The approved action could not be revalidated and consumed")
878
+ return executor(json.loads(pending._prepared_json)["tool_args"])
879
+
816
880
  @staticmethod
817
881
  def enforcement_headers(result: Dict[str, Any], *, workload_id: Optional[str] = None) -> Dict[str, str]:
818
882
  """Build headers for an AgenticDome egress gateway from a broker result."""
@@ -49,6 +49,12 @@ PUBLISHED_AGENTICDOME_PACKAGES: Dict[str, Dict[str, str]] = {
49
49
  if isinstance(row, dict)
50
50
  }
51
51
 
52
+ # Prepared artifacts are kept separate from registry-verified published releases.
53
+ RELEASE_CANDIDATE_PACKAGES = {
54
+ 'agenticdome-python-sdk': {'version': '1.2.44', 'status': 'unpublished'},
55
+ 'agenticdome-sdk': {'version': '0.6.15', 'status': 'unpublished'},
56
+ }
57
+
52
58
 
53
59
  def _python_contract(
54
60
  label: str,
@@ -325,7 +331,7 @@ FRAMEWORK_HOOK_CATALOG: Dict[str, Dict[str, Any]] = {
325
331
  "adapter_module": "agenticdome-openclaw-security",
326
332
  "adapter_class": "OpenClawFirewall",
327
333
  "attachment_methods": ["protectedExecute", "sanitizeOutput"],
328
- "native_hooks": ["before_agent_run", "before_tool_call", "tool_result_persist"],
334
+ "native_hooks": ["before_agent_run", "before_tool_call", "after_tool_call", "tool_result_persist"],
329
335
  "docs": "agenticdome-openclaw-security package README",
330
336
  },
331
337
  "mcp-ts": {
@@ -1,7 +1,7 @@
1
1
  """Privacy-bounded Verified Action lifecycle reporting.
2
2
 
3
3
  The runtime authorization path remains authoritative. This module reports only
4
- minimized lifecycle evidence through a separately scoped portal credential and
4
+ minimized lifecycle evidence through the existing tenant runtime credential and
5
5
  never sends tool arguments, prompts, responses, credentials, or source code.
6
6
  """
7
7
 
@@ -15,9 +15,11 @@ import logging
15
15
  import os
16
16
  import queue
17
17
  import threading
18
+ import time
19
+ import urllib.error
18
20
  import urllib.request
19
21
  import uuid
20
- from dataclasses import dataclass
22
+ from dataclasses import dataclass, replace
21
23
  from datetime import datetime, timezone
22
24
  from typing import Any, Callable, Dict, Optional, TypeVar
23
25
 
@@ -72,33 +74,46 @@ class VerifiedActionDenied(RuntimeError):
72
74
  class VerifiedActionReporter:
73
75
  """Non-blocking, bounded reporter for one tenant's Verified Actions.
74
76
 
75
- Configure ``AGENTICDOME_EVIDENCE_API_BASE`` and a portal token containing
76
- only ``evidence:write``. Missing evidence credentials disable reporting;
77
- they never weaken or bypass the primary AgenticDome authorization call.
77
+ SDK credentials enable reporting to the assigned runtime by default.
78
+ Explicit portal evidence credentials remain supported for existing callers.
78
79
  """
79
80
 
80
- def __init__(self, portal: str = "", access_token: str = "", *, tenant_id: str = "", timeout: float = 5.0, queue_size: int = 256) -> None:
81
+ def __init__(self, portal: str = "", access_token: str = "", *, tenant_id: str = "", timeout: float = 5.0, queue_size: int = 256,
82
+ runtime_base: str = "", api_key: str = "", sender: Optional[Callable[..., Any]] = None) -> None:
81
83
  self.portal = portal.rstrip("/")
82
84
  self.access_token = access_token.strip()
83
85
  self.tenant_id = tenant_id.strip()
86
+ self.runtime_base = runtime_base.rstrip("/")
87
+ self.api_key = api_key.strip()
88
+ self.sender = sender
89
+ self.runtime_mode = not (self.portal and self.access_token)
84
90
  self.timeout = max(1.0, min(float(timeout), 30.0))
85
91
  self._queue: "queue.Queue[tuple[str, Dict[str, Any]] | None]" = queue.Queue(maxsize=max(16, min(int(queue_size), 4096)))
86
92
  self._worker: Optional[threading.Thread] = None
87
93
  self._lock = threading.Lock()
94
+ self._stats = {"queued": 0, "delivered": 0, "failed": 0, "dropped": 0}
88
95
 
89
96
  @classmethod
90
- def from_env(cls, *, tenant_id: str = "") -> "VerifiedActionReporter":
97
+ def from_env(cls, *, tenant_id: str = "", api_base: str = "", api_key: str = "", sender: Optional[Callable[..., Any]] = None) -> "VerifiedActionReporter":
98
+ if os.getenv("AGENTICDOME_EVIDENCE_ENABLED", "true").lower() in {"0", "false", "off", "no"}:
99
+ return cls()
91
100
  return cls(
92
101
  os.getenv("AGENTICDOME_EVIDENCE_API_BASE", ""),
93
102
  os.getenv("AGENTICDOME_EVIDENCE_TOKEN", ""),
94
103
  tenant_id=tenant_id or os.getenv("AGENTICDOME_TENANT_ID", ""),
95
104
  timeout=float(os.getenv("AGENTICDOME_EVIDENCE_TIMEOUT_S", "5") or "5"),
96
105
  queue_size=int(os.getenv("AGENTICDOME_EVIDENCE_QUEUE_SIZE", "256") or "256"),
106
+ runtime_base=api_base or os.getenv("AGENTICDOME_API_BASE", ""),
107
+ api_key=api_key or os.getenv("AGENTICDOME_API_KEY", ""), sender=sender,
97
108
  )
98
109
 
99
110
  @property
100
111
  def enabled(self) -> bool:
101
- return bool(self.portal and self.access_token and self.tenant_id)
112
+ return bool(self.tenant_id and ((self.portal and self.access_token) or
113
+ (self.runtime_base.startswith(("https://", "http://")) and self.api_key)))
114
+
115
+ def stats(self) -> Dict[str, Any]:
116
+ return dict(self._stats, pending=self._queue.unfinished_tasks, enabled=self.enabled, transport="runtime" if self.runtime_mode else "portal")
102
117
 
103
118
  def new_context(self, *, operation_type: str, tool_name: Optional[str] = None, tool_version: Optional[str] = None,
104
119
  arguments: Any = None, destination: Optional[str] = None, chain_id: Optional[str] = None,
@@ -141,6 +156,10 @@ class VerifiedActionReporter:
141
156
  }
142
157
  return self._enqueue("/api/agentguard/verified-actions/events", payload)
143
158
 
159
+ def with_arguments(self, context: VerifiedActionContext, arguments: Any) -> VerifiedActionContext:
160
+ """Keep correlation IDs while hashing the arguments actually admitted for execution."""
161
+ return replace(context, arguments_sha256=_digest(arguments))
162
+
144
163
  def outcome(self, context: VerifiedActionContext, outcome_class: str, *, side_effect_reference: Optional[str] = None) -> bool:
145
164
  allowed = {"not_attempted", "rejected", "accepted", "succeeded", "partially_succeeded", "failed", "rolled_back", "unknown"}
146
165
  outcome = _token(outcome_class, allowed, "unknown")
@@ -150,8 +169,8 @@ class VerifiedActionReporter:
150
169
  "chain_id": context.chain_id, "action_id": context.action_id,
151
170
  "jti": "sdk_" + uuid.uuid4().hex, "outcome_class": outcome,
152
171
  "assurance_level": "sdk_reported", "authorised_action_sha256": context.arguments_sha256,
153
- "observed_action_sha256": context.arguments_sha256, "destination_sha256": context.destination_sha256,
154
- "side_effect_ref_sha256": _digest(side_effect_reference), "attempted_at": now, "completed_at": now,
172
+ "observed_action_sha256": None if outcome == "not_attempted" else context.arguments_sha256, "destination_sha256": context.destination_sha256,
173
+ "side_effect_ref_sha256": _digest(side_effect_reference), "completed_at": now,
155
174
  }
156
175
  return self._enqueue("/api/agentguard/verified-actions/outcomes", payload)
157
176
 
@@ -162,7 +181,7 @@ class VerifiedActionReporter:
162
181
  marker = {"_flush_event": completed}
163
182
  if not self._enqueue("", marker):
164
183
  return False
165
- return completed.wait(max(0.1, timeout))
184
+ return completed.wait(max(0.1, timeout)) and self._stats["failed"] == 0
166
185
 
167
186
  def _enqueue(self, path: str, payload: Dict[str, Any]) -> bool:
168
187
  if not self.enabled:
@@ -170,8 +189,10 @@ class VerifiedActionReporter:
170
189
  self._ensure_worker()
171
190
  try:
172
191
  self._queue.put_nowait((path, payload))
192
+ if path:self._stats["queued"] += 1
173
193
  return True
174
194
  except queue.Full:
195
+ self._stats["dropped"] += 1
175
196
  logger.warning("Verified Action evidence queue is full; authorization behavior is unchanged.")
176
197
  return False
177
198
 
@@ -193,14 +214,27 @@ class VerifiedActionReporter:
193
214
  if isinstance(marker, threading.Event):
194
215
  marker.set()
195
216
  continue
196
- request = urllib.request.Request(
197
- self.portal + path, data=json.dumps(payload, separators=(",", ":")).encode("utf-8"), method="POST",
198
- headers={"Authorization": "Bearer " + self.access_token, "Content-Type": "application/json", "Accept": "application/json"},
199
- )
200
- with urllib.request.urlopen(request, timeout=self.timeout) as response: # noqa: S310 - configured AgenticDome portal
201
- response.read(1)
217
+ for attempt in range(3):
218
+ try:
219
+ if self.runtime_mode and self.sender:
220
+ self.sender(path.rsplit("/", 1)[-1], payload)
221
+ else:
222
+ endpoint = (self.runtime_base + "/mesh/evidence/" + path.rsplit("/", 1)[-1]) if self.runtime_mode else self.portal + path
223
+ headers = {"Content-Type": "application/json", "Accept": "application/json", "X-Tenant-Id": self.tenant_id}
224
+ headers.update({"X-API-Key": self.api_key} if self.runtime_mode else {"Authorization": "Bearer " + self.access_token})
225
+ request = urllib.request.Request(endpoint, data=json.dumps(payload, separators=(",", ":")).encode("utf-8"), method="POST", headers=headers)
226
+ with urllib.request.urlopen(request, timeout=self.timeout) as response:
227
+ response.read(1)
228
+ self._stats["delivered"] += 1
229
+ break
230
+ except Exception as exc:
231
+ status = getattr(exc, "code", getattr(exc, "status_code", None))
232
+ if attempt == 2 or status is not None and status not in {429, 500, 502, 503, 504}:
233
+ raise
234
+ time.sleep(.1 * (2 ** attempt))
202
235
  except Exception as exc: # evidence must never alter the protected operation
203
- logger.warning("Verified Action evidence delivery failed; authorization behavior is unchanged: %s", exc)
236
+ self._stats["failed"] += 1
237
+ logger.warning("Verified Action evidence delivery failed; authorization behavior is unchanged (%s)", type(exc).__name__)
204
238
  finally:
205
239
  self._queue.task_done()
206
240
 
@@ -13,6 +13,7 @@ import time
13
13
  import uuid
14
14
  import re
15
15
  from collections import defaultdict, deque
16
+ from contextvars import ContextVar
16
17
  from dataclasses import dataclass
17
18
  from datetime import date
18
19
  from inspect import isawaitable
@@ -23,6 +24,8 @@ from .client import AgenticDomeClient
23
24
  from ._mode import credentials_or_local_sim
24
25
  from .lifecycle import VerifiedActionReporter
25
26
 
27
+ _lifecycle_decision: ContextVar[Optional[Dict[str, Any]]] = ContextVar("agenticdome_mcp_lifecycle_decision", default=None)
28
+
26
29
  try:
27
30
  from .exceptions import AgenticDomeHTTPError
28
31
  except Exception: # pragma: no cover - compatibility with older package layouts
@@ -375,7 +378,8 @@ class AgenticDomeMCPHostFirewall:
375
378
  timeout=self.config.timeout_s,
376
379
  )
377
380
  self.token_store = token_store or _build_token_store(self.config)
378
- self.lifecycle_reporter = lifecycle_reporter or VerifiedActionReporter.from_env(tenant_id=self.config.tenant_id)
381
+ self.lifecycle_reporter = lifecycle_reporter or VerifiedActionReporter.from_env(tenant_id=self.config.tenant_id,
382
+ api_base=self.config.api_base, api_key=self.config.api_key, sender=getattr(self.client, "report_action_evidence", None))
379
383
  self.provider_action_rules = dict(provider_action_rules or {})
380
384
  self.resolve_provider_facts = resolve_provider_facts
381
385
  self._rate_lock = Lock()
@@ -975,6 +979,9 @@ class AgenticDomeMCPHostFirewall:
975
979
  )
976
980
 
977
981
  async def _handle_blocking_decision(self, *, decision: Dict[str, Any], rid: Any, context: Dict[str, Any], method: str, operation: str) -> Optional[Dict[str, Any]]:
982
+ evidence = _lifecycle_decision.get()
983
+ if evidence is not None:
984
+ evidence.update(verdict=self._verdict(decision), reason=self._reason(decision))
978
985
  verdict = self._verdict(decision)
979
986
  if verdict == "ALLOWED" or (verdict == "REDACTED" and self._sanitized_args(decision, explicit_only=True) is not None):
980
987
  return None
@@ -1433,7 +1440,7 @@ class AgenticDomeMCPHostFirewall:
1433
1440
  lifecycle = self.lifecycle_reporter.new_context(
1434
1441
  operation_type="mcp_operation", tool_name=tool_name, arguments=arguments,
1435
1442
  destination=self.config.mcp_server_url or self.config.mcp_server_id,
1436
- chain_id=self._safe_str(local_context.get("verified_chain_id") or local_context.get("action_chain_id")) or None,
1443
+ chain_id=self._safe_str(local_context.get("verified_chain_id") or local_context.get("action_chain_id") or local_context.get("chain_id")) or None,
1437
1444
  action_id=self._safe_str(local_context.get("verified_action_id") or local_context.get("action_id")) or None,
1438
1445
  parent_action_id=self._safe_str(local_context.get("parent_action_id")) or None,
1439
1446
  initiator_type="human" if self._safe_str(local_context.get("user_id")) else "agent",
@@ -1441,13 +1448,25 @@ class AgenticDomeMCPHostFirewall:
1441
1448
  )
1442
1449
  self.lifecycle_reporter.phase(lifecycle, "requested", "requested")
1443
1450
 
1444
- gated = await self.preflight_request(mcp_request=mcp_request, context=context)
1451
+ local_context.update(chain_id=lifecycle.chain_id, action_id=lifecycle.action_id, decision_id=lifecycle.action_id)
1452
+ decision_evidence: Dict[str, Any] = {}
1453
+ decision_token = _lifecycle_decision.set(decision_evidence)
1454
+ try:
1455
+ gated = await self.preflight_request(mcp_request=mcp_request, context=local_context)
1456
+ except Exception:
1457
+ self.lifecycle_reporter.outcome(lifecycle, "not_attempted")
1458
+ raise
1459
+ finally:
1460
+ _lifecycle_decision.reset(decision_token)
1445
1461
  if isinstance(gated, dict) and "error" in gated:
1446
1462
  self.lifecycle_reporter.phase(lifecycle, "authorised", "blocked")
1447
1463
  self.lifecycle_reporter.outcome(lifecycle, "not_attempted")
1448
1464
  return gated
1449
1465
 
1450
- self.lifecycle_reporter.phase(lifecycle, "authorised", "allowed")
1466
+ admitted_arguments = self._extract_tool_call(gated)[1] if method == "tools/call" else self._params(gated)
1467
+ lifecycle = self.lifecycle_reporter.with_arguments(lifecycle, admitted_arguments)
1468
+ if decision_evidence.get("verdict") in {"ALLOWED", "REDACTED"} and decision_evidence.get("reason") != "fail-open":
1469
+ self.lifecycle_reporter.phase(lifecycle, "authorised", "allowed")
1451
1470
  self.lifecycle_reporter.phase(lifecycle, "admitted", "admitted")
1452
1471
  self.lifecycle_reporter.phase(lifecycle, "attempted", "attempted")
1453
1472
 
@@ -54,6 +54,23 @@ and final verification. It does not silently apply code changes.
54
54
 
55
55
  ### Already integrated with your coding assistant?
56
56
 
57
+ The AI instruction first identifies the workload's actual local interpreter,
58
+ package manager and dependency manifest, checks the latest stable compatible
59
+ published SDK, and reconciles an older installation or mismatched SDK pin.
60
+ It records the tested exact SDK version and preserves unrelated pins, existing
61
+ hooks and configuration. An already aligned SDK is not reinstalled. The shared
62
+ production interpreter/image remains a separate operator installation target.
63
+
64
+ On a rerun, show current local tests, fresh validation and production activation
65
+ separately. Missing planning configuration means **AWAITING CONFIGURATION**, with
66
+ fresh readiness **NOT RUN**; a saved `ready=false` is historical rather than a
67
+ new code rejection. Replace current report sections in place and keep one
68
+ historical evidence section. Use only fresh matching JSON for the current verdict.
69
+ If the workload provides a reviewed local verification runner, use it for these
70
+ steps. An explicit planning-file option may load only the planning fields without
71
+ printing them; the CLI itself does not automatically load the application `.env`.
72
+ The Runtime / SDK key cannot replace the separately scoped Copilot key.
73
+
57
74
  The alternative AI Instruction path does not require repeating `init`,
58
75
  `integrate preview` or guided apply. From the root of the one deployable
59
76
  application workload, create and review the canonical
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agenticdome-python-sdk"
7
- version = "1.2.41"
7
+ version = "1.2.45"
8
8
  description = "Official Python SDK for AgenticDome security guardrails, DLP, tool authorization, and multi-agent delegation enforcement."
9
9
  readme = "README.md"
10
- requires-python = ">=3.9"
10
+ requires-python = ">=3.11"
11
11
  license = "Apache-2.0"
12
12
  license-files = ["LICENSE", "NOTICE"]
13
13
 
@@ -36,10 +36,10 @@ classifiers = [
36
36
  "Topic :: Internet :: WWW/HTTP",
37
37
  "Operating System :: OS Independent",
38
38
  "Programming Language :: Python :: 3",
39
- "Programming Language :: Python :: 3.9",
40
- "Programming Language :: Python :: 3.10",
41
39
  "Programming Language :: Python :: 3.11",
42
- "Programming Language :: Python :: 3.12"
40
+ "Programming Language :: Python :: 3.12",
41
+ "Programming Language :: Python :: 3.13",
42
+ "Programming Language :: Python :: 3.14"
43
43
  ]
44
44
 
45
45
  dependencies = [
@@ -175,9 +175,9 @@ agenticdome_sdk = ["*.cjs", "catalog_snapshot.json"]
175
175
 
176
176
  [tool.ruff]
177
177
  line-length = 120
178
- target-version = "py39"
178
+ target-version = "py311"
179
179
 
180
180
  [tool.mypy]
181
- python_version = "3.9"
181
+ python_version = "3.11"
182
182
  ignore_missing_imports = true
183
183
  strict = false