ct-toolkit 0.2.6__tar.gz → 0.2.8__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.2.6 → ct_toolkit-0.2.8}/PKG-INFO +1 -1
  2. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/core/exceptions.py +15 -0
  3. ct_toolkit-0.2.8/ct_toolkit/core/integrity.py +56 -0
  4. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/core/wrapper.py +96 -5
  5. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit.egg-info/PKG-INFO +1 -1
  6. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit.egg-info/SOURCES.txt +1 -0
  7. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/pyproject.toml +7 -1
  8. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/LICENSE +0 -0
  9. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/README.md +0 -0
  10. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/__init__.py +0 -0
  11. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/core/__init__.py +0 -0
  12. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/core/compatibility.py +0 -0
  13. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/core/kernel.py +0 -0
  14. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/divergence/__init__.py +0 -0
  15. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/divergence/analysis.py +0 -0
  16. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/divergence/engine.py +0 -0
  17. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/divergence/l2_judge.py +0 -0
  18. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/divergence/l3_icm.py +0 -0
  19. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/divergence/loss.py +0 -0
  20. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/divergence/scheduler.py +0 -0
  21. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/endorsement/__init__.py +0 -0
  22. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/endorsement/probes/__init__.py +0 -0
  23. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/endorsement/reflective.py +0 -0
  24. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/identity/__init__.py +0 -0
  25. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/identity/embedding.py +0 -0
  26. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/identity/templates/__init__.py +0 -0
  27. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/identity/templates/defense.yaml +0 -0
  28. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/identity/templates/finance.yaml +0 -0
  29. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/identity/templates/general.yaml +0 -0
  30. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/identity/templates/medical.yaml +0 -0
  31. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/kernels/__init__.py +0 -0
  32. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/kernels/default.yaml +0 -0
  33. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/kernels/defense.yaml +0 -0
  34. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/middleware/__init__.py +0 -0
  35. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/middleware/autogen.py +0 -0
  36. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/middleware/crewai.py +0 -0
  37. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/middleware/deepagents.py +0 -0
  38. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/middleware/langchain.py +0 -0
  39. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/provenance/__init__.py +0 -0
  40. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/provenance/log.py +0 -0
  41. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/provenance/vault/__init__.py +0 -0
  42. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/utils/__init__.py +0 -0
  43. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit/utils/logger.py +0 -0
  44. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit.egg-info/dependency_links.txt +0 -0
  45. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit.egg-info/requires.txt +0 -0
  46. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/ct_toolkit.egg-info/top_level.txt +0 -0
  47. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/setup.cfg +0 -0
  48. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/tests/test_analysis.py +0 -0
  49. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/tests/test_deep_agents_integration.py +0 -0
  50. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/tests/test_loss.py +0 -0
  51. {ct_toolkit-0.2.6 → ct_toolkit-0.2.8}/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.2.6
3
+ Version: 0.2.8
4
4
  Summary: Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems
5
5
  Author: Hakan Damar
6
6
  License: Apache-2.0
@@ -103,3 +103,18 @@ class ChainIntegrityError(ProvenanceError):
103
103
 
104
104
  class VaultError(ProvenanceError):
105
105
  """Vault connection or authorization error."""
