agentseal-gateframe 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentseal_gateframe-1.0.0/PKG-INFO +25 -0
- agentseal_gateframe-1.0.0/agentseal/__init__.py +43 -0
- agentseal_gateframe-1.0.0/agentseal/chain.py +194 -0
- agentseal_gateframe-1.0.0/agentseal/export.py +473 -0
- agentseal_gateframe-1.0.0/agentseal/integrations/__init__.py +0 -0
- agentseal_gateframe-1.0.0/agentseal/integrations/autogen_hook.py +135 -0
- agentseal_gateframe-1.0.0/agentseal/integrations/crewai_callback.py +127 -0
- agentseal_gateframe-1.0.0/agentseal/integrations/langchain_callback.py +127 -0
- agentseal_gateframe-1.0.0/agentseal/policies.py +215 -0
- agentseal_gateframe-1.0.0/agentseal/profiles/__init__.py +1 -0
- agentseal_gateframe-1.0.0/agentseal/profiles/adverse_action.py +326 -0
- agentseal_gateframe-1.0.0/agentseal/schema.py +149 -0
- agentseal_gateframe-1.0.0/agentseal/setup.py +2 -0
- agentseal_gateframe-1.0.0/agentseal/wrapper.py +303 -0
- agentseal_gateframe-1.0.0/agentseal_gateframe.egg-info/PKG-INFO +25 -0
- agentseal_gateframe-1.0.0/agentseal_gateframe.egg-info/SOURCES.txt +40 -0
- agentseal_gateframe-1.0.0/agentseal_gateframe.egg-info/dependency_links.txt +1 -0
- agentseal_gateframe-1.0.0/agentseal_gateframe.egg-info/requires.txt +10 -0
- agentseal_gateframe-1.0.0/agentseal_gateframe.egg-info/top_level.txt +4 -0
- agentseal_gateframe-1.0.0/api/__init__.py +0 -0
- agentseal_gateframe-1.0.0/api/auth.py +86 -0
- agentseal_gateframe-1.0.0/api/main.py +379 -0
- agentseal_gateframe-1.0.0/api/main_bkp200526.py +299 -0
- agentseal_gateframe-1.0.0/api/main_old.py +64 -0
- agentseal_gateframe-1.0.0/api/routes/__init__.py +0 -0
- agentseal_gateframe-1.0.0/api/routes/batch_sign.py +300 -0
- agentseal_gateframe-1.0.0/api/routes/chain_verify.py +213 -0
- agentseal_gateframe-1.0.0/api/routes/claims_reconstruct.py +434 -0
- agentseal_gateframe-1.0.0/api/routes/dpr_registry.py +217 -0
- agentseal_gateframe-1.0.0/api/routes/evidence_pack.py +148 -0
- agentseal_gateframe-1.0.0/api/routes/sign.py +119 -0
- agentseal_gateframe-1.0.0/api/routes/validate.py +108 -0
- agentseal_gateframe-1.0.0/core/__init__.py +0 -0
- agentseal_gateframe-1.0.0/core/dpr.py +173 -0
- agentseal_gateframe-1.0.0/core/reason_codes.py +873 -0
- agentseal_gateframe-1.0.0/core/signing.py +221 -0
- agentseal_gateframe-1.0.0/models/__init__.py +0 -0
- agentseal_gateframe-1.0.0/models/database.py +243 -0
- agentseal_gateframe-1.0.0/models/database_old.py +93 -0
- agentseal_gateframe-1.0.0/models/schemas.py +184 -0
- agentseal_gateframe-1.0.0/setup.cfg +4 -0
- agentseal_gateframe-1.0.0/setup.py +22 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentseal-gateframe
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: GateFrame AgentSeal — Cryptographic evidence receipts for AI agent decisions
|
|
5
|
+
Home-page: https://gateframe-api.netlify.app
|
|
6
|
+
Author: Shankar Anand
|
|
7
|
+
Author-email: shankar@gateframe.io
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Requires-Dist: requests>=2.28
|
|
10
|
+
Provides-Extra: langchain
|
|
11
|
+
Requires-Dist: langchain-core>=0.1; extra == "langchain"
|
|
12
|
+
Provides-Extra: autogen
|
|
13
|
+
Requires-Dist: autogen-agentchat>=0.4; extra == "autogen"
|
|
14
|
+
Provides-Extra: crewai
|
|
15
|
+
Requires-Dist: crewai>=0.28; extra == "crewai"
|
|
16
|
+
Dynamic: author
|
|
17
|
+
Dynamic: author-email
|
|
18
|
+
Dynamic: description
|
|
19
|
+
Dynamic: home-page
|
|
20
|
+
Dynamic: provides-extra
|
|
21
|
+
Dynamic: requires-dist
|
|
22
|
+
Dynamic: requires-python
|
|
23
|
+
Dynamic: summary
|
|
24
|
+
|
|
25
|
+
Ed25519 + SHA-256 Merkle chain. EU AI Act Art. 12. ECOA Reg B. DIFC Reg 10. Patent Pending USPTO 64/067,547.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AgentSeal by GateFrame
|
|
3
|
+
Cryptographic evidence receipts for regulated AI agents.
|
|
4
|
+
One line of code. Any framework. Any jurisdiction.
|
|
5
|
+
|
|
6
|
+
"Logs tell you what happened. GateFrame proves what AI did."
|
|
7
|
+
|
|
8
|
+
Quickstart:
|
|
9
|
+
from agentseal import GateFrameClient
|
|
10
|
+
|
|
11
|
+
client = GateFrameClient(
|
|
12
|
+
api_key="sk-ant-...",
|
|
13
|
+
agent_id="loan-underwriter-v3",
|
|
14
|
+
regulatory_framework="ECOA",
|
|
15
|
+
)
|
|
16
|
+
chain = client.start_session(session_id="APP-2026-90412")
|
|
17
|
+
|
|
18
|
+
response = client.messages_create(
|
|
19
|
+
model="claude-opus-4-5",
|
|
20
|
+
messages=[{"role": "user", "content": "Evaluate this application..."}],
|
|
21
|
+
max_tokens=1024,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
evidence = client.get_evidence()
|
|
25
|
+
verification = client.verify()
|
|
26
|
+
# {"valid": True, "total_records": 3}
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from .schema import AgentAction
|
|
30
|
+
from .chain import EvidenceChain
|
|
31
|
+
from .wrapper import GateFrameClient
|
|
32
|
+
from .policies import SEED_POLICIES, PolicyEngine, CompliancePolicy
|
|
33
|
+
|
|
34
|
+
__version__ = "0.1.0"
|
|
35
|
+
__author__ = "Shankar Anand <shankar@gateframe.io>"
|
|
36
|
+
__all__ = [
|
|
37
|
+
"AgentAction",
|
|
38
|
+
"EvidenceChain",
|
|
39
|
+
"GateFrameClient",
|
|
40
|
+
"SEED_POLICIES",
|
|
41
|
+
"PolicyEngine",
|
|
42
|
+
"CompliancePolicy",
|
|
43
|
+
]
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""
|
|
2
|
+
agentseal/chain.py
|
|
3
|
+
EvidenceChain — append-only, hash-linked sequence of AgentActions.
|
|
4
|
+
A single bit-flip anywhere breaks verify_chain().
|
|
5
|
+
This is the artifact regulators verify.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import asdict
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
|
+
from typing import Optional, List
|
|
11
|
+
import json
|
|
12
|
+
import uuid
|
|
13
|
+
|
|
14
|
+
from .schema import AgentAction
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class EvidenceChain:
|
|
18
|
+
"""
|
|
19
|
+
Append-only, hash-linked chain of AgentActions.
|
|
20
|
+
|
|
21
|
+
Each record's prev_hash points to the previous record's record_hash.
|
|
22
|
+
The chain is tamper-evident: any modification anywhere invalidates
|
|
23
|
+
all subsequent links and is detected by verify_chain().
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
chain = EvidenceChain(chain_id="APP-2026-90412", agent_id="underwriter-v3")
|
|
27
|
+
action = chain.record(
|
|
28
|
+
action_type="CREDIT_DENIAL",
|
|
29
|
+
action_payload={"dti": 0.48, "utilization": 0.82},
|
|
30
|
+
outcome={"decision": "denied"},
|
|
31
|
+
regulatory_context={"framework": "ECOA", "evidence_format": "CFPB_MODEL_C1"}
|
|
32
|
+
)
|
|
33
|
+
result = chain.verify_chain()
|
|
34
|
+
# {"valid": True, "total_records": 1, "broken_at": None}
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
SCHEMA_VERSION = "gateframe-agentseal-v1"
|
|
38
|
+
|
|
39
|
+
def __init__(self, chain_id: Optional[str] = None, agent_id: str = ""):
|
|
40
|
+
self.chain_id: str = chain_id or str(uuid.uuid4())
|
|
41
|
+
self.agent_id: str = agent_id
|
|
42
|
+
self.records: List[AgentAction] = []
|
|
43
|
+
self.created_at: str = datetime.now(timezone.utc).isoformat()
|
|
44
|
+
self._session_metadata: dict = {}
|
|
45
|
+
|
|
46
|
+
# ── Core API ──────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
def record(
|
|
49
|
+
self,
|
|
50
|
+
action_type: str,
|
|
51
|
+
action_payload: dict,
|
|
52
|
+
outcome: dict,
|
|
53
|
+
regulatory_context: Optional[dict] = None,
|
|
54
|
+
reasoning: Optional[list] = None,
|
|
55
|
+
agent_version: str = "",
|
|
56
|
+
authorized_by: str = "",
|
|
57
|
+
authorized_at: str = "",
|
|
58
|
+
operator_id: str = "",
|
|
59
|
+
operator_name: str = "",
|
|
60
|
+
institution_id: str = "",
|
|
61
|
+
institution_name: str = "",
|
|
62
|
+
delegation_present: bool = False,
|
|
63
|
+
decision_confidence: float = 0.0,
|
|
64
|
+
input_hash: Optional[str] = None,
|
|
65
|
+
) -> AgentAction:
|
|
66
|
+
"""
|
|
67
|
+
Append a new evidence record to the chain.
|
|
68
|
+
Automatically sets prev_hash to link it to the previous record.
|
|
69
|
+
Calls .seal() to compute and store record_hash before appending.
|
|
70
|
+
Returns the sealed AgentAction.
|
|
71
|
+
"""
|
|
72
|
+
prev_hash = self.records[-1].record_hash if self.records else None
|
|
73
|
+
|
|
74
|
+
action = AgentAction(
|
|
75
|
+
agent_id=self.agent_id,
|
|
76
|
+
agent_version=agent_version,
|
|
77
|
+
action_type=action_type,
|
|
78
|
+
action_payload=action_payload,
|
|
79
|
+
outcome=outcome,
|
|
80
|
+
decision_confidence=decision_confidence,
|
|
81
|
+
authorized_by=authorized_by,
|
|
82
|
+
authorized_at=authorized_at,
|
|
83
|
+
operator_id=operator_id,
|
|
84
|
+
operator_name=operator_name,
|
|
85
|
+
institution_id=institution_id,
|
|
86
|
+
institution_name=institution_name,
|
|
87
|
+
delegation_present=delegation_present,
|
|
88
|
+
regulatory_context=regulatory_context or {
|
|
89
|
+
"framework": "DORA",
|
|
90
|
+
"specific_articles": ["Article 8(1)"],
|
|
91
|
+
"evidence_format": "DORA_ICT",
|
|
92
|
+
"policy_version": "",
|
|
93
|
+
},
|
|
94
|
+
reasoning=reasoning or [],
|
|
95
|
+
prev_hash=prev_hash,
|
|
96
|
+
input_hash=input_hash,
|
|
97
|
+
).seal()
|
|
98
|
+
|
|
99
|
+
self.records.append(action)
|
|
100
|
+
return action
|
|
101
|
+
|
|
102
|
+
def set_metadata(self, **kwargs) -> None:
|
|
103
|
+
"""Attach arbitrary session-level metadata (not part of the hash chain)."""
|
|
104
|
+
self._session_metadata.update(kwargs)
|
|
105
|
+
|
|
106
|
+
# ── Verification ──────────────────────────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
def verify_chain(self) -> dict:
|
|
109
|
+
"""
|
|
110
|
+
Verify the integrity of every record and every link in the chain.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
{
|
|
114
|
+
"valid": bool,
|
|
115
|
+
"total_records": int,
|
|
116
|
+
"broken_at": int | None, # index of first broken record
|
|
117
|
+
"reason": str | None,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
A regulator calls this. Green means the evidence is admissible.
|
|
121
|
+
Red means something was modified after signing.
|
|
122
|
+
"""
|
|
123
|
+
for i, record in enumerate(self.records):
|
|
124
|
+
# 1. Verify record self-integrity
|
|
125
|
+
if not record.verify():
|
|
126
|
+
return {
|
|
127
|
+
"valid": False,
|
|
128
|
+
"total_records": len(self.records),
|
|
129
|
+
"broken_at": i,
|
|
130
|
+
"reason": "record_hash_mismatch",
|
|
131
|
+
"action_id": record.action_id,
|
|
132
|
+
}
|
|
133
|
+
# 2. Verify chain link
|
|
134
|
+
if i > 0:
|
|
135
|
+
expected_prev = self.records[i - 1].record_hash
|
|
136
|
+
if record.prev_hash != expected_prev:
|
|
137
|
+
return {
|
|
138
|
+
"valid": False,
|
|
139
|
+
"total_records": len(self.records),
|
|
140
|
+
"broken_at": i,
|
|
141
|
+
"reason": "chain_link_broken",
|
|
142
|
+
"action_id": record.action_id,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
"valid": True,
|
|
147
|
+
"total_records": len(self.records),
|
|
148
|
+
"broken_at": None,
|
|
149
|
+
"reason": None,
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# ── Export ────────────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
def export_for_regulator(self) -> dict:
|
|
155
|
+
"""
|
|
156
|
+
Full examiner-ready export.
|
|
157
|
+
This is what you hand to a regulator, an auditor, or a law firm.
|
|
158
|
+
"""
|
|
159
|
+
return {
|
|
160
|
+
"schema_version": self.SCHEMA_VERSION,
|
|
161
|
+
"chain_id": self.chain_id,
|
|
162
|
+
"agent_id": self.agent_id,
|
|
163
|
+
"created_at": self.created_at,
|
|
164
|
+
"exported_at": datetime.now(timezone.utc).isoformat(),
|
|
165
|
+
"chain_integrity": self.verify_chain(),
|
|
166
|
+
"record_count": len(self.records),
|
|
167
|
+
"regulatory_coverage": self._coverage_summary(),
|
|
168
|
+
"session_metadata": self._session_metadata,
|
|
169
|
+
"records": [asdict(r) for r in self.records],
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
def export_json(self, indent: int = 2) -> str:
|
|
173
|
+
return json.dumps(self.export_for_regulator(), indent=indent, default=str)
|
|
174
|
+
|
|
175
|
+
def _coverage_summary(self) -> dict:
|
|
176
|
+
"""Count records grouped by regulatory framework."""
|
|
177
|
+
frameworks: dict = {}
|
|
178
|
+
for r in self.records:
|
|
179
|
+
fw = r.regulatory_context.get("framework", "UNKNOWN")
|
|
180
|
+
frameworks[fw] = frameworks.get(fw, 0) + 1
|
|
181
|
+
return frameworks
|
|
182
|
+
|
|
183
|
+
# ── Convenience ───────────────────────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
def __len__(self) -> int:
|
|
186
|
+
return len(self.records)
|
|
187
|
+
|
|
188
|
+
def __repr__(self) -> str:
|
|
189
|
+
integrity = self.verify_chain()
|
|
190
|
+
return (
|
|
191
|
+
f"EvidenceChain(chain_id={self.chain_id!r}, "
|
|
192
|
+
f"records={len(self.records)}, "
|
|
193
|
+
f"valid={integrity['valid']})"
|
|
194
|
+
)
|