xproof 0.2.0__tar.gz → 0.2.2__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 (28) hide show
  1. xproof-0.2.2/PKG-INFO +281 -0
  2. xproof-0.2.2/README.md +240 -0
  3. {xproof-0.2.0 → xproof-0.2.2}/pyproject.toml +1 -1
  4. {xproof-0.2.0 → xproof-0.2.2}/xproof/__init__.py +1 -1
  5. {xproof-0.2.0 → xproof-0.2.2}/xproof/client.py +97 -0
  6. xproof-0.2.2/xproof.egg-info/PKG-INFO +281 -0
  7. xproof-0.2.0/PKG-INFO +0 -235
  8. xproof-0.2.0/README.md +0 -194
  9. xproof-0.2.0/xproof.egg-info/PKG-INFO +0 -235
  10. {xproof-0.2.0 → xproof-0.2.2}/LICENSE +0 -0
  11. {xproof-0.2.0 → xproof-0.2.2}/setup.cfg +0 -0
  12. {xproof-0.2.0 → xproof-0.2.2}/tests/test_client.py +0 -0
  13. {xproof-0.2.0 → xproof-0.2.2}/tests/test_integration.py +0 -0
  14. {xproof-0.2.0 → xproof-0.2.2}/xproof/exceptions.py +0 -0
  15. {xproof-0.2.0 → xproof-0.2.2}/xproof/integrations/__init__.py +0 -0
  16. {xproof-0.2.0 → xproof-0.2.2}/xproof/integrations/autogen.py +0 -0
  17. {xproof-0.2.0 → xproof-0.2.2}/xproof/integrations/crewai.py +0 -0
  18. {xproof-0.2.0 → xproof-0.2.2}/xproof/integrations/deerflow.py +0 -0
  19. {xproof-0.2.0 → xproof-0.2.2}/xproof/integrations/langchain.py +0 -0
  20. {xproof-0.2.0 → xproof-0.2.2}/xproof/integrations/llamaindex.py +0 -0
  21. {xproof-0.2.0 → xproof-0.2.2}/xproof/integrations/openai_agents.py +0 -0
  22. {xproof-0.2.0 → xproof-0.2.2}/xproof/models.py +0 -0
  23. {xproof-0.2.0 → xproof-0.2.2}/xproof/py.typed +0 -0
  24. {xproof-0.2.0 → xproof-0.2.2}/xproof/utils.py +0 -0
  25. {xproof-0.2.0 → xproof-0.2.2}/xproof.egg-info/SOURCES.txt +0 -0
  26. {xproof-0.2.0 → xproof-0.2.2}/xproof.egg-info/dependency_links.txt +0 -0
  27. {xproof-0.2.0 → xproof-0.2.2}/xproof.egg-info/requires.txt +0 -0
  28. {xproof-0.2.0 → xproof-0.2.2}/xproof.egg-info/top_level.txt +0 -0