106
+
107
+
108
+ # ── File Integrity Errors ────────────────────────────────────────────────────
109
+
110
+ class ConfigurationTamperingError(CTToolkitError):
111
+ """
112
+ Raised when a configuration file has been modified after initialization.
113
+ Indicates a potential security breach or unintended change.
114
+ """
115
+ def __init__(self, file_path: str):
116
+ self.file_path = file_path
117
+ super().__init__(
118
+ f"SECURITY ALERT: Tampering detected in configuration file: {file_path}. "
119
+ "Execution halted to prevent security bypass."
120
+ )
@@ -0,0 +1,56 @@
1
+ """
2
+ ct_toolkit.core.integrity
3
+ -------------------------
4
+ Cryptographic integrity monitoring for configuration files.
5
+ """
6
+ import hashlib
7
+ from pathlib import Path
8
+ from ct_toolkit.utils.logger import get_logger
9
+ from ct_toolkit.core.exceptions import ConfigurationTamperingError
10
+
11
+ logger = get_logger(__name__)
12
+
13
+ class IntegrityMonitor:
14
+ """
15
+ Monitors the cryptographic integrity of CT configuration files.
16
+ Prevents runtime tampering by agents or malicious actors.
17
+ """
18
+ def __init__(self):
19
+ self._file_hashes: dict[str, str] = {}
20
+
21
+ def _calculate_hash(self, file_path: Path) -> str:
22
+ """Calculates the SHA-256 hash of a given file."""
23
+ hasher = hashlib.sha256()
24
+ try:
25
+ with open(file_path, 'rb') as f:
26
+ buf = f.read()
27
+ hasher.update(buf)
28
+ return hasher.hexdigest()
29
+ except FileNotFoundError:
30
+ logger.error(f"Integrity check failed: File not found at {file_path}")
31
+ raise
32
+
33
+ def register_file(self, file_path: Path) -> None:
34
+ """Registers a file and stores its initial hash."""
35
+ if not file_path.is_file():
36
+ logger.warning(f"Integrity check: Cannot register non-file path: {file_path}")
37
+ return
38
+
39
+ file_hash = self._calculate_hash(file_path)
40
+ self._file_hashes[str(file_path)] = file_hash
41
+ logger.debug(f"Registered integrity hash for {file_path.name}")
42
+
43
+ def verify_integrity(self) -> None:
44
+ """
45
+ Verifies that none of the registered files have been modified.
46
+ Raises ConfigurationTamperingError if a mismatch is found.
47
+ """
48
+ if not self._file_hashes:
49
+ return # Nothing to verify
50
+
51
+ for path_str, original_hash in self._file_hashes.items():
52
+ current_hash = self._calculate_hash(Path(path_str))
53
+ if current_hash != original_hash:
54
+ raise ConfigurationTamperingError(path_str)
55
+ logger.debug("Integrity verification passed for all registered files.")
56
+
@@ -21,15 +21,18 @@ from __future__ import annotations
21
21
 
22
22
  import time
23
23
  import hashlib
24
+ import os
24
25
  from dataclasses import dataclass, field
25
26
  from pathlib import Path
26
27
  from typing import Any, Iterator
27
28
  import any_llm
28
29
  from any_llm import AnyLLM
30
+ import glob
29
31
 
30
32
  from ct_toolkit.core.kernel import ConstitutionalKernel
31
33
  from ct_toolkit.core.compatibility import CompatibilityLayer, CompatibilityResult
32
34
  from ct_toolkit.core.exceptions import CTToolkitError, MissingClientError
35
+ from ct_toolkit.core.integrity import IntegrityMonitor
33
36
  from ct_toolkit.divergence.scheduler import RiskProfile
34
37
  from ct_toolkit.utils.logger import get_logger
35
38
 
@@ -39,6 +42,7 @@ logger = get_logger(__name__)
39
42
  @dataclass
40
43
  class WrapperConfig:
41
44
  """TheseusWrapper configuration."""
45
+ project_root: str | Path | None = None
42
46
  kernel_path: str | Path | None = None
43
47
  template: str = "general"
44
48
  kernel_name: str = "default"
@@ -108,6 +112,7 @@ class TheseusWrapper:
108
112
  kernel_path: str | Path | None = None,
109
113
  template: str = "general",
110
114
  kernel_name: str = "default",
115
+ project_root: str | Path | None = None,
111
116
  ) -> None:
112
117
  """
113
118
  Initializes TheseusWrapper.
@@ -118,11 +123,14 @@ class TheseusWrapper:
118
123
  config: Optional WrapperConfig instance.
119
124
  provider: Provider name (e.g. "openai", "anthropic", "ollama", "google", "cohere").
120
125
  If provided, 'any-llm' will be used to initialize the client internally.
126
+ project_root: Path to the user's project root directory. Used to find a custom 'config/' folder.
127
+ Defaults to the current working directory.
121
128
  """
122
129
  self._config = config or WrapperConfig(
123
130
  kernel_path=kernel_path,
124
131
  template=template,
125
132
  kernel_name=kernel_name,
133
+ project_root=project_root,
126
134
  )
127
135
 
128
136
  if client is None and provider is None:
@@ -131,6 +139,11 @@ class TheseusWrapper:
131
139
 
132
140
  self._client = client
133
141
  self._provider = provider or self._detect_provider(client)
