ct-toolkit 0.3.0__tar.gz → 0.3.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 (51) hide show
  1. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/PKG-INFO +11 -6
  2. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/README.md +10 -5
  3. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/core/wrapper.py +66 -29
  4. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/divergence/engine.py +18 -3
  5. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/endorsement/reflective.py +1 -0
  6. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/provenance/log.py +100 -16
  7. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit.egg-info/PKG-INFO +11 -6
  8. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/pyproject.toml +1 -1
  9. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/LICENSE +0 -0
  10. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/__init__.py +0 -0
  11. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/core/__init__.py +0 -0
  12. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/core/compatibility.py +0 -0
  13. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/core/exceptions.py +0 -0
  14. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/core/integrity.py +0 -0
  15. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/core/kernel.py +0 -0
  16. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/divergence/__init__.py +0 -0
  17. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/divergence/analysis.py +0 -0
  18. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/divergence/l2_judge.py +0 -0
  19. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/divergence/l3_icm.py +0 -0
  20. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/divergence/loss.py +0 -0
  21. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/divergence/scheduler.py +0 -0
  22. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/endorsement/__init__.py +0 -0
  23. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/endorsement/probes/__init__.py +0 -0
  24. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/identity/__init__.py +0 -0
  25. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/identity/embedding.py +0 -0
  26. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/identity/templates/__init__.py +0 -0
  27. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/identity/templates/defense.yaml +0 -0
  28. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/identity/templates/finance.yaml +0 -0
  29. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/identity/templates/general.yaml +0 -0
  30. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/identity/templates/medical.yaml +0 -0
  31. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/kernels/__init__.py +0 -0
  32. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/kernels/default.yaml +0 -0
  33. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/kernels/defense.yaml +0 -0
  34. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/middleware/__init__.py +0 -0
  35. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/middleware/autogen.py +0 -0
  36. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/middleware/crewai.py +0 -0
  37. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/middleware/deepagents.py +0 -0
  38. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/middleware/langchain.py +0 -0
  39. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/provenance/__init__.py +0 -0
  40. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/provenance/vault/__init__.py +0 -0
  41. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/utils/__init__.py +0 -0
  42. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit/utils/logger.py +0 -0
  43. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit.egg-info/SOURCES.txt +0 -0
  44. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit.egg-info/dependency_links.txt +0 -0
  45. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit.egg-info/requires.txt +0 -0
  46. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/ct_toolkit.egg-info/top_level.txt +0 -0
  47. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/setup.cfg +0 -0
  48. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/tests/test_analysis.py +0 -0
  49. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/tests/test_deep_agents_integration.py +0 -0
  50. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/tests/test_loss.py +0 -0
  51. {ct_toolkit-0.3.0 → ct_toolkit-0.3.2}/tests/test_summarization_drift.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ct-toolkit
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems
5
5
  Author: Hakan Damar
6
6
  License: Apache-2.0
@@ -51,6 +51,7 @@ CT Toolkit is an open-source security layer designed to preserve the **identity
51
51
  ---
52
52
 
53
53
  ## 📖 Official Documentation
54
+
54
55
  For full API reference, architecture details, examples, and integration guides, visit our documentation site:
55
56
  👉 [**https://hakandamar.github.io/ct-toolkit/**](https://hakandamar.github.io/ct-toolkit/)
56
57
 
@@ -63,6 +64,7 @@ For full API reference, architecture details, examples, and integration guides,
63
64
  In complex agentic workflows, LLMs tend to "drift" from their original instructions. CT Toolkit provides the mathematical and cryptographic guardrails to ensure your agents remain aligned with their core constitution, even across deep hierarchies.
64
65
 
65
66
  - **Constitutional Kernels**: Axiomatic identity anchors.
67
+ - **Autonomous Self-Correction**: Active L2->L1 feedback loop that retries and corrects divergent responses before they reach the user.
66
68
  - **Divergence Engine**: Multi-tiered drift analysis (L1/L2/L3).
67
69
  - **Hierarchical Propagation**: Mother-to-child constraint inheritance.
68
70
  - **Provenance Log**: Immutable HMAC-signed interaction history.
@@ -92,15 +94,16 @@ print(f"Divergence Score: {response.divergence_score}")
92
94
 
93
95
  ## 🚦 Project Health & Status
94
96
 
95
- | Metric | Status |
96
- | :--- | :--- |
97
- | **Tests** | ✅ 218/218 passing (93% coverage) |
98
- | **Last Phase** | ✅ Phase 4: OS Support, Deep Agents & Alerts (Complete) |
99
- | **Current Goal** | 🔶 Phase 5: Vault and Security Infrastructure |
97
+ | Metric | Status |
98
+ | :--------------- | :------------------------------------------------------ |
99
+ | **Tests** | ✅ 231/232 passing (90% coverage) |
100
+ | **Last Phase** | ✅ Phase 4: OS Support, Deep Agents & Alerts (Complete) |
101
+ | **Current Goal** | 🔶 Phase 5: Vault & Security (In Progress) |
100
102
 
101
103
  For a detailed breakdown of the 8-phase roadmap, see [**PROJECT_STATUS.md**](docs/PROJECT_STATUS.md).
102
104
 
103
105
  ### Framework & Model Support
106
+
104
107
  Seamlessly integrate with your favorite frameworks and local models:
105
108
 
106
109
  - **Local Models**: Support for LM Studio, Ollama, and local Qwen/Llama endpoints.
@@ -111,9 +114,11 @@ Seamlessly integrate with your favorite frameworks and local models:
111
114
  ---
112
115
 
113
116
  ## Theoretical Foundation
117
+
114
118
  Translating the framework proposed in [**The Computational Theseus (2025)**](https://hakandamar.com/the-computational-theseus-engineering-identity-continuity-as-a-guardrail-against-sequential-963918c1720d) into engineering practice.
115
119
 
116
120
  ---
117
121
 
118
122
  ## License
123
+
119
124
  Apache License 2.0.
@@ -12,6 +12,7 @@ CT Toolkit is an open-source security layer designed to preserve the **identity
12
12
  ---
13
13
 
14
14
  ## 📖 Official Documentation
15
+
15
16
  For full API reference, architecture details, examples, and integration guides, visit our documentation site:
16
17
  👉 [**https://hakandamar.github.io/ct-toolkit/**](https://hakandamar.github.io/ct-toolkit/)
17
18
 
@@ -24,6 +25,7 @@ For full API reference, architecture details, examples, and integration guides,
24
25
  In complex agentic workflows, LLMs tend to "drift" from their original instructions. CT Toolkit provides the mathematical and cryptographic guardrails to ensure your agents remain aligned with their core constitution, even across deep hierarchies.
25
26
 
26
27
  - **Constitutional Kernels**: Axiomatic identity anchors.
28
+ - **Autonomous Self-Correction**: Active L2->L1 feedback loop that retries and corrects divergent responses before they reach the user.
27
29
  - **Divergence Engine**: Multi-tiered drift analysis (L1/L2/L3).
28
30
  - **Hierarchical Propagation**: Mother-to-child constraint inheritance.
29
31
  - **Provenance Log**: Immutable HMAC-signed interaction history.
@@ -53,15 +55,16 @@ print(f"Divergence Score: {response.divergence_score}")
53
55
 
54
56
  ## 🚦 Project Health & Status
55
57
 
56
- | Metric | Status |
57
- | :--- | :--- |
58
- | **Tests** | ✅ 218/218 passing (93% coverage) |
59
- | **Last Phase** | ✅ Phase 4: OS Support, Deep Agents & Alerts (Complete) |
60
- | **Current Goal** | 🔶 Phase 5: Vault and Security Infrastructure |
58
+ | Metric | Status |
59
+ | :--------------- | :------------------------------------------------------ |
60
+ | **Tests** | ✅ 231/232 passing (90% coverage) |
61
+ | **Last Phase** | ✅ Phase 4: OS Support, Deep Agents & Alerts (Complete) |
62
+ | **Current Goal** | 🔶 Phase 5: Vault & Security (In Progress) |
61
63
 
62
64
  For a detailed breakdown of the 8-phase roadmap, see [**PROJECT_STATUS.md**](docs/PROJECT_STATUS.md).
63
65
 
64
66
  ### Framework & Model Support
67
+
65
68
  Seamlessly integrate with your favorite frameworks and local models:
66
69
 
67
70
  - **Local Models**: Support for LM Studio, Ollama, and local Qwen/Llama endpoints.
@@ -72,9 +75,11 @@ Seamlessly integrate with your favorite frameworks and local models:
72
75
  ---
73
76
 
74
77
  ## Theoretical Foundation
78
+
75
79
  Translating the framework proposed in [**The Computational Theseus (2025)**](https://hakandamar.com/the-computational-theseus-engineering-identity-continuity-as-a-guardrail-against-sequential-963918c1720d) into engineering practice.
76
80
 
77
81
  ---
78
82
 
79
83
  ## License
84
+
80
85
  Apache License 2.0.
@@ -59,6 +59,10 @@ class WrapperConfig:
59
59
  embedding_model: str = "text-embedding-3-small"
60
60
  enterprise_mode: bool = False # Run all tiers all the time
61
61
  parent_kernel: ConstitutionalKernel | None = None # Propagated from mother agent
62
+
63
+ # -- Auto-Correction Loop --
64
+ auto_correction: bool = False
65
+ max_correction_retries: int = 1
62
66
 
63
67
  # -- Dynamic Stability-Plasticity Scheduling --
64
68
  elasticity_max_thresholds: tuple[float, float, float] | None = None # (max_l1, max_l2, max_l3)
@@ -385,34 +389,52 @@ class TheseusWrapper:
385
389
  f"Please provide a client or set the appropriate environment variables."
386
390
  )
387
391
 
388
- start_time = time.monotonic()
389
-
390
- try:
391
- raw_response = self._call_provider(messages, model=model, **kwargs)
392
- except Exception as e:
393
- logger.error(f"Provider API error: {e}")
394
- raise
395
-
396
- elapsed = time.monotonic() - start_time
397
- content = self._extract_content(raw_response)
398
- model_used = self._extract_model(raw_response, model)
399
- self._last_model = model_used
400
-
401
- # Compute current interaction experience
402
- interaction_count = 0
403
- if self._config.log_requests:
404
- interaction_count = self._provenance_log.get_interaction_count(
405
- template=self._config.template,
406
- kernel_name=self._kernel.name,
407
- model=model_used
392
+ retries = 0
393
+ max_retries = self._config.max_correction_retries if self._config.auto_correction else 0
394
+
395
+ while retries <= max_retries:
396
+ start_time = time.monotonic()
397
+
398
+ try:
399
+ raw_response = self._call_provider(messages, model=model, **kwargs)
400
+ except Exception as e:
401
+ logger.error(f"Provider API error: {e}")
402
+ raise
403
+
404
+ elapsed = time.monotonic() - start_time
405
+ content = self._extract_content(raw_response)
406
+ model_used = self._extract_model(raw_response, model)
407
+ self._last_model = model_used
408
+
409
+ # Compute current interaction experience
410
+ interaction_count = 0
411
+ if self._config.log_requests:
412
+ interaction_count = self._provenance_log.get_interaction_count(
413
+ template=self._config.template,
414
+ kernel_name=self._kernel.name,
415
+ model=model_used
416
+ )
417
+
418
+ # Divergence Engine (L1 -> L2 -> L3)
419
+ skip_l3 = self._config.auto_correction and retries < max_retries
420
+ div_result = self._run_divergence_engine(
421
+ message=message,
422
+ response=content,
423
+ interaction_count=interaction_count,
424
+ skip_l3=skip_l3,
408
425
  )
409
-
410
- # Divergence Engine (L1 -> L2 -> L3)
411
- div_result = self._run_divergence_engine(
412
- message=message,
413
- response=content,
414
- interaction_count=interaction_count,
415
- )
426
+
427
+ from ct_toolkit.divergence.l2_judge import JudgeVerdict
428
+ if getattr(div_result, 'l2_verdict', None) == JudgeVerdict.MISALIGNED and retries < max_retries:
429
+ logger.warning(f"Auto-correction triggered. Reason: {div_result.l2_reason}")
430
+ messages.extend([
431
+ {"role": "assistant", "content": content},
432
+ {"role": "user", "content": f"Identity Drift Detected. Your previous response violated the core identity instructions. Reason: {div_result.l2_reason}. Please revise your response to strictly adhere to the system prompt."}
433
+ ])
434
+ retries += 1
435
+ continue
436
+
437
+ break
416
438
 
417
439
  # Provenance Log record
418
440
  provenance_id = None
@@ -582,10 +604,10 @@ class TheseusWrapper:
582
604
  return raw.model
583
605
  return fallback or "unknown"
584
606
 
585
- def _run_divergence_engine(self, message: str, response: str, interaction_count: int = 0) -> Any:
607
+ def _run_divergence_engine(self, message: str, response: str, interaction_count: int = 0, skip_l3: bool = False) -> Any:
586
608
  """Runs the staged divergence engine (L1 -> L2 -> L3)."""
587
609
  try:
588
- return self._divergence_engine.analyze(message, response, interaction_count)
610
+ return self._divergence_engine.analyze(message, response, interaction_count, skip_l3=skip_l3)
589
611
  except Exception as e:
590
612
  logger.error(f"Divergence Engine execution failed: {e}")
591
613
  from ct_toolkit.divergence.engine import DivergenceResult, DivergenceTier
@@ -635,6 +657,21 @@ class TheseusWrapper:
635
657
  commitment_new_value=commitment_new_value,
636
658
  )
637
659
 
660
+ def export_provenance_log(self) -> list[dict[str, Any]]:
661
+ """
662
+ Exports the entire verified provenance log.
663
+
664
+ Verifies the integrity of the log before returning the data.
665
+ This is intended for external auditing and analysis.
666
+
667
+ Returns:
668
+ A list of all log entries as dictionaries.
669
+
670
+ Raises:
671
+ ChainIntegrityError: If the log's hash chain is broken.
672
+ """
673
+ return self._provenance_log.export_log()
674
+
638
675
  @property
639
676
  def kernel(self) -> ConstitutionalKernel:
640
677
  return self._kernel
@@ -154,6 +154,7 @@ class DivergenceEngine:
154
154
  request_text: str,
155
155
  response_text: str,
156
156
  interaction_count: int = 0,
157
+ skip_l3: bool = False,
157
158
  ) -> DivergenceResult:
158
159
  """
159
160
  Performs full divergence analysis.
@@ -166,8 +167,8 @@ class DivergenceEngine:
166
167
  )
167
168
 
168
169
  if self._enterprise:
169
- return self._enterprise_analyze(request_text, response_text)
170
- return self._standard_analyze(request_text, response_text)
170
+ return self._enterprise_analyze(request_text, response_text, skip_l3=skip_l3)
171
+ return self._standard_analyze(request_text, response_text, skip_l3=skip_l3)
171
172
 
172
173
  def get_drift_report(self, window_size: int = 50, model: str | None = None) -> DriftReport | None:
173
174
  """
@@ -189,6 +190,7 @@ class DivergenceEngine:
189
190
  self,
190
191
  request_text: str,
191
192
  response_text: str,
193
+ skip_l3: bool = False,
192
194
  ) -> DivergenceResult:
193
195
  """Progressive analysis: L1 → L2 if needed → L3 if needed."""
194
196
 
@@ -236,6 +238,18 @@ class DivergenceEngine:
236
238
  )