xproof-0.2.2/PKG-INFO ADDED
@@ -0,0 +1,281 @@
1
+ Metadata-Version: 2.4
2
+ Name: xproof
3
+ Version: 0.2.2
4
+ Summary: Python SDK for xProof — blockchain-anchored proof-of-existence for AI agents on MultiversX
5
+ Author-email: xProof <contact@xproof.app>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://xproof.app
8
+ Project-URL: Repository, https://github.com/jasonxkensei/xproof
9
+ Project-URL: Documentation, https://xproof.app/docs
10
+ Project-URL: Issues, https://github.com/jasonxkensei/xproof/issues
11
+ Project-URL: Changelog, https://github.com/jasonxkensei/xproof/releases
12
+ Keywords: blockchain,multiversx,proof-of-existence,certification,sha256,ai-agents,accountability,audit-trail,web3
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Security :: Cryptography
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.8
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: requests>=2.25.0
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=7.0; extra == "dev"
32
+ Requires-Dist: responses>=0.20.0; extra == "dev"
33
+ Provides-Extra: llamaindex
34
+ Requires-Dist: llama-index-core>=0.13.0; extra == "llamaindex"
35
+ Provides-Extra: autogen
36
+ Requires-Dist: pyautogen>=0.2.0; extra == "autogen"
37
+ Provides-Extra: openai-agents
38
+ Requires-Dist: openai-agents>=0.0.3; extra == "openai-agents"
39
+ Provides-Extra: deerflow
40
+ Dynamic: license-file
41
+
42
+ # xproof
43
+
44
+ On-chain decision provenance for autonomous agents. **WHY before acting. WHAT after.** Timestamps written by the chain, not your agent.
45
+
46
+ ```bash
47
+ pip install xproof
48
+ ```
49
+
50
+ ---
51
+
52
+ ## 3 steps. 30 seconds.
53
+
54
+ ### Step 1 — Register (no wallet, no payment)
55
+
56
+ ```bash
57
+ curl -X POST https://xproof.app/api/agent/register \
58
+ -H "Content-Type: application/json" \
59
+ -d '{"agent_name": "my-agent"}'
60
+ ```
61
+
62
+ ```json
63
+ { "api_key": "pm_...", "trial": { "remaining": 10 } }
64
+ ```
65
+
66
+ ### Step 2 — Anchor WHY before acting
67
+
68
+ Hash your reasoning and certify it *before* your agent executes.
69
+
70
+ ```bash
71
+ curl -X POST https://xproof.app/api/proof \
72
+ -H "Authorization: Bearer pm_..." \
73
+ -H "Content-Type: application/json" \
74
+ -d '{
75
+ "file_hash": "<sha256_of_reasoning>",
76
+ "file_name": "reasoning.json",
77
+ "author": "my-agent",
78
+ "metadata": { "action_type": "decision" }
79
+ }'
80
+ ```
81
+
82
+ ```json
83
+ { "id": "why-proof-uuid", "transaction_hash": "0x..." }
84
+ ```
85
+
86
+ ### Step 3 — Anchor WHAT after acting
87
+
88
+ Hash your output and link it to the WHY proof.
89
+
90
+ ```bash
91
+ curl -X POST https://xproof.app/api/proof \
92
+ -H "Authorization: Bearer pm_..." \
93
+ -H "Content-Type: application/json" \
94
+ -d '{
95
+ "file_hash": "<sha256_of_output>",
96
+ "file_name": "output.json",
97
+ "author": "my-agent",
98
+ "metadata": { "action_type": "execution", "why_proof_id": "why-proof-uuid" }
99
+ }'
100
+ ```
101
+
102
+ ```json
103
+ { "id": "what-proof-uuid", "transaction_hash": "0x..." }
104
+ ```
105
+
106
+ When something goes wrong, you don't guess. You verify.
107
+
108
+ ---
109
+
110
+ ## Python SDK
111
+
112
+ ```python
113
+ from xproof import XProofClient, hash_string
114
+
115
+ # Register — zero-friction, no wallet, no payment
116
+ client = XProofClient.register("my-agent")
117
+ # 10 free certs, API key stored automatically
118
+
119
+ # Step 2: Anchor WHY before acting
120
+ why = client.certify_hash(
121
+ file_hash=hash_string('{"action": "summarize", "model": "gpt-4"}'),
122
+ file_name="reasoning.json",
123
+ author="my-agent",
124
+ metadata={"action_type": "decision"},
125
+ )
126
+
127
+ # Step 3: Anchor WHAT after acting
128
+ what = client.certify_hash(
129
+ file_hash=hash_string(execution_output),
130
+ file_name="output.json",
131
+ author="my-agent",
132
+ metadata={"action_type": "execution", "why_proof_id": why.id},
133
+ )
134
+
135
+ print(what.transaction_url) # MultiversX explorer link
136
+ ```
137
+
138
+ Or use an existing API key:
139
+
140
+ ```python
141
+ client = XProofClient(api_key="pm_your_api_key")
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Framework Integrations
147
+
148
+ ### LangChain
149
+
150
+ ```python
151
+ from xproof.integrations.langchain import XProofCallbackHandler
152
+
153
+ handler = XProofCallbackHandler(api_key="pm_...")
154
+ llm = ChatOpenAI(callbacks=[handler])
155
+ ```
156
+
157
+ ### CrewAI
158
+
159
+ ```python
160
+ from xproof.integrations.crewai import XProofListener
161
+
162
+ listener = XProofListener(api_key="pm_...")
163
+ ```
164
+
165
+ ### AutoGen
166
+
167
+ ```python
168
+ from xproof.integrations.autogen import XProofHook
169
+
170
+ hook = XProofHook(api_key="pm_...")
171
+ agent.register_hook("process_last_received_message", hook.on_message)
172
+ ```
173
+
174
+ ### LlamaIndex
175
+
176
+ ```python
177
+ from xproof.integrations.llamaindex import XProofCallbackHandler
178
+ from llama_index.core import Settings
179
+ from llama_index.core.callbacks import CallbackManager
180
+
181
+ Settings.callback_manager = CallbackManager([XProofCallbackHandler(api_key="pm_...")])
182
+ ```
183
+
184
+ ---
185
+
186
+ ## 4W Framework (WHO / WHAT / WHEN / WHY)
187
+
188
+ Full accountability metadata on every certification:
189
+
190
+ ```python
191
+ from xproof import XProofClient, hash_bytes
192
+
193
+ client = XProofClient(api_key="pm_your_key")
194
+
195
+ action_data = b'{"action": "generate_report", "model": "gpt-4"}'
196
+ action_hash = hash_bytes(action_data)
197
+
198
+ cert = client.certify_hash(
199
+ file_hash=action_hash,
200
+ file_name="agent-action.json",
201
+ author="research-agent",
202
+ who="erd1abc...or-agent-id",
203
+ what=action_hash,
204
+ when="2026-03-20T12:00:00Z",
205
+ why=hash_bytes(b"Summarize Q1 earnings report"),
206
+ metadata={"model": "gpt-4", "session_id": "sess-123"},
207
+ )
208
+ ```
209
+
210
+ ## Batch Certification
211
+
212
+ Certify up to 50 files in a single API call:
213
+
214
+ ```python
215
+ results = client.batch_certify([
216
+ {"file_hash": "abc123...", "file_name": "file1.pdf", "author": "my-agent"},
217
+ {"file_hash": "def456...", "file_name": "file2.pdf"},
218
+ ])
219
+
220
+ print(results.summary.total) # 2
221
+ print(results.summary.created) # 2
222
+ ```
223
+
224
+ ## Certify a Local File
225
+
226
+ ```python
227
+ # Auto-hashes with SHA-256
228
+ cert = client.certify("path/to/report.pdf", author="my-agent")
229
+ print(cert.id)
230
+ print(cert.transaction_url)
231
+ ```
232
+
233
+ ## Verify a Proof
234
+
235
+ ```python
236
+ # By proof ID
237
+ proof = client.verify("certification-uuid")
238
+ print(proof.file_name, proof.blockchain_status)
239
+
240
+ # By file hash
241
+ proof = client.verify_hash("e3b0c442...")
242
+ ```
243
+
244
+ ## Pricing
245
+
246
+ ```python
247
+ pricing = client.get_pricing()
248
+ print(pricing.price_usd) # e.g. 0.05
249
+ ```
250
+
251
+ ## API Reference
252
+
253
+ ### `XProofClient(api_key=None, base_url="https://xproof.app", timeout=30)`
254
+
255
+ | Parameter | Type | Default |
256
+ |------------|-------|------------------------|
257
+ | `api_key` | `str` | `None` |
258
+ | `base_url` | `str` | `"https://xproof.app"` |
259
+ | `timeout` | `int` | `30` (seconds) |
260
+
261
+ ### Methods
262
+
263
+ | Method | Description |
264
+ |--------|-------------|
265
+ | `XProofClient.register(agent_name)` | Register agent, get trial key |
266
+ | `certify(path, author, file_name?, **fourW)` | Certify file (hashes locally) |
267
+ | `certify_hash(file_hash, file_name, author, **fourW)` | Certify by pre-computed hash |
268
+ | `batch_certify(files)` | Batch certify (up to 50) |
269
+ | `verify(proof_id)` | Look up by proof ID |
270
+ | `verify_hash(file_hash)` | Look up by file hash |
271
+ | `get_pricing()` | Get current pricing |
272
+
273
+ ## Links
274
+
275
+ - [xproof.app](https://xproof.app) — dashboard & docs
276
+ - [npm SDK](https://www.npmjs.com/package/@xproof/xproof) — `npm install @xproof/xproof`
277
+ - [Examples](https://github.com/jasonxkensei/xproof-examples) — LangChain, CrewAI, AutoGen, LlamaIndex
278
+
279
+ ## License
280
+
281
+ MIT
xproof-0.2.2/README.md ADDED
@@ -0,0 +1,240 @@
1
+ # xproof
2
+
3
+ On-chain decision provenance for autonomous agents. **WHY before acting. WHAT after.** Timestamps written by the chain, not your agent.
4
+
5
+ ```bash
6
+ pip install xproof
7
+ ```
8
+
9
+ ---
10
+
11
+ ## 3 steps. 30 seconds.
12
+
13
+ ### Step 1 — Register (no wallet, no payment)
14
+
15
+ ```bash
16
+ curl -X POST https://xproof.app/api/agent/register \
17
+ -H "Content-Type: application/json" \
18
+ -d '{"agent_name": "my-agent"}'
19
+ ```
20
+
21
+ ```json
22
+ { "api_key": "pm_...", "trial": { "remaining": 10 } }
23
+ ```
24
+
25
+ ### Step 2 — Anchor WHY before acting
26
+
27
+ Hash your reasoning and certify it *before* your agent executes.
28
+
29
+ ```bash
30
+ curl -X POST https://xproof.app/api/proof \
31
+ -H "Authorization: Bearer pm_..." \
32
+ -H "Content-Type: application/json" \
33
+ -d '{
34
+ "file_hash": "<sha256_of_reasoning>",
35
+ "file_name": "reasoning.json",
36
+ "author": "my-agent",
37
+ "metadata": { "action_type": "decision" }
38
+ }'
39
+ ```
40
+
41
+ ```json
42
+ { "id": "why-proof-uuid", "transaction_hash": "0x..." }
43
+ ```
44
+
45
+ ### Step 3 — Anchor WHAT after acting
46
+
47
+ Hash your output and link it to the WHY proof.
48
+
49
+ ```bash
50
+ curl -X POST https://xproof.app/api/proof \
51
+ -H "Authorization: Bearer pm_..." \
52
+ -H "Content-Type: application/json" \
53
+ -d '{
54
+ "file_hash": "<sha256_of_output>",
55
+ "file_name": "output.json",
56
+ "author": "my-agent",
57
+ "metadata": { "action_type": "execution", "why_proof_id": "why-proof-uuid" }
58
+ }'
59
+ ```
60
+
61
+ ```json
62
+ { "id": "what-proof-uuid", "transaction_hash": "0x..." }
63
+ ```
64
+
65
+ When something goes wrong, you don't guess. You verify.
66
+
67
+ ---
68
+
69
+ ## Python SDK
70
+
71
+ ```python
72
+ from xproof import XProofClient, hash_string
73
+
74
+ # Register — zero-friction, no wallet, no payment
75
+ client = XProofClient.register("my-agent")
76
+ # 10 free certs, API key stored automatically
77
+
78
+ # Step 2: Anchor WHY before acting
79
+ why = client.certify_hash(
80
+ file_hash=hash_string('{"action": "summarize", "model": "gpt-4"}'),
81
+ file_name="reasoning.json",
82
+ author="my-agent",
83
+ metadata={"action_type": "decision"},
84
+ )
85
+
86
+ # Step 3: Anchor WHAT after acting
87
+ what = client.certify_hash(
88
+ file_hash=hash_string(execution_output),
89
+ file_name="output.json",
90
+ author="my-agent",
91
+ metadata={"action_type": "execution", "why_proof_id": why.id},
92
+ )
93
+
94
+ print(what.transaction_url) # MultiversX explorer link
95
+ ```
96
+
97
+ Or use an existing API key:
98
+
99
+ ```python
100
+ client = XProofClient(api_key="pm_your_api_key")
101
+ ```
102
+
103
+ ---
104
+
105
+ ## Framework Integrations
106
+
107
+ ### LangChain
108
+
109
+ ```python
110
+ from xproof.integrations.langchain import XProofCallbackHandler
111
+
112
+ handler = XProofCallbackHandler(api_key="pm_...")
113
+ llm = ChatOpenAI(callbacks=[handler])
114
+ ```
115
+
116
+ ### CrewAI
117
+
118
+ ```python
119
+ from xproof.integrations.crewai import XProofListener
120
+
121
+ listener = XProofListener(api_key="pm_...")
122
+ ```
123
+
124
+ ### AutoGen
125
+
126
+ ```python
127
+ from xproof.integrations.autogen import XProofHook
128
+
129
+ hook = XProofHook(api_key="pm_...")
130
+ agent.register_hook("process_last_received_message", hook.on_message)
131
+ ```
132
+
133
+ ### LlamaIndex
134
+
135
+ ```python
136
+ from xproof.integrations.llamaindex import XProofCallbackHandler
137
+ from llama_index.core import Settings
138
+ from llama_index.core.callbacks import CallbackManager
139
+
140
+ Settings.callback_manager = CallbackManager([XProofCallbackHandler(api_key="pm_...")])
141
+ ```
142
+
143
+ ---
144
+
145
+ ## 4W Framework (WHO / WHAT / WHEN / WHY)
146
+
147
+ Full accountability metadata on every certification:
148
+
149
+ ```python
150
+ from xproof import XProofClient, hash_bytes
151
+
152
+ client = XProofClient(api_key="pm_your_key")
153
+
154
+ action_data = b'{"action": "generate_report", "model": "gpt-4"}'
155
+ action_hash = hash_bytes(action_data)
156
+
157
+ cert = client.certify_hash(
158
+ file_hash=action_hash,
159
+ file_name="agent-action.json",
160
+ author="research-agent",
161
+ who="erd1abc...or-agent-id",
162
+ what=action_hash,
163
+ when="2026-03-20T12:00:00Z",
164
+ why=hash_bytes(b"Summarize Q1 earnings report"),
165
+ metadata={"model": "gpt-4", "session_id": "sess-123"},
166
+ )
167
+ ```
168
+
169
+ ## Batch Certification
170
+
171
+ Certify up to 50 files in a single API call:
172
+
173
+ ```python
174
+ results = client.batch_certify([
175
+ {"file_hash": "abc123...", "file_name": "file1.pdf", "author": "my-agent"},
176
+ {"file_hash": "def456...", "file_name": "file2.pdf"},
177
+ ])
178
+
179
+ print(results.summary.total) # 2
180
+ print(results.summary.created) # 2
181
+ ```
182
+
183
+ ## Certify a Local File
184
+
185
+ ```python
186
+ # Auto-hashes with SHA-256
187
+ cert = client.certify("path/to/report.pdf", author="my-agent")
188
+ print(cert.id)
189
+ print(cert.transaction_url)
190
+ ```
191
+
192
+ ## Verify a Proof
193
+
194
+ ```python
195
+ # By proof ID
196
+ proof = client.verify("certification-uuid")
197
+ print(proof.file_name, proof.blockchain_status)
198
+
199
+ # By file hash
200
+ proof = client.verify_hash("e3b0c442...")
201
+ ```
202
+
203
+ ## Pricing
204
+
205
+ ```python
206
+ pricing = client.get_pricing()
207
+ print(pricing.price_usd) # e.g. 0.05
208
+ ```
209
+
210
+ ## API Reference
211
+
212
+ ### `XProofClient(api_key=None, base_url="https://xproof.app", timeout=30)`
213
+
214
+ | Parameter | Type | Default |
215
+ |------------|-------|------------------------|
216
+ | `api_key` | `str` | `None` |
217
+ | `base_url` | `str` | `"https://xproof.app"` |
218
+ | `timeout` | `int` | `30` (seconds) |
219
+
220
+ ### Methods
221
+
222
+ | Method | Description |
223
+ |--------|-------------|
224
+ | `XProofClient.register(agent_name)` | Register agent, get trial key |
225
+ | `certify(path, author, file_name?, **fourW)` | Certify file (hashes locally) |
226
+ | `certify_hash(file_hash, file_name, author, **fourW)` | Certify by pre-computed hash |
227
+ | `batch_certify(files)` | Batch certify (up to 50) |
228
+ | `verify(proof_id)` | Look up by proof ID |
229
+ | `verify_hash(file_hash)` | Look up by file hash |
230
+ | `get_pricing()` | Get current pricing |
231
+
232
+ ## Links
233
+
234
+ - [xproof.app](https://xproof.app) — dashboard & docs
235
+ - [npm SDK](https://www.npmjs.com/package/@xproof/xproof) — `npm install @xproof/xproof`
236
+ - [Examples](https://github.com/jasonxkensei/xproof-examples) — LangChain, CrewAI, AutoGen, LlamaIndex
237
+
238
+ ## License
239
+
240
+ MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "xproof"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "Python SDK for xProof — blockchain-anchored proof-of-existence for AI agents on MultiversX"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -21,7 +21,7 @@ from .models import (
21
21
  )
22
22
  from .utils import hash_bytes, hash_file
23
23
 
24
- __version__ = "0.2.0"
24
+ __version__ = "0.2.2"
25
25
 
26
26
  __all__ = [
27
27
  "XProofClient",
@@ -281,6 +281,103 @@ class XProofClient:
281
281
  data = self._request("POST", "/api/proof", json=payload)
282
282
  return Certification.from_dict(data)
283
283
 
284
+ VALID_THRESHOLD_STAGES = ("initial", "partial", "pre-commitment", "final")
285
+
286
+ def certify_with_confidence(
287
+ self,
288
+ file_hash: str,
289
+ file_name: str,
290
+ author: str,
291
+ confidence_level: float,
292
+ threshold_stage: str,
293
+ decision_id: str,
294
+ *,
295
+ who: Optional[str] = None,
296
+ what: Optional[str] = None,
297
+ when: Optional[str] = None,
298
+ why: Optional[str] = None,
299
+ metadata: Optional[Dict[str, Any]] = None,
300
+ ) -> Certification:
301
+ """Certify a file hash with confidence-level anchoring.
302
+
303
+ Creates a forensic trail by anchoring proofs at different confidence
304
+ thresholds, linked by a shared ``decision_id``. An agent trading at
305
+ 60%, 80%, then final creates an immutable trail that distinguishes
306
+ real reasoning from post-hoc reconstruction.
307
+
308
+ Args:
309
+ file_hash: 64-character lowercase hex SHA-256 digest.
310
+ file_name: Original file name.
311
+ author: Author / owner name.
312
+ confidence_level: Confidence between 0.0 and 1.0.
313
+ threshold_stage: One of ``initial``, ``partial``,
314
+ ``pre-commitment``, ``final``.
315
+ decision_id: Shared identifier linking all proofs in the
316
+ same decision chain.
317
+ who: 4W -- agent identity.
318
+ what: 4W -- action hash or description.
319
+ when: 4W -- ISO-8601 timestamp.
320
+ why: 4W -- instruction or reason.
321
+ metadata: Extra key-value metadata stored alongside the proof.
322
+
323
+ Returns:
324
+ A :class:`Certification` with the on-chain proof details.
325
+
326
+ Raises:
327
+ ValueError: If confidence_level or threshold_stage is invalid.
328
+ """
329
+ if not self.api_key:
330
+ raise ValueError("api_key is required — call register() or pass an api_key")
331
+ if not 0.0 <= confidence_level <= 1.0:
332
+ raise ValueError("confidence_level must be between 0.0 and 1.0")
333
+ if threshold_stage not in self.VALID_THRESHOLD_STAGES:
334
+ raise ValueError(
335
+ f"threshold_stage must be one of: {', '.join(self.VALID_THRESHOLD_STAGES)}"
336
+ )
337
+ if not decision_id or not decision_id.strip():
338
+ raise ValueError("decision_id is required")
339
+
340
+ proof_metadata: Dict[str, Any] = dict(metadata) if metadata else {}
341
+ proof_metadata["confidence_level"] = confidence_level
342
+ proof_metadata["threshold_stage"] = threshold_stage
343
+ proof_metadata["decision_id"] = decision_id
344
+ if who is not None:
345
+ proof_metadata["who"] = who
346
+ if what is not None:
347
+ proof_metadata["what"] = what
348
+ if when is not None:
349
+ proof_metadata["when"] = when
350
+ if why is not None:
351
+ proof_metadata["why"] = why
352
+
353
+ payload: Dict[str, Any] = {
354
+ "filename": file_name,
355
+ "file_hash": file_hash,
356
+ "author_name": author,
357
+ "metadata": proof_metadata,
358
+ }
359
+ data = self._request("POST", "/api/proof", json=payload)
360
+ return Certification.from_dict(data)
361
+
362
+ def get_confidence_trail(self, decision_id: str) -> Dict[str, Any]:
363
+ """Retrieve the full confidence trail for a decision chain.
364
+
365
+ Args:
366
+ decision_id: The shared identifier linking proofs in the chain.
367
+
368
+ Returns:
369
+ A dictionary with ``decision_id``, ``total_anchors``,
370
+ ``current_confidence``, ``current_stage``, ``is_finalized``,
371
+ and ``stages`` (list of anchor details sorted by confidence).
372
+ """
373
+ from urllib.parse import quote
374
+ data = self._request(
375
+ "GET",
376
+ f"/api/confidence-trail/{quote(decision_id, safe='')}",
377
+ auth_required=False,
378
+ )
379
+ return data
380
+
284
381
  def batch_certify(
285
382
  self,
286
383
  files: List[Dict[str, Any]],