agenticdome-python-sdk 1.2.28__py3-none-any.whl → 1.2.30__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.
- {agenticdome_python_sdk-1.2.28.dist-info → agenticdome_python_sdk-1.2.30.dist-info}/METADATA +76 -23
- {agenticdome_python_sdk-1.2.28.dist-info → agenticdome_python_sdk-1.2.30.dist-info}/RECORD +18 -14
- agenticdome_sdk/__init__.py +5 -0
- agenticdome_sdk/_framework_firewall.py +61 -20
- agenticdome_sdk/attestation.py +205 -0
- agenticdome_sdk/catalog_snapshot.json +6 -6
- agenticdome_sdk/client.py +77 -7
- agenticdome_sdk/connect.py +273 -0
- agenticdome_sdk/hook_catalog.py +20 -20
- agenticdome_sdk/lifecycle.py +256 -0
- agenticdome_sdk/mcp_host.py +36 -2
- agenticdome_sdk/onboarding_cli.py +139 -4
- agenticdome_sdk/outcomes.py +55 -0
- {agenticdome_python_sdk-1.2.28.dist-info → agenticdome_python_sdk-1.2.30.dist-info}/WHEEL +0 -0
- {agenticdome_python_sdk-1.2.28.dist-info → agenticdome_python_sdk-1.2.30.dist-info}/entry_points.txt +0 -0
- {agenticdome_python_sdk-1.2.28.dist-info → agenticdome_python_sdk-1.2.30.dist-info}/licenses/LICENSE +0 -0
- {agenticdome_python_sdk-1.2.28.dist-info → agenticdome_python_sdk-1.2.30.dist-info}/licenses/NOTICE +0 -0
- {agenticdome_python_sdk-1.2.28.dist-info → agenticdome_python_sdk-1.2.30.dist-info}/top_level.txt +0 -0
{agenticdome_python_sdk-1.2.28.dist-info → agenticdome_python_sdk-1.2.30.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agenticdome-python-sdk
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.30
|
|
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
|
|
@@ -31,21 +31,22 @@ License-File: LICENSE
|
|
|
31
31
|
License-File: NOTICE
|
|
32
32
|
Requires-Dist: requests>=2.31.0
|
|
33
33
|
Requires-Dist: urllib3>=2.0.0
|
|
34
|
+
Requires-Dist: cryptography>=42.0.0
|
|
34
35
|
Provides-Extra: crewai
|
|
35
|
-
Requires-Dist: crewai<=1.15.
|
|
36
|
+
Requires-Dist: crewai<=1.15.21,>=1.15.5; extra == "crewai"
|
|
36
37
|
Provides-Extra: redis
|
|
37
38
|
Requires-Dist: redis>=4.5.0; extra == "redis"
|
|
38
39
|
Provides-Extra: pop
|
|
39
40
|
Requires-Dist: cryptography>=42.0.0; extra == "pop"
|
|
40
41
|
Provides-Extra: pydanticai
|
|
41
|
-
Requires-Dist: pydantic-ai<=2.
|
|
42
|
+
Requires-Dist: pydantic-ai<=2.42.0,>=2.16.0; extra == "pydanticai"
|
|
42
43
|
Requires-Dist: anyio>=4.0.0; extra == "pydanticai"
|
|
43
44
|
Provides-Extra: pydantic
|
|
44
|
-
Requires-Dist: pydantic-ai<=2.
|
|
45
|
+
Requires-Dist: pydantic-ai<=2.42.0,>=2.16.0; extra == "pydantic"
|
|
45
46
|
Requires-Dist: anyio>=4.0.0; extra == "pydantic"
|
|
46
47
|
Provides-Extra: langgraph
|
|
47
48
|
Requires-Dist: langgraph<=1.2.11,>=1.2.9; extra == "langgraph"
|
|
48
|
-
Requires-Dist: langchain-core<=1.6.
|
|
49
|
+
Requires-Dist: langchain-core<=1.6.2,>=1.5.0; extra == "langgraph"
|
|
49
50
|
Requires-Dist: langchain-openai==1.3.5; extra == "langgraph"
|
|
50
51
|
Requires-Dist: anyio>=4.0.0; extra == "langgraph"
|
|
51
52
|
Provides-Extra: microsoft
|
|
@@ -54,18 +55,18 @@ Requires-Dist: autogen-agentchat==0.7.5; extra == "autogen"
|
|
|
54
55
|
Provides-Extra: microsoft-autogen
|
|
55
56
|
Requires-Dist: autogen-agentchat==0.7.5; extra == "microsoft-autogen"
|
|
56
57
|
Provides-Extra: foundry
|
|
57
|
-
Requires-Dist: azure-ai-projects<=2.
|
|
58
|
+
Requires-Dist: azure-ai-projects<=2.6.0,>=2.3.0; extra == "foundry"
|
|
58
59
|
Requires-Dist: azure-identity==1.25.3; extra == "foundry"
|
|
59
60
|
Provides-Extra: agno
|
|
60
|
-
Requires-Dist: agno<=3.0.
|
|
61
|
+
Requires-Dist: agno<=3.0.9,>=2.8.0; extra == "agno"
|
|
61
62
|
Provides-Extra: openai-agents
|
|
62
|
-
Requires-Dist: openai-agents<=0.22.
|
|
63
|
+
Requires-Dist: openai-agents<=0.22.2,>=0.18.3; extra == "openai-agents"
|
|
63
64
|
Provides-Extra: mcp
|
|
64
|
-
Requires-Dist: mcp<=
|
|
65
|
+
Requires-Dist: mcp<=2.2.0,>=1.26.0; extra == "mcp"
|
|
65
66
|
Provides-Extra: bedrock
|
|
66
|
-
Requires-Dist: boto3<=1.43.
|
|
67
|
+
Requires-Dist: boto3<=1.43.91,>=1.43.54; extra == "bedrock"
|
|
67
68
|
Provides-Extra: aws-bedrock
|
|
68
|
-
Requires-Dist: boto3<=1.43.
|
|
69
|
+
Requires-Dist: boto3<=1.43.91,>=1.43.54; extra == "aws-bedrock"
|
|
69
70
|
Provides-Extra: llamaindex
|
|
70
71
|
Requires-Dist: llama-index<=0.14.24,>=0.14.23; extra == "llamaindex"
|
|
71
72
|
Provides-Extra: llama-index
|
|
@@ -75,30 +76,30 @@ Requires-Dist: google-adk<=2.8.0,>=2.5.0; extra == "google-adk"
|
|
|
75
76
|
Provides-Extra: adk
|
|
76
77
|
Requires-Dist: google-adk<=2.8.0,>=2.5.0; extra == "adk"
|
|
77
78
|
Provides-Extra: claude
|
|
78
|
-
Requires-Dist: claude-agent-sdk<=0.2.
|
|
79
|
+
Requires-Dist: claude-agent-sdk<=0.2.152,>=0.2.126; extra == "claude"
|
|
79
80
|
Provides-Extra: claude-agent-sdk
|
|
80
|
-
Requires-Dist: claude-agent-sdk<=0.2.
|
|
81
|
+
Requires-Dist: claude-agent-sdk<=0.2.152,>=0.2.126; extra == "claude-agent-sdk"
|
|
81
82
|
Provides-Extra: smolagents
|
|
82
83
|
Requires-Dist: smolagents==1.26.0; extra == "smolagents"
|
|
83
84
|
Provides-Extra: all
|
|
84
|
-
Requires-Dist: crewai<=1.15.
|
|
85
|
+
Requires-Dist: crewai<=1.15.21,>=1.15.5; extra == "all"
|
|
85
86
|
Requires-Dist: redis>=4.5.0; extra == "all"
|
|
86
87
|
Requires-Dist: cryptography>=42.0.0; extra == "all"
|
|
87
|
-
Requires-Dist: pydantic-ai<=2.
|
|
88
|
+
Requires-Dist: pydantic-ai<=2.42.0,>=2.16.0; extra == "all"
|
|
88
89
|
Requires-Dist: anyio>=4.0.0; extra == "all"
|
|
89
90
|
Requires-Dist: langgraph<=1.2.11,>=1.2.9; extra == "all"
|
|
90
|
-
Requires-Dist: langchain-core<=1.6.
|
|
91
|
+
Requires-Dist: langchain-core<=1.6.2,>=1.5.0; extra == "all"
|
|
91
92
|
Requires-Dist: langchain-openai==1.3.5; extra == "all"
|
|
92
93
|
Requires-Dist: autogen-agentchat==0.7.5; extra == "all"
|
|
93
|
-
Requires-Dist: agno<=3.0.
|
|
94
|
-
Requires-Dist: azure-ai-projects<=2.
|
|
94
|
+
Requires-Dist: agno<=3.0.9,>=2.8.0; extra == "all"
|
|
95
|
+
Requires-Dist: azure-ai-projects<=2.6.0,>=2.3.0; extra == "all"
|
|
95
96
|
Requires-Dist: azure-identity==1.25.3; extra == "all"
|
|
96
|
-
Requires-Dist: openai-agents<=0.22.
|
|
97
|
-
Requires-Dist: mcp<=
|
|
98
|
-
Requires-Dist: boto3<=1.43.
|
|
97
|
+
Requires-Dist: openai-agents<=0.22.2,>=0.18.3; extra == "all"
|
|
98
|
+
Requires-Dist: mcp<=2.2.0,>=1.26.0; extra == "all"
|
|
99
|
+
Requires-Dist: boto3<=1.43.91,>=1.43.54; extra == "all"
|
|
99
100
|
Requires-Dist: llama-index<=0.14.24,>=0.14.23; extra == "all"
|
|
100
101
|
Requires-Dist: google-adk<=2.8.0,>=2.5.0; extra == "all"
|
|
101
|
-
Requires-Dist: claude-agent-sdk<=0.2.
|
|
102
|
+
Requires-Dist: claude-agent-sdk<=0.2.152,>=0.2.126; extra == "all"
|
|
102
103
|
Requires-Dist: smolagents==1.26.0; extra == "all"
|
|
103
104
|
Provides-Extra: dev
|
|
104
105
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
@@ -2336,7 +2337,7 @@ Start with the dedicated [MCP Host and Gateway Action Firewall guide](https://gi
|
|
|
2336
2337
|
pip install "agenticdome-python-sdk[mcp]"
|
|
2337
2338
|
```
|
|
2338
2339
|
|
|
2339
|
-
**MCP package compatibility:** the `[mcp]` extra currently installs the externally certified PyPI range `mcp>=1.26.0,<=
|
|
2340
|
+
**MCP package compatibility:** the `[mcp]` extra currently installs the externally certified PyPI range `mcp>=1.26.0,<=2.2.0`. That is the supported range of the third-party `mcp` package, not the version of AgenticDome's SDK. MCP 2.0 removed the certified `mcp.server.fastmcp` import surface used by the earlier native compatibility test. AgenticDome's transport-independent JSON-RPC firewall has since passed the isolated MCP 2.x certification matrix through 2.2.0 while preserving the 1.26.0 floor. CrewAI 1.15.x combined installations can still resolve the narrower MCP 1.28.x line according to CrewAI's dependency constraint; this does not reduce the independently certified AgenticDome range.
|
|
2340
2341
|
|
|
2341
2342
|
**Wrap the forwarding boundary** — the full host flow is `JSON-RPC request → rate limit / upstream prompt screen → method authorization → optional delegation-token verification → third-party MCP server → list filtering / result sanitization → client`:
|
|
2342
2343
|
|
|
@@ -2606,6 +2607,30 @@ result = client.mesh_validate(
|
|
|
2606
2607
|
print(result)
|
|
2607
2608
|
```
|
|
2608
2609
|
|
|
2610
|
+
**Multimodal content descriptors:**
|
|
2611
|
+
|
|
2612
|
+
Use `inspect_content()` when a document, image, audio or video processor has
|
|
2613
|
+
already produced bounded text, a transcript, trusted sensitivity labels, or a
|
|
2614
|
+
content digest. The active tenant policy is evaluated inside the assigned
|
|
2615
|
+
AgenticDome runtime. OCR, transcription and proprietary classification are not
|
|
2616
|
+
shipped in this public SDK, and the returned evidence does not retain raw
|
|
2617
|
+
content.
|
|
2618
|
+
|
|
2619
|
+
```python
|
|
2620
|
+
verdict = client.inspect_content(
|
|
2621
|
+
direction="output",
|
|
2622
|
+
content_parts=[{
|
|
2623
|
+
"modality": "image",
|
|
2624
|
+
"mime_type": "image/png",
|
|
2625
|
+
"content_sha256": "sha256:" + image_digest,
|
|
2626
|
+
"labels": ["phi"],
|
|
2627
|
+
}],
|
|
2628
|
+
policy_context={"request_purpose": "clinical-summary-delivery"},
|
|
2629
|
+
)
|
|
2630
|
+
if verdict["verdict"] == "BLOCKED":
|
|
2631
|
+
raise PermissionError(verdict["reason"])
|
|
2632
|
+
```
|
|
2633
|
+
|
|
2609
2634
|
**Incident reporting:**
|
|
2610
2635
|
|
|
2611
2636
|
```python
|
|
@@ -2719,6 +2744,34 @@ Plus, for every production deployment:
|
|
|
2719
2744
|
|
|
2720
2745
|
---
|
|
2721
2746
|
|
|
2747
|
+
## Verified Action lifecycle evidence
|
|
2748
|
+
|
|
2749
|
+
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.
|
|
2750
|
+
|
|
2751
|
+
```bash
|
|
2752
|
+
export AGENTICDOME_EVIDENCE_API_BASE="https://your-control-plane.example"
|
|
2753
|
+
export AGENTICDOME_EVIDENCE_TOKEN="<portal-token-scoped-to-evidence:write>"
|
|
2754
|
+
```
|
|
2755
|
+
|
|
2756
|
+
```python
|
|
2757
|
+
from agenticdome_sdk import VerifiedActionReporter, verified_action
|
|
2758
|
+
|
|
2759
|
+
evidence = VerifiedActionReporter.from_env(tenant_id="your-tenant-id")
|
|
2760
|
+
|
|
2761
|
+
@verified_action(
|
|
2762
|
+
reporter=evidence,
|
|
2763
|
+
operation_type="function_call",
|
|
2764
|
+
executor_type="function",
|
|
2765
|
+
target_type="service",
|
|
2766
|
+
)
|
|
2767
|
+
def refresh_customer_record(customer_id: str):
|
|
2768
|
+
...
|
|
2769
|
+
```
|
|
2770
|
+
|
|
2771
|
+
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.
|
|
2772
|
+
|
|
2773
|
+
---
|
|
2774
|
+
|
|
2722
2775
|
## Source Installation and Verification
|
|
2723
2776
|
|
|
2724
2777
|
Most customers should install the published package from PyPI. Contributors evaluating the public source can run the public tests from the SDK root:
|
|
@@ -1,37 +1,41 @@
|
|
|
1
|
-
agenticdome_python_sdk-1.2.
|
|
2
|
-
agenticdome_python_sdk-1.2.
|
|
3
|
-
agenticdome_sdk/__init__.py,sha256=
|
|
4
|
-
agenticdome_sdk/_framework_firewall.py,sha256=
|
|
1
|
+
agenticdome_python_sdk-1.2.30.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
2
|
+
agenticdome_python_sdk-1.2.30.dist-info/licenses/NOTICE,sha256=obvKP6kV1Ny5R8_INFOpf0X5um9U2X43LGwQFobO9kg,550
|
|
3
|
+
agenticdome_sdk/__init__.py,sha256=UY__0ppEM4MlsTQoLh8alZ7AoMjtegGKe4j_Sj7CpNk,903
|
|
4
|
+
agenticdome_sdk/_framework_firewall.py,sha256=W3gy7WsK50scE7ypJrmVJhHUOBsr0zAWQ85rghqaJk4,31963
|
|
5
5
|
agenticdome_sdk/_mode.py,sha256=XvOgh5sW-FG8S39V483h3uLiFh8wSt14OmYXz_6eDO8,1283
|
|
6
6
|
agenticdome_sdk/agno.py,sha256=L3yfjTBHW47xzT67a1DsjtwmvrAoYBkbxADpsr85Cp0,60766
|
|
7
|
+
agenticdome_sdk/attestation.py,sha256=XFMq8UEMU8L_8-wH6gKTkWJQguYDm3pbZ65G506bLiw,10126
|
|
7
8
|
agenticdome_sdk/autogen.py,sha256=KP4qlIcsNXB-4ZT9D7Slc1xWEeWXRIWkc5HGW0U6k2c,29018
|
|
8
9
|
agenticdome_sdk/aws_bedrock.py,sha256=2zD9j7xi0jna0sMeMf-hKZdc3Y4RPwzj6tMVOUGgIDQ,69581
|
|
9
|
-
agenticdome_sdk/catalog_snapshot.json,sha256=
|
|
10
|
+
agenticdome_sdk/catalog_snapshot.json,sha256=ixwzcK8geYTdan-UrZde0X-twunMHBziPz97fum0LBo,951
|
|
10
11
|
agenticdome_sdk/claude.py,sha256=XeY9AUu61SYcJjQVz_AtL_WaN6X4KqP5nprEbbr4Q6A,17456
|
|
11
|
-
agenticdome_sdk/client.py,sha256=
|
|
12
|
+
agenticdome_sdk/client.py,sha256=Sr43Qp3JWqHGCjWwelgIWGjHPsEdnxaoang70DMOufA,67641
|
|
13
|
+
agenticdome_sdk/connect.py,sha256=yNRh84mqdWr73fXW3S8Zc_lV1QN6gce5T6mZ20cr8ps,16726
|
|
12
14
|
agenticdome_sdk/copilot_ir.py,sha256=OMkzkVGSP2pFkxZBjAXNCCRcJk21X0wnllWNhQeErp0,16588
|
|
13
15
|
agenticdome_sdk/copilot_ir_collector.cjs,sha256=oxLY8ByONo7Es4YjYiJuRz_A7IKL65Sy486UXnu-4Rs,9681
|
|
14
16
|
agenticdome_sdk/crewai.py,sha256=sSJQ49O7-8kqOAD2_jrsMtMOmBOcJmFkeOv7S3-ayMs,52490
|
|
15
17
|
agenticdome_sdk/demo.py,sha256=dyfqt2N_cJoyUWKnjPYuQxuw2hb2DrCNUf1jQKeYYoc,8350
|
|
16
18
|
agenticdome_sdk/google_adk.py,sha256=2TA-tUhY0lOnkGcs1BN9CUEz67ebVX4HJ7mnNoRPcZs,57258
|
|
17
|
-
agenticdome_sdk/hook_catalog.py,sha256=
|
|
19
|
+
agenticdome_sdk/hook_catalog.py,sha256=PduNxLhnBiIF14mAxABnpBPsvR7sTzDmCyudbU1BvrI,29272
|
|
18
20
|
agenticdome_sdk/identity.py,sha256=mrymGgZSVsKJ9THRde54qGmMT7j_uZHmPJ-HtWk7PXE,4635
|
|
19
21
|
agenticdome_sdk/langgraph.py,sha256=FUvF6DSA5aZP3eoBoNeldgGtPrz-dO5xcVmrPPSBEhI,84312
|
|
22
|
+
agenticdome_sdk/lifecycle.py,sha256=9LvxmUU1LC_tMchb5uVFBnZIkVFUxOv3VH9wG_9gPtA,12855
|
|
20
23
|
agenticdome_sdk/llamaindex.py,sha256=BHplSUIsgMi7k8tBztprzaV9iaOXiljXbdeMRax_2h8,42860
|
|
21
|
-
agenticdome_sdk/mcp_host.py,sha256=
|
|
24
|
+
agenticdome_sdk/mcp_host.py,sha256=bJG0pCfMYkI6ig4tmPvtJKcdFFfDOXNmatLbh_X9lso,63283
|
|
22
25
|
agenticdome_sdk/mcp_http_gateway.py,sha256=j7B_IVhr0QLSPdsE8iVKcH0mCtQN7EE622JKY3d2LHI,13533
|
|
23
26
|
agenticdome_sdk/mcp_rehearsal_server.py,sha256=cox4XqUTL8d0gaDzqxDZcEALkaz8otjOjOqHvoUeeCo,2118
|
|
24
27
|
agenticdome_sdk/mcp_verification.py,sha256=VqQS5SpPVFI3G7qCwrT90JmG8vFE6pRJEB7PViHjKqw,14235
|
|
25
28
|
agenticdome_sdk/microsoft_agent_framework.py,sha256=GfIIuk0n-VhsZaTyoB7P6ilpz9bFLD95TWuX0rFSWeU,67219
|
|
26
29
|
agenticdome_sdk/microsoft_ai_foundry.py,sha256=YlO968s1dbAYcaEqhqufL92NFR66GuTtngnCv7-1mYc,56911
|
|
27
|
-
agenticdome_sdk/onboarding_cli.py,sha256=
|
|
30
|
+
agenticdome_sdk/onboarding_cli.py,sha256=8ymGtNmllNH6lP87pvxHe2GySlcsHoDoLiispfQ5rDs,95417
|
|
28
31
|
agenticdome_sdk/openai_agents.py,sha256=nNM-YcsUDcE23sGv1buLudcYFz0NLju2Mwiw_wrVaJg,57326
|
|
32
|
+
agenticdome_sdk/outcomes.py,sha256=jadWDzQaZXrHNvpPme8lQY0jihYLCoM3s4CihAq0JRo,2507
|
|
29
33
|
agenticdome_sdk/proof.py,sha256=ECKc14xSE0V1HHmcpZjfVz8-Q2H_fooah3BfdblFV8M,2878
|
|
30
34
|
agenticdome_sdk/pydantic.py,sha256=WKb8X0Fgydfid-ep8E-M8Yslz2pWcCP6w7n_aBxt1rI,54961
|
|
31
35
|
agenticdome_sdk/simulation.py,sha256=UCbZy3u9AzfxQCfY38SCV3CxGxhinV9EA4aHMPlK4mc,8426
|
|
32
36
|
agenticdome_sdk/smolagents.py,sha256=wjMA1HVAQ-9wRNM38Jr9wlCpKMhF-9vxtTk6R-kwQhs,19094
|
|
33
|
-
agenticdome_python_sdk-1.2.
|
|
34
|
-
agenticdome_python_sdk-1.2.
|
|
35
|
-
agenticdome_python_sdk-1.2.
|
|
36
|
-
agenticdome_python_sdk-1.2.
|
|
37
|
-
agenticdome_python_sdk-1.2.
|
|
37
|
+
agenticdome_python_sdk-1.2.30.dist-info/METADATA,sha256=VfpIoruBWktFlx3XfM79Psr_SUgJRHllPWbRoDid-I0,139108
|
|
38
|
+
agenticdome_python_sdk-1.2.30.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
39
|
+
agenticdome_python_sdk-1.2.30.dist-info/entry_points.txt,sha256=cRfCphkr5gLrkGIzXJEtL50HdHLajoZLbZtcMLE8NoY,113
|
|
40
|
+
agenticdome_python_sdk-1.2.30.dist-info/top_level.txt,sha256=LZbbjXco5mqDrGwlTJpIKI5Sp6R_njzHHjExxAs9c3s,16
|
|
41
|
+
agenticdome_python_sdk-1.2.30.dist-info/RECORD,,
|
agenticdome_sdk/__init__.py
CHANGED
|
@@ -2,6 +2,7 @@ from .client import AgenticDomeClient, AgenticDomeError, AgenticDomeHTTPError
|
|
|
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
|
|
5
|
+
from .lifecycle import VerifiedActionContext, VerifiedActionDenied, VerifiedActionReporter, verified_action
|
|
5
6
|
|
|
6
7
|
__all__ = [
|
|
7
8
|
"AgenticDomeClient",
|
|
@@ -17,4 +18,8 @@ __all__ = [
|
|
|
17
18
|
"LOCAL_SIM_MODE",
|
|
18
19
|
"is_local_sim_mode",
|
|
19
20
|
"resolve_mode",
|
|
21
|
+
"VerifiedActionContext",
|
|
22
|
+
"VerifiedActionDenied",
|
|
23
|
+
"VerifiedActionReporter",
|
|
24
|
+
"verified_action",
|
|
20
25
|
]
|
|
@@ -16,6 +16,7 @@ from threading import Lock, Thread
|
|
|
16
16
|
from typing import Any, Awaitable, Callable, Deque, Dict, Optional, Tuple, Type
|
|
17
17
|
|
|
18
18
|
from .client import AgenticDomeClient
|
|
19
|
+
from .lifecycle import VerifiedActionReporter
|
|
19
20
|
from ._mode import credentials_or_local_sim
|
|
20
21
|
|
|
21
22
|
|
|
@@ -201,6 +202,7 @@ class FrameworkFirewallBase:
|
|
|
201
202
|
timeout=config.timeout_s,
|
|
202
203
|
)
|
|
203
204
|
self.token_store = token_store or build_token_store(config, logger)
|
|
205
|
+
self.lifecycle_reporter = VerifiedActionReporter.from_env(tenant_id=config.tenant_id)
|
|
204
206
|
self.denied_error = denied_error
|
|
205
207
|
self.label = label
|
|
206
208
|
self.logger = logger
|
|
@@ -211,6 +213,7 @@ class FrameworkFirewallBase:
|
|
|
211
213
|
self._circuit_open_until = 0.0
|
|
212
214
|
|
|
213
215
|
def close(self) -> None:
|
|
216
|
+
self.lifecycle_reporter.flush(timeout=2.0)
|
|
214
217
|
try:
|
|
215
218
|
self.client.close()
|
|
216
219
|
except Exception:
|
|
@@ -611,35 +614,73 @@ class FrameworkFirewallBase:
|
|
|
611
614
|
call_args = self.normalize_args(kwargs if kwargs else (args[0] if len(args) == 1 and isinstance(args[0], dict) else {}))
|
|
612
615
|
sid = self.session_id(session_id)
|
|
613
616
|
aid = agent_id or self.config.agent_id
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
+
lifecycle = self.lifecycle_reporter.new_context(
|
|
618
|
+
operation_type="tool_call", tool_name=tool_name, arguments=call_args,
|
|
619
|
+
initiator_type="agent", executor_type="tool", target_type="tool",
|
|
617
620
|
)
|
|
621
|
+
self.lifecycle_reporter.phase(lifecycle, "requested", "requested")
|
|
622
|
+
try:
|
|
623
|
+
decision = await self.aauthorize_tool_call(
|
|
624
|
+
session_id=sid, agent_id=aid, tool_name=tool_name, tool_args=call_args,
|
|
625
|
+
tool_platform=tool_platform, text=f"[{self.label}] {aid} calls {tool_name}",
|
|
626
|
+
)
|
|
627
|
+
except Exception:
|
|
628
|
+
self.lifecycle_reporter.phase(lifecycle, "authorised", "blocked")
|
|
629
|
+
self.lifecycle_reporter.outcome(lifecycle, "not_attempted")
|
|
630
|
+
raise
|
|
631
|
+
self.lifecycle_reporter.phase(lifecycle, "authorised", "allowed")
|
|
632
|
+
self.lifecycle_reporter.phase(lifecycle, "admitted", "admitted")
|
|
618
633
|
clean = self.sanitized_args(decision, call_args)
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
634
|
+
self.lifecycle_reporter.phase(lifecycle, "attempted", "attempted")
|
|
635
|
+
try:
|
|
636
|
+
if kwargs:
|
|
637
|
+
raw = await handler(*args, **clean)
|
|
638
|
+
elif len(args) == 1 and isinstance(args[0], dict):
|
|
639
|
+
raw = await handler(clean)
|
|
640
|
+
else:
|
|
641
|
+
raw = await handler(*args, **kwargs)
|
|
642
|
+
reviewed = await self.areview_value(raw, session_id=sid, agent_id=aid, policy_context={"tool_name": tool_name}) if sanitize_output else raw
|
|
643
|
+
except Exception:
|
|
644
|
+
self.lifecycle_reporter.outcome(lifecycle, "failed")
|
|
645
|
+
raise
|
|
646
|
+
self.lifecycle_reporter.outcome(lifecycle, "succeeded")
|
|
647
|
+
return reviewed
|
|
626
648
|
|
|
627
649
|
def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
628
650
|
call_args = self.normalize_args(kwargs if kwargs else (args[0] if len(args) == 1 and isinstance(args[0], dict) else {}))
|
|
629
651
|
sid = self.session_id(session_id)
|
|
630
652
|
aid = agent_id or self.config.agent_id
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
653
|
+
lifecycle = self.lifecycle_reporter.new_context(
|
|
654
|
+
operation_type="tool_call", tool_name=tool_name, arguments=call_args,
|
|
655
|
+
initiator_type="agent", executor_type="tool", target_type="tool",
|
|
634
656
|
)
|
|
657
|
+
self.lifecycle_reporter.phase(lifecycle, "requested", "requested")
|
|
658
|
+
try:
|
|
659
|
+
decision = self.authorize_tool_call(
|
|
660
|
+
session_id=sid, agent_id=aid, tool_name=tool_name, tool_args=call_args,
|
|
661
|
+
tool_platform=tool_platform, text=f"[{self.label}] {aid} calls {tool_name}",
|
|
662
|
+
)
|
|
663
|
+
except Exception:
|
|
664
|
+
self.lifecycle_reporter.phase(lifecycle, "authorised", "blocked")
|
|
665
|
+
self.lifecycle_reporter.outcome(lifecycle, "not_attempted")
|
|
666
|
+
raise
|
|
667
|
+
self.lifecycle_reporter.phase(lifecycle, "authorised", "allowed")
|
|
668
|
+
self.lifecycle_reporter.phase(lifecycle, "admitted", "admitted")
|
|
635
669
|
clean = self.sanitized_args(decision, call_args)
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
670
|
+
self.lifecycle_reporter.phase(lifecycle, "attempted", "attempted")
|
|
671
|
+
try:
|
|
672
|
+
if kwargs:
|
|
673
|
+
raw = handler(*args, **clean)
|
|
674
|
+
elif len(args) == 1 and isinstance(args[0], dict):
|
|
675
|
+
raw = handler(clean)
|
|
676
|
+
else:
|
|
677
|
+
raw = handler(*args, **kwargs)
|
|
678
|
+
reviewed = self.review_value(raw, session_id=sid, agent_id=aid, policy_context={"tool_name": tool_name}) if sanitize_output else raw
|
|
679
|
+
except Exception:
|
|
680
|
+
self.lifecycle_reporter.outcome(lifecycle, "failed")
|
|
681
|
+
raise
|
|
682
|
+
self.lifecycle_reporter.outcome(lifecycle, "succeeded")
|
|
683
|
+
return reviewed
|
|
643
684
|
|
|
644
685
|
wrapper = async_wrapper if is_async else sync_wrapper
|
|
645
686
|
return functools.wraps(handler)(wrapper)
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""Signed runtime-coverage evidence with no customer source or proprietary analysis."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import base64
|
|
6
|
+
import hashlib
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import threading
|
|
10
|
+
import time
|
|
11
|
+
import urllib.request
|
|
12
|
+
import uuid
|
|
13
|
+
from datetime import datetime, timezone
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, Dict, Iterable, Optional
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _crypto():
|
|
19
|
+
try:
|
|
20
|
+
from cryptography.hazmat.primitives import hashes, serialization
|
|
21
|
+
from cryptography.hazmat.primitives.asymmetric import padding, rsa
|
|
22
|
+
except ImportError as exc: # pragma: no cover - packaging/install failure
|
|
23
|
+
raise RuntimeError("Runtime attestation requires the SDK's cryptography dependency.") from exc
|
|
24
|
+
return hashes, serialization, padding, rsa
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def canonical_json(value: Dict[str, Any]) -> bytes:
|
|
28
|
+
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def ensure_attestation_key(directory: Path) -> tuple[str, str]:
|
|
32
|
+
"""Return private/public PEM, creating a local non-uploaded private key once."""
|
|
33
|
+
hashes, serialization, padding, rsa = _crypto()
|
|
34
|
+
del hashes, padding
|
|
35
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
36
|
+
private_path = directory / "attestation-private.pem"
|
|
37
|
+
public_path = directory / "attestation-public.pem"
|
|
38
|
+
if private_path.exists():
|
|
39
|
+
private_pem = private_path.read_text(encoding="ascii")
|
|
40
|
+
key = serialization.load_pem_private_key(private_pem.encode("ascii"), password=None)
|
|
41
|
+
else:
|
|
42
|
+
key = rsa.generate_private_key(public_exponent=65537, key_size=3072)
|
|
43
|
+
private_pem = key.private_bytes(
|
|
44
|
+
serialization.Encoding.PEM,
|
|
45
|
+
serialization.PrivateFormat.PKCS8,
|
|
46
|
+
serialization.NoEncryption(),
|
|
47
|
+
).decode("ascii")
|
|
48
|
+
private_path.write_text(private_pem, encoding="ascii")
|
|
49
|
+
os.chmod(private_path, 0o600)
|
|
50
|
+
public_pem = key.public_key().public_bytes(
|
|
51
|
+
serialization.Encoding.PEM,
|
|
52
|
+
serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
53
|
+
).decode("ascii")
|
|
54
|
+
public_path.write_text(public_pem, encoding="ascii")
|
|
55
|
+
os.chmod(public_path, 0o644)
|
|
56
|
+
return private_pem, public_pem
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def sign_claims(claims: Dict[str, Any], private_key_pem: str) -> str:
|
|
60
|
+
hashes, serialization, padding, rsa = _crypto()
|
|
61
|
+
del rsa
|
|
62
|
+
key = serialization.load_pem_private_key(private_key_pem.encode("ascii"), password=None)
|
|
63
|
+
signature = key.sign(canonical_json(claims), padding.PKCS1v15(), hashes.SHA256())
|
|
64
|
+
return base64.b64encode(signature).decode("ascii")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def build_hook_manifest(
|
|
68
|
+
*, tenant_id: str, workload_uuid: str, deployment_id: str,
|
|
69
|
+
expected_hooks: Iterable[str], observed_hooks: Iterable[str],
|
|
70
|
+
private_key_pem: str, sdk_version: str, framework_versions: Optional[Dict[str, str]] = None,
|
|
71
|
+
high_impact_actions: Optional[Iterable[str]] = None, build_sha256: Optional[str] = None,
|
|
72
|
+
sdk_checksum: Optional[str] = None, ttl_seconds: int = 600,
|
|
73
|
+
) -> Dict[str, Any]:
|
|
74
|
+
now = int(time.time())
|
|
75
|
+
claims: Dict[str, Any] = {
|
|
76
|
+
"schema": "agenticdome.hook-manifest.v1",
|
|
77
|
+
"tenant_id": tenant_id,
|
|
78
|
+
"workload_uuid": workload_uuid,
|
|
79
|
+
"deployment_id": deployment_id,
|
|
80
|
+
"manifest_id": str(uuid.uuid4()),
|
|
81
|
+
"build_sha256": build_sha256,
|
|
82
|
+
"sdk_checksum": sdk_checksum,
|
|
83
|
+
"sdk_version": sdk_version,
|
|
84
|
+
"framework_versions": framework_versions or {},
|
|
85
|
+
"expected_hooks": sorted(set(str(item) for item in expected_hooks if item)),
|
|
86
|
+
"observed_hooks": sorted(set(str(item) for item in observed_hooks if item)),
|
|
87
|
+
"high_impact_actions": sorted(set(str(item) for item in (high_impact_actions or []) if item)),
|
|
88
|
+
"started_at": datetime.fromtimestamp(now, timezone.utc).isoformat(),
|
|
89
|
+
"expires_at": datetime.fromtimestamp(now + max(60, min(ttl_seconds, 600)), timezone.utc).isoformat(),
|
|
90
|
+
}
|
|
91
|
+
return {**claims, "signature": sign_claims(claims, private_key_pem)}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def build_hook_heartbeat(
|
|
95
|
+
*, tenant_id: str, workload_uuid: str, deployment_id: str, manifest_sha256: str,
|
|
96
|
+
active_hooks: Iterable[str], sequence: int, private_key_pem: str, ttl_seconds: int = 120,
|
|
97
|
+
) -> Dict[str, Any]:
|
|
98
|
+
now = int(time.time())
|
|
99
|
+
claims: Dict[str, Any] = {
|
|
100
|
+
"schema": "agenticdome.hook-heartbeat.v1",
|
|
101
|
+
"tenant_id": tenant_id,
|
|
102
|
+
"workload_uuid": workload_uuid,
|
|
103
|
+
"deployment_id": deployment_id,
|
|
104
|
+
"manifest_sha256": manifest_sha256,
|
|
105
|
+
"sequence": max(1, int(sequence)),
|
|
106
|
+
"active_hooks": sorted(set(str(item) for item in active_hooks if item)),
|
|
107
|
+
"observed_at": datetime.fromtimestamp(now, timezone.utc).isoformat(),
|
|
108
|
+
"expires_at": datetime.fromtimestamp(now + max(30, min(ttl_seconds, 300)), timezone.utc).isoformat(),
|
|
109
|
+
}
|
|
110
|
+
return {**claims, "signature": sign_claims(claims, private_key_pem)}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def manifest_sha256(manifest: Dict[str, Any]) -> str:
|
|
114
|
+
claims = {key: value for key, value in manifest.items() if key != "signature"}
|
|
115
|
+
return hashlib.sha256(canonical_json(claims)).hexdigest()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _post_json(url: str, payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
119
|
+
request = urllib.request.Request(url, data=canonical_json(payload), method="POST", headers={"Content-Type": "application/json", "Accept": "application/json"})
|
|
120
|
+
with urllib.request.urlopen(request, timeout=15) as response: # noqa: S310 - explicit AgenticDome portal
|
|
121
|
+
value = json.loads(response.read().decode("utf-8"))
|
|
122
|
+
if not isinstance(value, dict):
|
|
123
|
+
raise RuntimeError("AgenticDome returned an invalid attestation response.")
|
|
124
|
+
return value
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class RuntimeCoverageAttestor:
|
|
128
|
+
"""Background signed manifest/heartbeat reporter; never sends source or arguments."""
|
|
129
|
+
|
|
130
|
+
def __init__(self, *, portal: str, tenant_id: str, workload_uuid: str, deployment_id: str,
|
|
131
|
+
private_key_pem: str, expected_hooks: Iterable[str], sdk_version: str,
|
|
132
|
+
high_impact_actions: Optional[Iterable[str]] = None, heartbeat_seconds: int = 60):
|
|
133
|
+
self.portal = portal.rstrip("/")
|
|
134
|
+
self.tenant_id = tenant_id
|
|
135
|
+
self.workload_uuid = workload_uuid
|
|
136
|
+
self.deployment_id = deployment_id
|
|
137
|
+
self.private_key_pem = private_key_pem
|
|
138
|
+
self.expected_hooks = set(str(item) for item in expected_hooks if item)
|
|
139
|
+
self.observed_hooks: set[str] = set()
|
|
140
|
+
self.sdk_version = sdk_version
|
|
141
|
+
self.high_impact_actions = set(str(item) for item in (high_impact_actions or []) if item)
|
|
142
|
+
self.heartbeat_seconds = max(15, min(int(heartbeat_seconds), 120))
|
|
143
|
+
self.sequence = 0
|
|
144
|
+
self.current_manifest_sha256 = ""
|
|
145
|
+
self.last_error: Optional[str] = None
|
|
146
|
+
self._stop = threading.Event()
|
|
147
|
+
self._thread: Optional[threading.Thread] = None
|
|
148
|
+
self._lock = threading.Lock()
|
|
149
|
+
|
|
150
|
+
@classmethod
|
|
151
|
+
def from_environment(cls, expected_hooks: Iterable[str], high_impact_actions: Optional[Iterable[str]] = None) -> Optional["RuntimeCoverageAttestor"]:
|
|
152
|
+
names = ["AGENTICDOME_CONTROL_PLANE_URL", "AGENTICDOME_TENANT_ID", "AGENTICDOME_WORKLOAD_UUID", "AGENTICDOME_DEPLOYMENT_ID", "AGENTICDOME_ATTESTATION_KEY_PATH"]
|
|
153
|
+
values = {name: os.getenv(name, "").strip() for name in names}
|
|
154
|
+
if not all(values.values()):
|
|
155
|
+
return None
|
|
156
|
+
key = Path(values["AGENTICDOME_ATTESTATION_KEY_PATH"]).read_text(encoding="ascii")
|
|
157
|
+
return cls(portal=values["AGENTICDOME_CONTROL_PLANE_URL"], tenant_id=values["AGENTICDOME_TENANT_ID"],
|
|
158
|
+
workload_uuid=values["AGENTICDOME_WORKLOAD_UUID"], deployment_id=values["AGENTICDOME_DEPLOYMENT_ID"],
|
|
159
|
+
private_key_pem=key, expected_hooks=expected_hooks, sdk_version=os.getenv("AGENTICDOME_SDK_VERSION", "unknown"),
|
|
160
|
+
high_impact_actions=high_impact_actions)
|
|
161
|
+
|
|
162
|
+
def observe(self, hook_id: str) -> None:
|
|
163
|
+
if hook_id:
|
|
164
|
+
with self._lock:
|
|
165
|
+
self.observed_hooks.add(str(hook_id))
|
|
166
|
+
|
|
167
|
+
def start(self) -> None:
|
|
168
|
+
if self._thread and self._thread.is_alive():
|
|
169
|
+
return
|
|
170
|
+
self._thread = threading.Thread(target=self._run, name="agenticdome-coverage", daemon=True)
|
|
171
|
+
self._thread.start()
|
|
172
|
+
|
|
173
|
+
def stop(self, timeout: float = 2.0) -> None:
|
|
174
|
+
self._stop.set()
|
|
175
|
+
if self._thread:
|
|
176
|
+
self._thread.join(timeout=max(0.0, timeout))
|
|
177
|
+
|
|
178
|
+
def _manifest(self) -> None:
|
|
179
|
+
with self._lock:
|
|
180
|
+
observed = sorted(self.observed_hooks)
|
|
181
|
+
manifest = build_hook_manifest(tenant_id=self.tenant_id, workload_uuid=self.workload_uuid,
|
|
182
|
+
deployment_id=self.deployment_id, expected_hooks=self.expected_hooks,
|
|
183
|
+
observed_hooks=observed, private_key_pem=self.private_key_pem,
|
|
184
|
+
sdk_version=self.sdk_version, high_impact_actions=self.high_impact_actions)
|
|
185
|
+
_post_json(self.portal + "/api/agentguard/runtime/hook-manifest", manifest)
|
|
186
|
+
self.current_manifest_sha256 = manifest_sha256(manifest)
|
|
187
|
+
|
|
188
|
+
def _run(self) -> None:
|
|
189
|
+
manifest_at = 0.0
|
|
190
|
+
while not self._stop.is_set():
|
|
191
|
+
try:
|
|
192
|
+
if not self.current_manifest_sha256 or time.monotonic() - manifest_at >= 300:
|
|
193
|
+
self._manifest()
|
|
194
|
+
manifest_at = time.monotonic()
|
|
195
|
+
self.sequence += 1
|
|
196
|
+
with self._lock:
|
|
197
|
+
active = sorted(self.observed_hooks)
|
|
198
|
+
heartbeat = build_hook_heartbeat(tenant_id=self.tenant_id, workload_uuid=self.workload_uuid,
|
|
199
|
+
deployment_id=self.deployment_id, manifest_sha256=self.current_manifest_sha256,
|
|
200
|
+
active_hooks=active, sequence=self.sequence, private_key_pem=self.private_key_pem)
|
|
201
|
+
_post_json(self.portal + "/api/agentguard/runtime/hook-heartbeat", heartbeat)
|
|
202
|
+
self.last_error = None
|
|
203
|
+
except Exception as exc: # evidence failure is surfaced via stale coverage; action fail-closed remains gateway-owned
|
|
204
|
+
self.last_error = exc.__class__.__name__
|
|
205
|
+
self._stop.wait(self.heartbeat_seconds)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "agenticdome.catalog-snapshot.v1",
|
|
3
3
|
"catalog_schema": "agenticdome.hook-catalog.v1",
|
|
4
|
-
"verified_at": "2026-
|
|
5
|
-
"python_sdk_version": "1.2.
|
|
4
|
+
"verified_at": "2026-09-10",
|
|
5
|
+
"python_sdk_version": "1.2.30",
|
|
6
6
|
"published_packages": {
|
|
7
7
|
"agenticdome-python-sdk": {
|
|
8
8
|
"registry": "pypi",
|
|
9
|
-
"version": "1.2.
|
|
10
|
-
"url": "https://pypi.org/project/agenticdome-python-sdk/1.2.
|
|
9
|
+
"version": "1.2.30",
|
|
10
|
+
"url": "https://pypi.org/project/agenticdome-python-sdk/1.2.30/"
|
|
11
11
|
},
|
|
12
12
|
"agenticdome-sdk": {
|
|
13
13
|
"registry": "npm",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
},
|
|
22
22
|
"openclaw": {
|
|
23
23
|
"registry": "npm",
|
|
24
|
-
"version": "2026.
|
|
25
|
-
"url": "https://www.npmjs.com/package/openclaw/v/2026.
|
|
24
|
+
"version": "2026.9.3",
|
|
25
|
+
"url": "https://www.npmjs.com/package/openclaw/v/2026.9.3"
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
}
|