237
239
 
238
240
  # L3 — L2 problematic or L1 too high
241
+ if skip_l3:
242
+ return DivergenceResult(
243
+ tier=DivergenceTier.L2_JUDGE,
244
+ l1_score=l1_score,
245
+ l2_verdict=l2_result.verdict,
246
+ l2_confidence=l2_result.confidence,
247
+ l2_reason=l2_result.reason,
248
+ action_required=True,
249
+ cascade_blocked=True,
250
+ summary="L3 skipped due to auto-correction retry logic",
251
+ )
252
+
239
253
  if not self._l3_available:
240
254
  return DivergenceResult(
241
255
  tier=DivergenceTier.L2_JUDGE,
@@ -269,6 +283,7 @@ class DivergenceEngine:
269
283
  self,
270
284
  request_text: str,
271
285
  response_text: str,
286
+ skip_l3: bool = False,
272
287
  ) -> DivergenceResult:
273
288
  """Enterprise mode: L1 + L2 + L3 all run, decision based on total score."""
274
289
  l1_score = self._run_l1(response_text)
@@ -278,7 +293,7 @@ class DivergenceEngine:
278
293
  l2_result = self._run_l2(request_text, response_text)
279
294
 
280
295
  l3_report = None
281
- if self._l3_available:
296
+ if self._l3_available and not skip_l3:
282
297
  l3_report = self._run_l3(max_probes=5) # Fast subset in Enterprise