142
+ self._project_root = (
143
+ Path(self._config.project_root)
144
+ if self._config.project_root
145
+ else Path(os.getcwd())
146
+ )
134
147
  self._kernel = self._load_kernel()
135
148
 
136
149
  # If a parent kernel exists, merge it into our own kernel as axiomatic constraints
@@ -146,8 +159,57 @@ class TheseusWrapper:
146
159
  self._divergence_engine = self._init_divergence_engine()
147
160
  self._last_model: str = "unknown"
148
161
 
162
+ # ── Integrity Monitoring ──
163
+ self._integrity_monitor = IntegrityMonitor()
164
+ self._register_monitored_files()
165
+
149
166
  self._log_init()
150
167
 
168
+ def _register_monitored_files(self):
169
+ """
170
+ Finds and registers all critical configuration files for integrity monitoring,
171
+ including both built-in package files and user-provided custom configs.
172
+ """
173
+ logger.debug("Registering configuration files for integrity monitoring.")
174
+
175
+ # 1. Scan built-in package files
176
+ try:
177
+ from importlib.resources import files
178
+ package_root = files("ct_toolkit")
179
+
180
+ internal_patterns = [
181
+ "kernels/**/*.yaml",
182
+ "identity/templates/**/*.yaml",
183
+ "endorsement/probes/**/*.json",
184
+ ]
185
+
186
+ for pattern in internal_patterns:
187
+ for file_path in package_root.rglob(pattern):
188
+ if file_path.is_file():
189
+ self._integrity_monitor.register_file(file_path)
190
+
191
+ except (ImportError, ModuleNotFoundError):
192
+ # Fallback for older Python or different environments
193
+ package_root = Path(__file__).parent.parent
194
+ # This is less ideal as it might find non-package files, but it's a
195
+ # reasonable fallback for development environments.
196
+ for pattern in internal_patterns:
197
+ for file_path in package_root.rglob(pattern):
198
+ if file_path.is_file():
199
+ self._integrity_monitor.register_file(file_path)
200
+
201
+ # 2. Scan user's custom config directory
202
+ if self._project_root:
203
+ user_config_dir = self._project_root / "config"
204
+ if user_config_dir.is_dir():
205
+ logger.debug(f"Scanning user config directory for integrity monitoring: {user_config_dir}")
206
+ user_patterns = ["*_kernel.yaml", "*_identity.yaml", "*_probes.json", "*.yaml", "*.json"]
207
+ for pattern in user_patterns:
208
+ for file_path in user_config_dir.rglob(pattern):
209
+ if file_path.is_file():
210
+ self._integrity_monitor.register_file(file_path)
211
+
212
+
151
213
  # ── Factory / Init ─────────────────────────────────────────────────────────
152
214
 
153
215
  def _detect_provider(self, client: Any) -> str:
@@ -180,30 +242,40 @@ class TheseusWrapper:
180
242
  return "unknown"
181
243
 
182
244
  def _load_kernel(self) -> ConstitutionalKernel:
245
+ # 1. Check for explicit path
183
246
  if self._config.kernel_path:
247
+ logger.debug(f"Loading kernel from explicit path: {self._config.kernel_path}")
184
248
  return ConstitutionalKernel.from_yaml(self._config.kernel_path)
185
-
186
- import os
249
+
187
250
  safe_name = os.path.basename(self._config.kernel_name)
188
251
 
189
- # Load built-in kernel using importlib.resources
252
+ # 2. Check for user-defined config directory
253
+ if self._project_root:
254
+ user_config_path = self._project_root / "config" / f"{safe_name}.yaml"
255
+ if user_config_path.exists():
256
+ logger.debug(f"Loading kernel from user config: {user_config_path}")
257
+ return ConstitutionalKernel.from_yaml(user_config_path)
258
+
259
+ # 3. Load built-in kernel using importlib.resources
190
260
  try:
191
261
  from importlib.resources import files
192
262
  kernel_resource = files("ct_toolkit.kernels").joinpath(f"{safe_name}.yaml")
193
263
  if kernel_resource.is_file():
264
+ logger.debug(f"Loading built-in kernel: {safe_name}.yaml")
194
265
  return ConstitutionalKernel.from_yaml(kernel_resource)
195
266
  except (ImportError, FileNotFoundError):