283
298
 
284
299
  # Calculate total risk score
@@ -210,6 +210,7 @@ class ReflectiveEndorsement:
210
210
  self._log = provenance_log
211
211
  self._approval_channel = approval_channel or cli_approval_channel
212
212
  self._pending_records: list[EndorsementRecord] = []
213
+ self._pre_update_snapshot: dict[str, Any] | None = None
213
214
 
214
215
  # ── Main Flow ───────────────────────────────────────────────────────────────
215
216
 
@@ -43,6 +43,7 @@ class ProvenanceEntry:
43
43
  divergence_score: float | None
44
44
  metadata: dict[str, Any]
45
45
  prev_entry_hash: str # Content hash of the previous entry
46
+ status: str = field(default="active") # "active" or "rolled_back"
46
47
  hmac_signature: str = field(default="", repr=False)
47
48
 
48
49
  def content_hash(self) -> str:
@@ -56,6 +57,7 @@ class ProvenanceEntry:
56
57
  "divergence_score": self.divergence_score,
57
58
  "metadata": self.metadata,
58
59
  "prev_entry_hash": self.prev_entry_hash,
60
+ "status": self.status,
59
61
  },
60
62
  sort_keys=True,
61
63
  )
@@ -111,6 +113,7 @@ class ProvenanceLog:
111
113
  divergence_score=divergence_score,
112
114
  metadata=metadata or {},
113
115
  prev_entry_hash=prev_hash,
116
+ status="active",
114
117
  )
115
118
 
116
119
  content_hash = entry.content_hash()
@@ -124,13 +127,14 @@ class ProvenanceLog:
124
127
 
125
128
  def verify_chain(self) -> bool:
126
129
  """
127
- Verifies the entire chain from start to finish.
130
+ Verifies the active chain from start to finish.
128
131
  - Is each entry's HMAC valid?
129
132
  - Does each entry correctly reference the previous one?
133
+ - Ignores 'rolled_back' entries.
130
134
 
131
135
  Returns: True (valid) | raises ChainIntegrityError
132
136
  """
133
- entries = self._load_all_entries()
137
+ entries = self._load_all_entries(include_rolled_back=False)
134
138
  if not entries:
135
139
  return True
136
140
 
@@ -148,12 +152,12 @@ class ProvenanceLog:
148
152
 
149
153
  expected_prev = entry.content_hash()
150
154
 
151
- logger.info(f"Chain verified: {len(entries)} entries valid.")
155
+ logger.info(f"Chain verified: {len(entries)} active entries valid.")
152
156
  return True
153
157
 
154
- def get_entries(self, limit: int = 100) -> list[ProvenanceEntry]:
158
+ def get_entries(self, limit: int = 100, include_rolled_back: bool = False) -> list[ProvenanceEntry]:
155
159
  """Returns the last N entries."""