196
- # Fallback
267
+ # Fallback for older python versions
197
268
  kernel_path = (
198
269
  Path(__file__).parent.parent
199
270
  / "kernels"
200
271
  / f"{safe_name}.yaml"
201
272
  )
202
273
  if kernel_path.exists():
274
+ logger.debug(f"Loading built-in kernel (fallback): {kernel_path}")
203
275
  return ConstitutionalKernel.from_yaml(kernel_path)
204
276
 
205
277
  logger.warning(
206
- f"Kernel '{self._config.kernel_name}' not found, using default."
278
+ f"Kernel '{self._config.kernel_name}' not found in user config or built-ins. Using default."
207
279
  )
208
280
  return ConstitutionalKernel.default()
209
281
 
@@ -299,6 +371,9 @@ class TheseusWrapper:
299
371
  system: Additional system prompt (appended to kernel)
300
372
  history: Conversation history [{"role": ..., "content": ...}]
301
373
  """
374
+ # --- Pre-flight Checks ---
375
+ self._integrity_monitor.verify_integrity()
376
+
302
377
  composed_system = self._compose_system_prompt(system)
303
378
  messages = self._build_messages(message, history, composed_system)
304
379
 
@@ -416,6 +491,10 @@ class TheseusWrapper:
416
491
  if hasattr(self._client, "api_key") and self._client.api_key:
417
492
  any_llm_kwargs["api_key"] = self._client.api_key
418
493
 
494
+ # Transfer common client configurations if available
495
+ if hasattr(self._client, "timeout") and "timeout" not in any_llm_kwargs:
496
+ any_llm_kwargs["timeout"] = self._client.timeout
497
+
419
498
  try:
420
499
  return any_llm.completion(**any_llm_kwargs)
421
500
  except Exception as e:
@@ -473,6 +552,16 @@ class TheseusWrapper:
473
552
  return messages
474
553
 
475
554
  def _extract_content(self, raw: Any) -> str:
555
+ # Handle dict responses (e.g. from some mockers or raw APIs)
556
+ if isinstance(raw, dict):
557
+ if "choices" in raw and isinstance(raw["choices"], list) and raw["choices"]:
558
+ # OpenAI dict format
559
+ return raw["choices"][0].get("message", {}).get("content", "") or ""
560
+ if "message" in raw:
561
+ # Ollama dict format
562
+ return raw["message"].get("content", "") or ""
563
+ return str(raw)
564
+
476
565
  # OpenAI
477
566
  if hasattr(raw, "choices"):
478
567
  return raw.choices[0].message.content or ""
@@ -485,6 +574,8 @@ class TheseusWrapper:
485
574
  return str(raw)
486
575
 
487
576
  def _extract_model(self, raw: Any, fallback: str | None) -> str:
577
+ if isinstance(raw, dict) and "model" in raw:
578
+ return raw["model"]
488
579
  if hasattr(raw, "model"):
489
580
  return raw.model
490
581
  return fallback or "unknown"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ct-toolkit
3
- Version: 0.2.6
3
+ Version: 0.2.8
4
4
  Summary: Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems
5
5
  Author: Hakan Damar
6
6
  License: Apache-2.0
@@ -10,6 +10,7 @@ ct_toolkit.egg-info/top_level.txt
10
10
  ct_toolkit/core/__init__.py
11
11
  ct_toolkit/core/compatibility.py
12
12
  ct_toolkit/core/exceptions.py
13
+ ct_toolkit/core/integrity.py
13
14
  ct_toolkit/core/kernel.py
14
15
  ct_toolkit/core/wrapper.py
15
16
  ct_toolkit/divergence/__init__.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ct-toolkit"
7
- version = "0.2.6"
7
+ version = "0.2.8"
8
8
  description = "Computational Theseus Toolkit — Identity Continuity Guardrails for Agentic Systems"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -71,3 +71,9 @@ dev = [
71
71
  "pytest-cov>=7.0.0",
72
72
  "twine>=6.2.0",
73
73
  ]
74
+
75
+ [tool.semantic_release]
76
+ version_toml = ["pyproject.toml:project.version"]
77
+ branch = "main"
78
+ changelog_file = "CHANGELOG.md"
79
+ build_command = "pip install build && python -m build"
File without changes
File without changes
File without changes