156
- return self._load_all_entries(limit=limit)
160
+ return self._load_all_entries(limit=limit, include_rolled_back=include_rolled_back)
157
161
 
158
162
  def get_entry(self, entry_id: str) -> ProvenanceEntry | None:
159
163
  cursor = self._conn.execute(
@@ -166,15 +170,15 @@ class ProvenanceLog:
166
170
  self, template: str, kernel_name: str, model: str
167
171
  ) -> int:
168
172
  """
169
- Calculates the number of interactions matching the current context.
173
+ Calculates the number of active interactions matching the current context.
170
174
  This provides the experience metric for Stability-Plasticity Scheduling.
171
175
  """
172
- # We parse the JSON metadata column. SQLite JSON1 extension is standard.
173
176
  query = """
174
177
  SELECT COUNT(*) FROM provenance
175
178
  WHERE json_extract(metadata, '$.template') = ?
176
179
  AND json_extract(metadata, '$.kernel') = ?
177
180
  AND json_extract(metadata, '$.model') = ?
181
+ AND status = 'active'
178
182
  """
179
183
  try:
180
184
  row = self._conn.execute(query, (template, kernel_name, model)).fetchone()
@@ -184,21 +188,81 @@ class ProvenanceLog:
184
188
  f"Could not count interactions (JSON query failed): {e}. "
185
189
  "Returning 0 to avoid inflating ElasticityScheduler thresholds."
186
190
  )
187
- # Return 0 (conservative) — do NOT return total count without filters
188
- # as it would incorrectly inflate the interaction_count for the scheduler.
189
191
  return 0
190
192
 
193
+ def export_log(self, include_rolled_back: bool = False) -> list[dict[str, Any]]:
194
+ """
195
+ Exports the verified log as a list of dictionaries.
196
+ By default, only active entries are exported.
197
+
198
+ Args:
199
+ include_rolled_back: If True, include all entries.
200
+
201
+ Returns:
202
+ A list of all log entries, where each entry is a dictionary.
203
+
204
+ Raises:
205
+ ChainIntegrityError: If the active chain is invalid.
206
+ """
207
+ self.verify_chain() # Ensure integrity before export
208
+ all_entries = self._load_all_entries(include_rolled_back=include_rolled_back)
209
+ return [asdict(entry) for entry in all_entries]
210
+
211
+ def get_read_only_connection(self) -> sqlite3.Connection:
212
+ """
213
+ Returns a read-only connection to the database.
214
+ """
215
+ try:
216
+ db_uri = self._vault_path.as_uri() + "?mode=ro"
217
+ return sqlite3.connect(db_uri, uri=True, check_same_thread=False)
218
+ except sqlite3.OperationalError as e:
219
+ raise VaultError(
220
+ "Could not open read-only connection. "
221
+ "Ensure the database file exists and has correct permissions."
222
+ ) from e
223
+
224
+ def rollback(self, agent_id: str, entry_id: str) -> None:
225
+ """
226
+ Rolls back the log to the specified entry ID for a given agent.
227
+ All subsequent entries for that agent will be marked as 'rolled_back'.
228
+ """
229
+ with self._lock:
230
+ entry = self.get_entry(entry_id)
231
+ if not entry:
232
+ raise ValueError(f"Entry with ID '{entry_id}' not found.")
233
+
234
+ # This is a simplification. In a real multi-agent system,
235
+ # we would need a more robust way to identify agent-specific entries.
236
+ # Here, we assume an 'agent_id' is stored in the metadata.
237
+ if entry.metadata.get("agent_id") != agent_id:
238
+ raise ValueError("Entry does not belong to the specified agent.")
239
+
240
+ try:
241
+ self._conn.execute(
242
+ """
243
+ UPDATE provenance
244
+ SET status = 'rolled_back'
245
+ WHERE timestamp > ? AND json_extract(metadata, '$.agent_id') = ?
246
+ """,
247
+ (entry.timestamp, agent_id),
248
+ )
249
+ self._conn.commit()
250
+ logger.info(f"Provenance log rolled back for agent '{agent_id}' to entry '{entry_id}'")
251
+ except sqlite3.Error as e:
252
+ raise VaultError(f"SQLite rollback error: {e}") from e
253
+
191
254
  # ── Database ──────────────────────────────────────────────────────────────
192
255
 
193
256
  def _init_db(self) -> sqlite3.Connection:
194
257
  import os
195
258
  self._vault_path.parent.mkdir(parents=True, exist_ok=True)
196
- # Create file if not exist to set permissions before sqlite connection
197
259
  if not self._vault_path.exists():
198
260
  self._vault_path.touch()
199
261
  self._vault_path.chmod(0o600)
200
262
 
201
263
  conn = sqlite3.connect(str(self._vault_path), check_same_thread=False)
264
+
265
+ # Create table if it doesn't exist
202
266
  conn.execute("""
203
267
  CREATE TABLE IF NOT EXISTS provenance (
204
268
  id TEXT PRIMARY KEY,
@@ -208,9 +272,18 @@ class ProvenanceLog:
208
272
  divergence_score REAL,
209
273
  metadata TEXT NOT NULL,
210
274
  prev_entry_hash TEXT NOT NULL,
211
- hmac_signature TEXT NOT NULL
275
+ hmac_signature TEXT NOT NULL,
276
+ status TEXT NOT NULL DEFAULT 'active'
212
277
  )
213
278
  """)
279
+
280
+ # Add status column for migration from older versions
281
+ try:
282
+ conn.execute("SELECT status FROM provenance LIMIT 1").fetchall()
283
+ except sqlite3.OperationalError:
284
+ conn.execute("ALTER TABLE provenance ADD COLUMN status TEXT DEFAULT 'active'")
285
+ logger.info("Migrated database: Added 'status' column to provenance table.")
286
+
214
287
  conn.commit()
215
288
  return conn
216
289
 
@@ -220,8 +293,8 @@ class ProvenanceLog:
220
293
  """
221
294
  INSERT INTO provenance
222
295
  (id, timestamp, request_hash, response_hash,
223
- divergence_score, metadata, prev_entry_hash, hmac_signature)
224
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
296
+ divergence_score, metadata, prev_entry_hash, hmac_signature, status)
297
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
225
298
  """,
226
299
  (
227
300
  entry.id,
@@ -232,22 +305,32 @@ class ProvenanceLog:
232
305
  json.dumps(entry.metadata),
233
306
  entry.prev_entry_hash,
234
307
  entry.hmac_signature,
308
+ entry.status,
235
309
  ),
236
310
  )
237
311
  self._conn.commit()
238
312
  except sqlite3.Error as e:
239
313
  raise VaultError(f"SQLite write error: {e}") from e
240
314
 
241
- def _load_all_entries(self, limit: int | None = None) -> list[ProvenanceEntry]:
242
- query = "SELECT * FROM provenance ORDER BY timestamp ASC"
315
+ def _load_all_entries(self, limit: int | None = None, include_rolled_back: bool = False) -> list[ProvenanceEntry]:
316
+ query = "SELECT * FROM provenance"
317
+ conditions = []
318
+ if not include_rolled_back:
319
+ conditions.append("status = 'active'")
320
+
321
+ if conditions:
322
+ query += " WHERE " + " AND ".join(conditions)
323
+
324
+ query += " ORDER BY timestamp ASC"
243
325
  if limit:
244
326
  query += f" LIMIT {limit}"
327
+
245
328
  rows = self._conn.execute(query).fetchall()
246
329
  return [self._row_to_entry(r) for r in rows]
247
330
 
248
331
  def _get_last_entry_hash(self) -> str:
249
332
  row = self._conn.execute(
250
- "SELECT * FROM provenance ORDER BY timestamp DESC LIMIT 1"
333
+ "SELECT * FROM provenance WHERE status = 'active' ORDER BY timestamp DESC LIMIT 1"
251
334
  ).fetchone()
252
335
  if not row:
253
336
  return self.GENESIS_HASH
@@ -264,6 +347,7 @@ class ProvenanceLog:
264
347
  metadata=json.loads(row[5]),
265
348
  prev_entry_hash=row[6],
266
349
  hmac_signature=row[7],
350
+ status=row[8],
267
351
  )
268
352
 
269
353
  # ── Cryptography ────────────────────────────────────────────────────────────
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ct-toolkit
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems
5
5
  Author: Hakan Damar
6
6
  License: Apache-2.0
@@ -51,6 +51,7 @@ CT Toolkit is an open-source security layer designed to preserve the **identity
51
51
  ---
52
52
 
53
53
  ## 📖 Official Documentation
54
+
54
55
  For full API reference, architecture details, examples, and integration guides, visit our documentation site:
55
56
  👉 [**https://hakandamar.github.io/ct-toolkit/**](https://hakandamar.github.io/ct-toolkit/)
56
57
 
@@ -63,6 +64,7 @@ For full API reference, architecture details, examples, and integration guides,
63
64
  In complex agentic workflows, LLMs tend to "drift" from their original instructions. CT Toolkit provides the mathematical and cryptographic guardrails to ensure your agents remain aligned with their core constitution, even across deep hierarchies.
64
65
 
65
66
  - **Constitutional Kernels**: Axiomatic identity anchors.
67
+ - **Autonomous Self-Correction**: Active L2->L1 feedback loop that retries and corrects divergent responses before they reach the user.
66
68
  - **Divergence Engine**: Multi-tiered drift analysis (L1/L2/L3).
67
69
  - **Hierarchical Propagation**: Mother-to-child constraint inheritance.
68
70
  - **Provenance Log**: Immutable HMAC-signed interaction history.
@@ -92,15 +94,16 @@ print(f"Divergence Score: {response.divergence_score}")
92
94
 
93
95
  ## 🚦 Project Health & Status
94
96
 
95
- | Metric | Status |
96
- | :--- | :--- |
97
- | **Tests** | ✅ 218/218 passing (93% coverage) |
98
- | **Last Phase** | ✅ Phase 4: OS Support, Deep Agents & Alerts (Complete) |
99
- | **Current Goal** | 🔶 Phase 5: Vault and Security Infrastructure |
97
+ | Metric | Status |
98
+ | :--------------- | :------------------------------------------------------ |
99
+ | **Tests** | ✅ 231/232 passing (90% coverage) |
100
+ | **Last Phase** | ✅ Phase 4: OS Support, Deep Agents & Alerts (Complete) |
101
+ | **Current Goal** | 🔶 Phase 5: Vault & Security (In Progress) |
100
102
 
101
103
  For a detailed breakdown of the 8-phase roadmap, see [**PROJECT_STATUS.md**](docs/PROJECT_STATUS.md).
102
104
 
103
105
  ### Framework & Model Support
106
+
104
107
  Seamlessly integrate with your favorite frameworks and local models:
105
108
 
106
109
  - **Local Models**: Support for LM Studio, Ollama, and local Qwen/Llama endpoints.
@@ -111,9 +114,11 @@ Seamlessly integrate with your favorite frameworks and local models:
111
114
  ---
112
115
 
113
116
  ## Theoretical Foundation
117
+
114
118
  Translating the framework proposed in [**The Computational Theseus (2025)**](https://hakandamar.com/the-computational-theseus-engineering-identity-continuity-as-a-guardrail-against-sequential-963918c1720d) into engineering practice.
115
119
 
116
120
  ---
117
121
 
118
122
  ## License
123
+
119
124
  Apache License 2.0.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ct-toolkit"
7
- version = "0.3.0"
7
+ version = "0.3.2"
8
8
  description = "Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
File without changes
File without changes