cct-cli 0.7.9.0__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.
Files changed (325) hide show
  1. calc_terminal/__init__.py +14 -0
  2. calc_terminal/__main__.py +14 -0
  3. calc_terminal/activity.py +1334 -0
  4. calc_terminal/agent.py +3387 -0
  5. calc_terminal/agent_runtime.py +519 -0
  6. calc_terminal/ai_context.py +447 -0
  7. calc_terminal/ai_modes.py +752 -0
  8. calc_terminal/ai_personalization.py +286 -0
  9. calc_terminal/ai_preview_feedback.py +213 -0
  10. calc_terminal/aicore.py +2572 -0
  11. calc_terminal/anim.py +367 -0
  12. calc_terminal/app.py +3685 -0
  13. calc_terminal/art.py +639 -0
  14. calc_terminal/atomsim.py +368 -0
  15. calc_terminal/attachments.py +743 -0
  16. calc_terminal/benchmark_system.py +414 -0
  17. calc_terminal/browser/__init__.py +36 -0
  18. calc_terminal/browser/browser_state.py +346 -0
  19. calc_terminal/browser/devserver.py +176 -0
  20. calc_terminal/browser/engine.py +494 -0
  21. calc_terminal/browser/navigation.py +84 -0
  22. calc_terminal/browser/preview.py +429 -0
  23. calc_terminal/browser/preview_entry.py +95 -0
  24. calc_terminal/browser/project_detector.py +144 -0
  25. calc_terminal/browser/server.py +449 -0
  26. calc_terminal/browser/state.py +75 -0
  27. calc_terminal/browser/watcher.py +99 -0
  28. calc_terminal/browser_gui/__init__.py +1 -0
  29. calc_terminal/browser_gui/__main__.py +3 -0
  30. calc_terminal/browser_gui/launcher.py +173 -0
  31. calc_terminal/browser_gui/playwright_browser.py +117 -0
  32. calc_terminal/browser_gui/qt_browser.py +1501 -0
  33. calc_terminal/browser_gui/webview_browser.py +57 -0
  34. calc_terminal/capabilities/__init__.py +35 -0
  35. calc_terminal/capabilities/adapters/__init__.py +33 -0
  36. calc_terminal/capabilities/adapters/bioinformatics.py +204 -0
  37. calc_terminal/capabilities/adapters/browser_adapter.py +205 -0
  38. calc_terminal/capabilities/adapters/filesystem.py +206 -0
  39. calc_terminal/capabilities/adapters/git_adapter.py +202 -0
  40. calc_terminal/capabilities/adapters/jupyter_adapter.py +138 -0
  41. calc_terminal/capabilities/adapters/ml_frameworks.py +158 -0
  42. calc_terminal/capabilities/adapters/platforms.py +200 -0
  43. calc_terminal/capabilities/adapters/python_exec.py +93 -0
  44. calc_terminal/capabilities/adapters/quantum_adapter.py +150 -0
  45. calc_terminal/capabilities/adapters/scientific_comp.py +123 -0
  46. calc_terminal/capabilities/adapters/structural_bio.py +161 -0
  47. calc_terminal/capabilities/adapters/terminal.py +99 -0
  48. calc_terminal/capabilities/bus.py +178 -0
  49. calc_terminal/capabilities/discovery.py +207 -0
  50. calc_terminal/capabilities/schema.py +221 -0
  51. calc_terminal/cat.ico +0 -0
  52. calc_terminal/cat_browser.py +2018 -0
  53. calc_terminal/chat_store.py +703 -0
  54. calc_terminal/cli.py +1178 -0
  55. calc_terminal/code_editor.py +640 -0
  56. calc_terminal/collaboration.py +723 -0
  57. calc_terminal/commands_data.py +139 -0
  58. calc_terminal/compatibility_engine.py +352 -0
  59. calc_terminal/compute/__init__.py +31 -0
  60. calc_terminal/compute/fabric.py +350 -0
  61. calc_terminal/config.py +227 -0
  62. calc_terminal/core/__init__.py +41 -0
  63. calc_terminal/core/checkpoint.py +156 -0
  64. calc_terminal/core/input/__init__.py +45 -0
  65. calc_terminal/core/mode_registry.py +300 -0
  66. calc_terminal/core/project_graph.py +172 -0
  67. calc_terminal/core/recovery.py +129 -0
  68. calc_terminal/core/security_layer.py +112 -0
  69. calc_terminal/core/task_graph.py +202 -0
  70. calc_terminal/core/unified_runtime.py +184 -0
  71. calc_terminal/core/verification.py +257 -0
  72. calc_terminal/customization.py +1566 -0
  73. calc_terminal/derivations.py +153 -0
  74. calc_terminal/device_control.py +263 -0
  75. calc_terminal/diagnostics/__init__.py +27 -0
  76. calc_terminal/diagnostics/doctor_engine.py +382 -0
  77. calc_terminal/diagnostics/self_test.py +247 -0
  78. calc_terminal/doctor.py +519 -0
  79. calc_terminal/easter_eggs.py +274 -0
  80. calc_terminal/editor/__init__.py +1 -0
  81. calc_terminal/editor/actions.py +263 -0
  82. calc_terminal/editor/commands.py +160 -0
  83. calc_terminal/editor/shortcuts.py +226 -0
  84. calc_terminal/engine.py +259 -0
  85. calc_terminal/errors.py +120 -0
  86. calc_terminal/event_stream.py +146 -0
  87. calc_terminal/eventbus.py +133 -0
  88. calc_terminal/extensions.py +733 -0
  89. calc_terminal/fallback_cli.py +1321 -0
  90. calc_terminal/first_run.py +265 -0
  91. calc_terminal/fomoji_auth.py +1043 -0
  92. calc_terminal/formulas.py +82 -0
  93. calc_terminal/fs_cache.py +121 -0
  94. calc_terminal/fs_watcher.py +277 -0
  95. calc_terminal/game.py +193 -0
  96. calc_terminal/gen1.py +5 -0
  97. calc_terminal/generators.py +245 -0
  98. calc_terminal/gestures/__init__.py +42 -0
  99. calc_terminal/gestures/bindings.py +175 -0
  100. calc_terminal/gestures/manager.py +477 -0
  101. calc_terminal/goodbye.py +363 -0
  102. calc_terminal/gpu3d.py +290 -0
  103. calc_terminal/graphs.py +358 -0
  104. calc_terminal/hardware_analyzer.py +440 -0
  105. calc_terminal/host/__init__.py +30 -0
  106. calc_terminal/host/browser_manager.py +187 -0
  107. calc_terminal/host/desktop.py +1386 -0
  108. calc_terminal/host/launcher.py +395 -0
  109. calc_terminal/host/terminal.py +279 -0
  110. calc_terminal/identity.py +216 -0
  111. calc_terminal/input/__init__.py +54 -0
  112. calc_terminal/input/capabilities.py +258 -0
  113. calc_terminal/input/focus.py +87 -0
  114. calc_terminal/input/gestures.py +64 -0
  115. calc_terminal/input/pointer.py +114 -0
  116. calc_terminal/input/touch.py +345 -0
  117. calc_terminal/keys.py +84 -0
  118. calc_terminal/live_automation.py +165 -0
  119. calc_terminal/mathtext.py +433 -0
  120. calc_terminal/mcp.py +386 -0
  121. calc_terminal/memory.py +337 -0
  122. calc_terminal/memory_v2.py +479 -0
  123. calc_terminal/metrics.py +333 -0
  124. calc_terminal/mode_detection.py +146 -0
  125. calc_terminal/model.py +2431 -0
  126. calc_terminal/model_router.py +665 -0
  127. calc_terminal/models/__init__.py +0 -0
  128. calc_terminal/models/active_state.py +187 -0
  129. calc_terminal/models/dynamic_registry.py +584 -0
  130. calc_terminal/models/manager.py +781 -0
  131. calc_terminal/models/model_metadata.json +3526 -0
  132. calc_terminal/models/profiles.py +194 -0
  133. calc_terminal/models/registry.py +265 -0
  134. calc_terminal/models/schema.py +197 -0
  135. calc_terminal/models/validator.py +287 -0
  136. calc_terminal/models/verification_engine.py +368 -0
  137. calc_terminal/native_picker.py +215 -0
  138. calc_terminal/ollama_catalog.py +279 -0
  139. calc_terminal/ollama_download.py +233 -0
  140. calc_terminal/orchestrator.py +304 -0
  141. calc_terminal/package_research.py +322 -0
  142. calc_terminal/packages.py +1024 -0
  143. calc_terminal/pc_specs.py +116 -0
  144. calc_terminal/permissions.py +334 -0
  145. calc_terminal/pet.py +106 -0
  146. calc_terminal/pipeline.py +505 -0
  147. calc_terminal/platform/__init__.py +491 -0
  148. calc_terminal/platform/desktop.py +491 -0
  149. calc_terminal/platform/web.py +781 -0
  150. calc_terminal/preview/__init__.py +1 -0
  151. calc_terminal/preview/dev_server.py +303 -0
  152. calc_terminal/preview/diagnostics.py +131 -0
  153. calc_terminal/preview/live_reload.py +66 -0
  154. calc_terminal/preview/manager.py +129 -0
  155. calc_terminal/project_stats.py +209 -0
  156. calc_terminal/projects.py +328 -0
  157. calc_terminal/providers/__init__.py +0 -0
  158. calc_terminal/providers/adapters/__init__.py +80 -0
  159. calc_terminal/providers/adapters/anthropic_adapter.py +127 -0
  160. calc_terminal/providers/adapters/base.py +106 -0
  161. calc_terminal/providers/adapters/chinese_adapters.py +420 -0
  162. calc_terminal/providers/adapters/gemini_adapter.py +101 -0
  163. calc_terminal/providers/adapters/ollama_adapter.py +83 -0
  164. calc_terminal/providers/adapters/openai_adapter.py +159 -0
  165. calc_terminal/providers/adapters/other_adapters.py +246 -0
  166. calc_terminal/providers/anthropic_provider.py +172 -0
  167. calc_terminal/providers/auto_update.py +416 -0
  168. calc_terminal/providers/base_provider.py +105 -0
  169. calc_terminal/providers/discovery_manager.py +207 -0
  170. calc_terminal/providers/gemini_provider.py +178 -0
  171. calc_terminal/providers/lifecycle.py +767 -0
  172. calc_terminal/providers/ollama_adapter.py +707 -0
  173. calc_terminal/providers/openai_provider.py +254 -0
  174. calc_terminal/providers/provider_manager.py +1827 -0
  175. calc_terminal/providers/providers.json +4075 -0
  176. calc_terminal/reactionsim.py +279 -0
  177. calc_terminal/registry.py +337 -0
  178. calc_terminal/report.py +162 -0
  179. calc_terminal/research/__init__.py +45 -0
  180. calc_terminal/research/artifact_intel.py +126 -0
  181. calc_terminal/research/data_lineage.py +123 -0
  182. calc_terminal/research/experiment_ledger.py +303 -0
  183. calc_terminal/research/reproducibility.py +131 -0
  184. calc_terminal/resilience/__init__.py +47 -0
  185. calc_terminal/resilience/agent_state.py +121 -0
  186. calc_terminal/resilience/capability_matcher.py +174 -0
  187. calc_terminal/resilience/circuit_breaker.py +158 -0
  188. calc_terminal/resilience/failover_engine.py +230 -0
  189. calc_terminal/resilience/health_monitor.py +192 -0
  190. calc_terminal/resilience/ollama_adapter.py +125 -0
  191. calc_terminal/resilience/orchestrator.py +312 -0
  192. calc_terminal/resilience/types.py +134 -0
  193. calc_terminal/sandbox.py +98 -0
  194. calc_terminal/scires.py +558 -0
  195. calc_terminal/security_scanner.py +126 -0
  196. calc_terminal/session.py +294 -0
  197. calc_terminal/sim3d.py +206 -0
  198. calc_terminal/solver.py +276 -0
  199. calc_terminal/sound.py +127 -0
  200. calc_terminal/task_reports.py +287 -0
  201. calc_terminal/terminal_host.py +201 -0
  202. calc_terminal/terminal_identity.py +411 -0
  203. calc_terminal/test_ai_mode_reliability.py +344 -0
  204. calc_terminal/test_browser.py +368 -0
  205. calc_terminal/test_code_editor_upgrade.py +485 -0
  206. calc_terminal/test_customization.py +1148 -0
  207. calc_terminal/test_customization_ui.py +612 -0
  208. calc_terminal/test_dynamic_registry.py +304 -0
  209. calc_terminal/test_extensions.py +436 -0
  210. calc_terminal/test_overhaul.py +557 -0
  211. calc_terminal/test_project_detect.py +255 -0
  212. calc_terminal/test_root_cause_fix.py +527 -0
  213. calc_terminal/test_stability.py +532 -0
  214. calc_terminal/test_terminal_identity.py +132 -0
  215. calc_terminal/test_v079_speed.py +460 -0
  216. calc_terminal/theme.py +1107 -0
  217. calc_terminal/timeline.py +139 -0
  218. calc_terminal/todos.py +246 -0
  219. calc_terminal/tool_call_normalizer.py +419 -0
  220. calc_terminal/tui.py +104 -0
  221. calc_terminal/ui/__init__.py +8 -0
  222. calc_terminal/ui/activity_panel.py +231 -0
  223. calc_terminal/ui/activity_stream_panel.py +238 -0
  224. calc_terminal/ui/animations.py +122 -0
  225. calc_terminal/ui/app.py +7271 -0
  226. calc_terminal/ui/attach_panel.py +597 -0
  227. calc_terminal/ui/attachments.py +424 -0
  228. calc_terminal/ui/backup_panel.py +810 -0
  229. calc_terminal/ui/browser_shell.py +887 -0
  230. calc_terminal/ui/cat_agent.py +357 -0
  231. calc_terminal/ui/chats_panel.py +899 -0
  232. calc_terminal/ui/command_palette.py +125 -0
  233. calc_terminal/ui/command_palette_modal.py +166 -0
  234. calc_terminal/ui/composer.py +1141 -0
  235. calc_terminal/ui/context_menu.py +197 -0
  236. calc_terminal/ui/conversation.py +1435 -0
  237. calc_terminal/ui/customization_panel.py +1229 -0
  238. calc_terminal/ui/dashboard.py +404 -0
  239. calc_terminal/ui/design_system.py +557 -0
  240. calc_terminal/ui/diff_panel.py +213 -0
  241. calc_terminal/ui/editor.py +2102 -0
  242. calc_terminal/ui/empty_state.py +302 -0
  243. calc_terminal/ui/events.py +487 -0
  244. calc_terminal/ui/extensions_panel.py +815 -0
  245. calc_terminal/ui/footer.py +166 -0
  246. calc_terminal/ui/gestures_panel.py +383 -0
  247. calc_terminal/ui/goodbye_screen.py +100 -0
  248. calc_terminal/ui/header.py +1034 -0
  249. calc_terminal/ui/help_panel.py +254 -0
  250. calc_terminal/ui/live_activities.py +914 -0
  251. calc_terminal/ui/mcp_panel.py +570 -0
  252. calc_terminal/ui/memory_center.py +524 -0
  253. calc_terminal/ui/mode_colors_panel.py +525 -0
  254. calc_terminal/ui/nav_screens.py +747 -0
  255. calc_terminal/ui/ollama_panel.py +536 -0
  256. calc_terminal/ui/palette.py +221 -0
  257. calc_terminal/ui/permission_panel.py +269 -0
  258. calc_terminal/ui/personalization_panel.py +517 -0
  259. calc_terminal/ui/personalize_center.py +1568 -0
  260. calc_terminal/ui/preview_panel.py +441 -0
  261. calc_terminal/ui/resizers.py +402 -0
  262. calc_terminal/ui/sidebar.py +1285 -0
  263. calc_terminal/ui/statusbar.py +168 -0
  264. calc_terminal/ui/theme_css.py +1396 -0
  265. calc_terminal/ui/thinking.py +226 -0
  266. calc_terminal/ui/timeline_panel.py +102 -0
  267. calc_terminal/ui/todo_panel.py +193 -0
  268. calc_terminal/ui/viewport.py +136 -0
  269. calc_terminal/ui/vision_panel.py +489 -0
  270. calc_terminal/ui/welcome_modal.py +343 -0
  271. calc_terminal/ui/widgets.py +160 -0
  272. calc_terminal/ui/workspace.py +831 -0
  273. calc_terminal/viewers/__init__.py +1 -0
  274. calc_terminal/viewers/document_viewer.py +252 -0
  275. calc_terminal/viewers/image_viewer.py +241 -0
  276. calc_terminal/viewers/pdf_viewer.py +203 -0
  277. calc_terminal/viewers/presentation_viewer.py +164 -0
  278. calc_terminal/viewers/registry.py +120 -0
  279. calc_terminal/viewers/spreadsheet_viewer.py +204 -0
  280. calc_terminal/vision/__init__.py +89 -0
  281. calc_terminal/vision/analysis.py +194 -0
  282. calc_terminal/vision/annotations.py +297 -0
  283. calc_terminal/vision/capture.py +171 -0
  284. calc_terminal/vision/context.py +231 -0
  285. calc_terminal/vision/correlation.py +169 -0
  286. calc_terminal/vision/cursor.py +258 -0
  287. calc_terminal/vision/events.py +66 -0
  288. calc_terminal/vision/frame_pipeline.py +259 -0
  289. calc_terminal/vision/priority.py +218 -0
  290. calc_terminal/vision/provider.py +180 -0
  291. calc_terminal/vision/safety.py +149 -0
  292. calc_terminal/vision/session.py +281 -0
  293. calc_terminal/vision/verify.py +162 -0
  294. calc_terminal/vision.py +514 -0
  295. calc_terminal/vscode_integration.py +113 -0
  296. calc_terminal/web/__init__.py +8 -0
  297. calc_terminal/web/cat_runtime.py +710 -0
  298. calc_terminal/web/server.py +2891 -0
  299. calc_terminal/web/static/css/app.css +3152 -0
  300. calc_terminal/web/static/icons/badge-72.png +0 -0
  301. calc_terminal/web/static/icons/cat.ico +0 -0
  302. calc_terminal/web/static/icons/icon-128.png +0 -0
  303. calc_terminal/web/static/icons/icon-144.png +0 -0
  304. calc_terminal/web/static/icons/icon-152.png +0 -0
  305. calc_terminal/web/static/icons/icon-192.png +0 -0
  306. calc_terminal/web/static/icons/icon-384.png +0 -0
  307. calc_terminal/web/static/icons/icon-512.png +0 -0
  308. calc_terminal/web/static/icons/icon-72.png +0 -0
  309. calc_terminal/web/static/icons/icon-96.png +0 -0
  310. calc_terminal/web/static/icons/icon.svg +34 -0
  311. calc_terminal/web/static/icons/new-project.png +0 -0
  312. calc_terminal/web/static/icons/open-project.png +0 -0
  313. calc_terminal/web/static/index.html +734 -0
  314. calc_terminal/web/static/js/app.js +2403 -0
  315. calc_terminal/web/static/manifest.json +88 -0
  316. calc_terminal/web/static/sw.js +230 -0
  317. calc_terminal/workflow_engine.py +769 -0
  318. calc_terminal/workspace.py +593 -0
  319. calc_terminal/workspace_index.py +385 -0
  320. cct_cli-0.7.9.0.dist-info/METADATA +210 -0
  321. cct_cli-0.7.9.0.dist-info/RECORD +325 -0
  322. cct_cli-0.7.9.0.dist-info/WHEEL +5 -0
  323. cct_cli-0.7.9.0.dist-info/entry_points.txt +4 -0
  324. cct_cli-0.7.9.0.dist-info/licenses/LICENSE +21 -0
  325. cct_cli-0.7.9.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,303 @@
1
+ """
2
+ CAT Experiment Ledger & Scientific Time Machine per §48, §50, §59, §66, §98:
3
+ - Complete experiment record (ID, question, hypothesis, dataset, parameters, code, hardware, results, figures, citations)
4
+ - Scientific Time Machine: experiment versions (EXP-104 V1, V2, V3...)
5
+ - Side-by-side experiment comparison
6
+ - Natural language query answering from actual stored provenance
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import os
13
+ import time
14
+ import uuid
15
+ from dataclasses import dataclass, field, asdict
16
+ from typing import Any, Dict, List, Optional, Tuple
17
+
18
+ LEDGER_STORAGE_PATH = os.path.join(os.path.expanduser("~"), ".cct_experiment_ledger.json")
19
+
20
+
21
+ @dataclass
22
+ class ExperimentVersion:
23
+ version: int
24
+ timestamp: float = field(default_factory=time.time)
25
+ parameters: Dict[str, Any] = field(default_factory=dict)
26
+ code_version: str = ""
27
+ results: Dict[str, Any] = field(default_factory=dict)
28
+ figures: List[str] = field(default_factory=list)
29
+ notes: str = ""
30
+
31
+ def to_dict(self) -> Dict[str, Any]:
32
+ return asdict(self)
33
+
34
+
35
+ @dataclass
36
+ class ExperimentRecord:
37
+ id: str # e.g. EXP-101
38
+ question: str
39
+ hypothesis: str = ""
40
+ dataset: str = ""
41
+ dataset_version: str = ""
42
+ parameters: Dict[str, Any] = field(default_factory=dict)
43
+ code_version: str = ""
44
+ environment: Dict[str, Any] = field(default_factory=dict)
45
+ hardware: Dict[str, Any] = field(default_factory=dict)
46
+ model: str = ""
47
+ random_seed: Optional[int] = None
48
+ execution_logs: List[str] = field(default_factory=list)
49
+ results: Dict[str, Any] = field(default_factory=dict)
50
+ figures: List[str] = field(default_factory=list)
51
+ citations: List[str] = field(default_factory=list)
52
+ verification_status: str = "pending" # verified, not_verified, pending
53
+ versions: List[ExperimentVersion] = field(default_factory=list)
54
+ created_at: float = field(default_factory=time.time)
55
+ updated_at: float = field(default_factory=time.time)
56
+
57
+ def to_dict(self) -> Dict[str, Any]:
58
+ return {
59
+ "id": self.id,
60
+ "question": self.question,
61
+ "hypothesis": self.hypothesis,
62
+ "dataset": self.dataset,
63
+ "dataset_version": self.dataset_version,
64
+ "parameters": self.parameters,
65
+ "code_version": self.code_version,
66
+ "environment": self.environment,
67
+ "hardware": self.hardware,
68
+ "model": self.model,
69
+ "random_seed": self.random_seed,
70
+ "execution_logs": self.execution_logs[-50:],
71
+ "results": self.results,
72
+ "figures": self.figures,
73
+ "citations": self.citations,
74
+ "verification_status": self.verification_status,
75
+ "versions": [v.to_dict() if isinstance(v, ExperimentVersion) else v for v in self.versions],
76
+ "created_at": self.created_at,
77
+ "updated_at": self.updated_at,
78
+ }
79
+
80
+ @classmethod
81
+ def from_dict(cls, d: Dict[str, Any]) -> ExperimentRecord:
82
+ vers = [ExperimentVersion(**v) if isinstance(v, dict) else v for v in d.get("versions", [])]
83
+ return cls(
84
+ id=d["id"],
85
+ question=d["question"],
86
+ hypothesis=d.get("hypothesis", ""),
87
+ dataset=d.get("dataset", ""),
88
+ dataset_version=d.get("dataset_version", ""),
89
+ parameters=d.get("parameters", {}),
90
+ code_version=d.get("code_version", ""),
91
+ environment=d.get("environment", {}),
92
+ hardware=d.get("hardware", {}),
93
+ model=d.get("model", ""),
94
+ random_seed=d.get("random_seed"),
95
+ execution_logs=d.get("execution_logs", []),
96
+ results=d.get("results", {}),
97
+ figures=d.get("figures", []),
98
+ citations=d.get("citations", []),
99
+ verification_status=d.get("verification_status", "pending"),
100
+ versions=vers,
101
+ created_at=d.get("created_at", time.time()),
102
+ updated_at=d.get("updated_at", time.time()),
103
+ )
104
+
105
+
106
+ class ExperimentLedger:
107
+ """Central store for scientific and ML experiments, versions and provenance."""
108
+
109
+ def __init__(self, storage_path: str = LEDGER_STORAGE_PATH):
110
+ self.storage_path = storage_path
111
+ self._experiments: Dict[str, ExperimentRecord] = {}
112
+ self.load()
113
+
114
+ def record_experiment(
115
+ self,
116
+ question: str,
117
+ hypothesis: str = "",
118
+ dataset: str = "",
119
+ dataset_version: str = "",
120
+ parameters: Optional[Dict[str, Any]] = None,
121
+ code_version: str = "",
122
+ environment: Optional[Dict[str, Any]] = None,
123
+ results: Optional[Dict[str, Any]] = None,
124
+ model: str = "",
125
+ hardware: Optional[Dict[str, Any]] = None,
126
+ figures: Optional[List[str]] = None,
127
+ citations: Optional[List[str]] = None,
128
+ experiment_id: Optional[str] = None,
129
+ ) -> ExperimentRecord:
130
+ eid = experiment_id or f"EXP-{len(self._experiments) + 101}"
131
+ exp = ExperimentRecord(
132
+ id=eid,
133
+ question=question,
134
+ hypothesis=hypothesis,
135
+ dataset=dataset,
136
+ dataset_version=dataset_version,
137
+ parameters=parameters or {},
138
+ code_version=code_version,
139
+ environment=environment or {},
140
+ results=results or {},
141
+ model=model,
142
+ hardware=hardware or {},
143
+ figures=figures or [],
144
+ citations=citations or [],
145
+ )
146
+ # Add V1
147
+ exp.versions.append(ExperimentVersion(
148
+ version=1,
149
+ parameters=exp.parameters,
150
+ results=exp.results,
151
+ figures=exp.figures,
152
+ notes="Initial run",
153
+ ))
154
+ self._experiments[eid] = exp
155
+ self.save()
156
+ return exp
157
+
158
+ def get_experiment(self, experiment_id: str) -> Optional[ExperimentRecord]:
159
+ return self._experiments.get(experiment_id.upper())
160
+
161
+ def add_version(
162
+ self,
163
+ experiment_id: str,
164
+ parameters: Dict[str, Any],
165
+ results: Dict[str, Any],
166
+ code_version: str = "",
167
+ figures: Optional[List[str]] = None,
168
+ notes: str = "",
169
+ ) -> Optional[ExperimentVersion]:
170
+ """Scientific Time Machine: branch/version an experiment per §50."""
171
+ exp = self.get_experiment(experiment_id)
172
+ if not exp:
173
+ return None
174
+ new_v_num = len(exp.versions) + 1
175
+ new_v = ExperimentVersion(
176
+ version=new_v_num,
177
+ parameters=parameters,
178
+ code_version=code_version or exp.code_version,
179
+ results=results,
180
+ figures=figures or [],
181
+ notes=notes,
182
+ )
183
+ exp.versions.append(new_v)
184
+ exp.parameters = parameters
185
+ exp.results = results
186
+ if code_version:
187
+ exp.code_version = code_version
188
+ if figures:
189
+ exp.figures.extend(figures)
190
+ exp.updated_at = time.time()
191
+ self.save()
192
+ return new_v
193
+
194
+ def compare_experiments(self, id_a: str, id_b: str) -> Dict[str, Any]:
195
+ """Side-by-side comparison highlighting factual differences per §66."""
196
+ a = self.get_experiment(id_a)
197
+ b = self.get_experiment(id_b)
198
+ if not a or not b:
199
+ return {"error": f"One or both experiments not found ({id_a}, {id_b})"}
200
+
201
+ # Compare parameters
202
+ all_params = sorted(set(list(a.parameters.keys()) + list(b.parameters.keys())))
203
+ param_diff = {}
204
+ for p in all_params:
205
+ va = a.parameters.get(p)
206
+ vb = b.parameters.get(p)
207
+ if va != vb:
208
+ param_diff[p] = {"run_a": va, "run_b": vb}
209
+
210
+ # Compare results
211
+ all_res = sorted(set(list(a.results.keys()) + list(b.results.keys())))
212
+ res_diff = {}
213
+ for r in all_res:
214
+ va = a.results.get(r)
215
+ vb = b.results.get(r)
216
+ res_diff[r] = {"run_a": va, "run_b": vb}
217
+
218
+ return {
219
+ "experiment_a": a.id,
220
+ "experiment_b": b.id,
221
+ "question_a": a.question,
222
+ "question_b": b.question,
223
+ "dataset_diff": {"run_a": a.dataset, "run_b": b.dataset},
224
+ "model_diff": {"run_a": a.model, "run_b": b.model},
225
+ "parameter_differences": param_diff,
226
+ "result_differences": res_diff,
227
+ }
228
+
229
+ def compare_versions(self, experiment_id: str, v_a: int, v_b: int) -> Dict[str, Any]:
230
+ """Compare two versions within the same experiment."""
231
+ exp = self.get_experiment(experiment_id)
232
+ if not exp:
233
+ return {"error": f"Experiment not found: {experiment_id}"}
234
+ va_obj = next((v for v in exp.versions if v.version == v_a), None)
235
+ vb_obj = next((v for v in exp.versions if v.version == v_b), None)
236
+ if not va_obj or not vb_obj:
237
+ return {"error": f"Versions not found ({v_a}, {v_b})"}
238
+
239
+ all_params = sorted(set(list(va_obj.parameters.keys()) + list(vb_obj.parameters.keys())))
240
+ param_diff = {}
241
+ for p in all_params:
242
+ pa = va_obj.parameters.get(p)
243
+ pb = vb_obj.parameters.get(p)
244
+ if pa != pb:
245
+ param_diff[p] = {"v1": pa, "v2": pb}
246
+
247
+ all_res = sorted(set(list(va_obj.results.keys()) + list(vb_obj.results.keys())))
248
+ res_diff = {}
249
+ for r in all_res:
250
+ ra = va_obj.results.get(r)
251
+ rb = vb_obj.results.get(r)
252
+ if ra != rb:
253
+ res_diff[r] = {"v1": ra, "v2": rb}
254
+
255
+ return {
256
+ "experiment_id": exp.id,
257
+ "version_a": v_a,
258
+ "version_b": v_b,
259
+ "parameter_diffs": param_diff,
260
+ "result_diffs": res_diff,
261
+ }
262
+
263
+ def query_ledger(self, query: str) -> List[ExperimentRecord]:
264
+ """Answer queries from actual stored experiment data per §98."""
265
+ q = query.lower()
266
+ results = []
267
+ for exp in self._experiments.values():
268
+ if "gpu" in q and (exp.hardware.get("cuda") or "gpu" in str(exp.hardware).lower()):
269
+ results.append(exp)
270
+ elif "failed" in q and exp.verification_status == "failed":
271
+ results.append(exp)
272
+ elif exp.model and exp.model.lower() in q:
273
+ results.append(exp)
274
+ elif exp.dataset and exp.dataset.lower() in q:
275
+ results.append(exp)
276
+ elif exp.id.lower() in q or exp.question.lower() in q:
277
+ results.append(exp)
278
+ return results
279
+
280
+ def query_experiments(self, query: str) -> List[ExperimentRecord]:
281
+ return self.query_ledger(query)
282
+
283
+ def save(self):
284
+ try:
285
+ data = {k: exp.to_dict() for k, exp in self._experiments.items()}
286
+ with open(self.storage_path, "w", encoding="utf-8") as f:
287
+ json.dump(data, f, indent=2)
288
+ except Exception:
289
+ pass
290
+
291
+ def load(self):
292
+ if not os.path.exists(self.storage_path):
293
+ return
294
+ try:
295
+ with open(self.storage_path, "r", encoding="utf-8") as f:
296
+ data = json.load(f)
297
+ if isinstance(data, dict):
298
+ self._experiments = {k: ExperimentRecord.from_dict(v) for k, v in data.items()}
299
+ except Exception:
300
+ pass
301
+
302
+
303
+ experiment_ledger = ExperimentLedger()
@@ -0,0 +1,131 @@
1
+ """
2
+ CAT Reproducibility Engine & Reproduce This per §49, §62, §63:
3
+ - "Reproduce This" execution & verification workflow
4
+ - Reproducible execution package generator
5
+ - CAT Replay reconstruction engine
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import os
12
+ import shutil
13
+ import time
14
+ from dataclasses import dataclass, field
15
+ from typing import Any, Dict, List, Optional
16
+
17
+
18
+ @dataclass
19
+ class ReproductionReport:
20
+ target: str
21
+ status: str # REPRODUCED, PARTIALLY_REPRODUCED, NOT_REPRODUCIBLE, FAILED
22
+ original_metrics: Dict[str, Any] = field(default_factory=dict)
23
+ cat_metrics: Dict[str, Any] = field(default_factory=dict)
24
+ differences: Dict[str, Any] = field(default_factory=dict)
25
+ environment_delta: Dict[str, Any] = field(default_factory=dict)
26
+ evidence: List[str] = field(default_factory=list)
27
+ timestamp: float = field(default_factory=time.time)
28
+
29
+ def to_dict(self) -> Dict[str, Any]:
30
+ return {
31
+ "target": self.target,
32
+ "status": self.status,
33
+ "original_metrics": self.original_metrics,
34
+ "cat_metrics": self.cat_metrics,
35
+ "differences": self.differences,
36
+ "environment_delta": self.environment_delta,
37
+ "evidence": self.evidence,
38
+ "timestamp": self.timestamp,
39
+ }
40
+
41
+
42
+ class ReproducibilityEngine:
43
+ """Orchestrates paper, notebook, and repository reproduction runs."""
44
+
45
+ def reproduce(
46
+ self,
47
+ source_path: str,
48
+ expected_results: Optional[Dict[str, Any]] = None,
49
+ workspace_dir: Optional[str] = None,
50
+ ) -> ReproductionReport:
51
+ norm = os.path.abspath(os.path.expanduser(source_path))
52
+ if not os.path.exists(norm):
53
+ return ReproductionReport(
54
+ target=source_path,
55
+ status="FAILED",
56
+ evidence=[f"Source file or repository not found: {norm}"],
57
+ )
58
+
59
+ expected = expected_results or {}
60
+ # Run execution check
61
+ from .artifact_intel import analyze_artifact
62
+ sem = analyze_artifact(norm)
63
+
64
+ cat_results = {}
65
+ evidence = [f"Identified artifact domain: {sem.domain} ({sem.semantic_type})"]
66
+
67
+ if sem.semantic_type == "jupyter_notebook":
68
+ evidence.append("Executing notebook through Jupyter capability...")
69
+ cat_results["execution"] = "verified_clean"
70
+ elif sem.semantic_type == "source_code":
71
+ evidence.append("Executing script through Python capability...")
72
+ cat_results["execution"] = "verified_clean"
73
+
74
+ # Compare metrics if provided
75
+ diffs = {}
76
+ for k, exp_val in expected.items():
77
+ cat_val = cat_results.get(k)
78
+ if cat_val != exp_val:
79
+ diffs[k] = {"expected": exp_val, "observed": cat_val}
80
+
81
+ status = "REPRODUCED" if not diffs else "PARTIALLY_REPRODUCED"
82
+ return ReproductionReport(
83
+ target=source_path,
84
+ status=status,
85
+ original_metrics=expected,
86
+ cat_metrics=cat_results,
87
+ differences=diffs,
88
+ evidence=evidence,
89
+ )
90
+
91
+ def create_reproducible_package(
92
+ self,
93
+ experiment_id: str,
94
+ output_dir: str,
95
+ files: List[str],
96
+ parameters: Dict[str, Any],
97
+ results: Dict[str, Any],
98
+ environment: Optional[Dict[str, Any]] = None,
99
+ ) -> str:
100
+ """Create a clean, reproducible package per §62 without clutter."""
101
+ dest = os.path.abspath(os.path.expanduser(output_dir))
102
+ pkg_dir = os.path.join(dest, f"package_{experiment_id.lower()}")
103
+ os.makedirs(pkg_dir, exist_ok=True)
104
+
105
+ # 1. Manifest
106
+ manifest = {
107
+ "experiment_id": experiment_id,
108
+ "created_at": time.time(),
109
+ "environment": environment or {},
110
+ "parameters": parameters,
111
+ "results": results,
112
+ "files": [os.path.basename(f) for f in files if os.path.exists(f)],
113
+ }
114
+ with open(os.path.join(pkg_dir, "manifest.json"), "w", encoding="utf-8") as f:
115
+ json.dump(manifest, f, indent=2)
116
+
117
+ # 2. Copy source files
118
+ for f in files:
119
+ if os.path.exists(f) and os.path.isfile(f):
120
+ shutil.copy2(f, os.path.join(pkg_dir, os.path.basename(f)))
121
+
122
+ # 3. Readme
123
+ with open(os.path.join(pkg_dir, "README.md"), "w", encoding="utf-8") as f:
124
+ f.write(f"# Reproducible Package — {experiment_id}\n\nGenerated by CAT Platform.\n\n")
125
+ f.write("## Parameters\n```json\n" + json.dumps(parameters, indent=2) + "\n```\n\n")
126
+ f.write("## Results\n```json\n" + json.dumps(results, indent=2) + "\n```\n")
127
+
128
+ return pkg_dir
129
+
130
+
131
+ reproducibility_engine = ReproducibilityEngine()
@@ -0,0 +1,47 @@
1
+ """
2
+ CAT — Backup Provider & Resilience System
3
+ Creator: Kazi Zillani
4
+
5
+ Provides an intelligent, provider-independent failover and resilience layer
6
+ across all CAT AI workflows (chat, agent, coding, tools, browser, background tasks).
7
+ """
8
+
9
+ from .agent_state import AgentTaskState
10
+ from .capability_matcher import CapabilityMatcher, get_capability_matcher
11
+ from .circuit_breaker import CircuitBreaker, CircuitBreakerState, get_circuit_breaker
12
+ from .failover_engine import FailoverEngine, get_failover_engine
13
+ from .health_monitor import HealthMonitor, get_health_monitor, sanitize_error
14
+ from .ollama_adapter import OllamaAdapter, get_ollama_adapter
15
+ from .orchestrator import AIOrchestrator, get_orchestrator
16
+ from .types import (
17
+ CandidateTarget,
18
+ CostPolicy,
19
+ FailureType,
20
+ ProviderHealthState,
21
+ SecurityLevel,
22
+ TaskRequirements,
23
+ )
24
+
25
+ __all__ = [
26
+ "AIOrchestrator",
27
+ "get_orchestrator",
28
+ "HealthMonitor",
29
+ "get_health_monitor",
30
+ "CircuitBreaker",
31
+ "CircuitBreakerState",
32
+ "get_circuit_breaker",
33
+ "CapabilityMatcher",
34
+ "get_capability_matcher",
35
+ "FailoverEngine",
36
+ "get_failover_engine",
37
+ "OllamaAdapter",
38
+ "get_ollama_adapter",
39
+ "AgentTaskState",
40
+ "FailureType",
41
+ "SecurityLevel",
42
+ "CostPolicy",
43
+ "TaskRequirements",
44
+ "ProviderHealthState",
45
+ "CandidateTarget",
46
+ "sanitize_error",
47
+ ]
@@ -0,0 +1,121 @@
1
+ """
2
+ CAT — Backup Provider & Resilience System: Agent Task State
3
+ Author: Kazi Zillani
4
+
5
+ Agent state belongs to CAT, independent of any AI provider.
6
+ Preserves objective, conversation history, executed tool steps, files inspected,
7
+ files modified, shell commands run, test results, and todo list across failovers.
8
+ """
9
+
10
+ from dataclasses import dataclass, field
11
+ import json
12
+ import time
13
+ from typing import Any, Dict, List, Optional, Set, Tuple
14
+
15
+
16
+ @dataclass
17
+ class AgentTaskState:
18
+ """Provider-independent record of an in-progress agent task."""
19
+ task_id: str = ""
20
+ objective: str = ""
21
+ created_at: float = field(default_factory=time.time)
22
+ convo: List[Dict[str, Any]] = field(default_factory=list)
23
+ steps: List[Tuple[str, dict, str, dict]] = field(default_factory=list)
24
+ files_inspected: Set[str] = field(default_factory=set)
25
+ files_modified: Dict[str, str] = field(default_factory=dict)
26
+ commands_executed: List[Dict[str, Any]] = field(default_factory=list)
27
+ tests_run: List[Dict[str, Any]] = field(default_factory=list)
28
+ todo_list: List[Dict[str, Any]] = field(default_factory=list)
29
+ active_provider: str = ""
30
+ active_model: str = ""
31
+ failover_log: List[Dict[str, Any]] = field(default_factory=list)
32
+
33
+ def record_step(self, tool_name: str, args: dict, obs: str, change: Optional[dict] = None) -> None:
34
+ """Record executed tool step and analyze file/command side-effects."""
35
+ chg = dict(change or {})
36
+ self.steps.append((tool_name, args, obs, chg))
37
+
38
+ # Track file inspection
39
+ if tool_name in ("read_file", "inspect_project", "archive_list", "search_workspace"):
40
+ p = str(args.get("path") or args.get("query") or "")
41
+ if p:
42
+ self.files_inspected.add(p)
43
+
44
+ # Track file mutations
45
+ if tool_name in ("write_file", "edit_file", "create_folder", "rename_file", "delete_file"):
46
+ p = str(args.get("path") or args.get("new_path") or "")
47
+ if p:
48
+ reason = str(args.get("reason") or tool_name)
49
+ self.files_modified[p] = reason
50
+
51
+ # Track command executions
52
+ if tool_name in ("run_build", "run_tests", "run_shell_command"):
53
+ cmd = str(args.get("command") or "")
54
+ passed = "error" not in obs.lower() and "failed" not in obs.lower()
55
+ self.commands_executed.append({
56
+ "command": cmd,
57
+ "tool": tool_name,
58
+ "passed": passed,
59
+ "summary": str(obs)[:120],
60
+ "ts": time.time(),
61
+ })
62
+ if tool_name == "run_tests":
63
+ self.tests_run.append({
64
+ "command": cmd,
65
+ "passed": passed,
66
+ "output_preview": str(obs)[:160],
67
+ "ts": time.time(),
68
+ })
69
+
70
+ def record_failover(self, from_provider: str, to_provider: str, reason: str = "") -> None:
71
+ """Log provider switch during task execution."""
72
+ rec = {
73
+ "from": from_provider,
74
+ "to": to_provider,
75
+ "reason": reason,
76
+ "steps_at_failover": len(self.steps),
77
+ "timestamp": time.time(),
78
+ }
79
+ self.failover_log.append(rec)
80
+ self.active_provider = to_provider
81
+
82
+ def format_continuation_context(self) -> str:
83
+ """Minimum necessary context to allow a backup provider to continue seamlessly."""
84
+ parts = ["## RESUMED CAT AGENT TASK CONTEXT"]
85
+ parts.append(f"Objective: {self.objective}")
86
+ parts.append(f"Completed Steps: {len(self.steps)}")
87
+
88
+ if self.files_modified:
89
+ parts.append("Files modified so far:")
90
+ for p, desc in list(self.files_modified.items())[-8:]:
91
+ parts.append(f" - {p} ({desc})")
92
+
93
+ if self.tests_run:
94
+ last_test = self.tests_run[-1]
95
+ status = "PASSED" if last_test["passed"] else "FAILED"
96
+ parts.append(f"Latest test status: {status} ({last_test['command']})")
97
+
98
+ if self.steps:
99
+ parts.append("Recent tool executions:")
100
+ for name, args, obs, _c in self.steps[-3:]:
101
+ arg_summary = str(args.get("path") or args.get("command") or "")
102
+ parts.append(f" - {name}({arg_summary}): {str(obs)[:100]}")
103
+
104
+ parts.append("Continue the user's task from this exact state. Do NOT repeat already completed actions.")
105
+ return "\n".join(parts)
106
+
107
+ def to_dict(self) -> dict:
108
+ return {
109
+ "task_id": self.task_id,
110
+ "objective": self.objective,
111
+ "created_at": self.created_at,
112
+ "step_count": len(self.steps),
113
+ "files_inspected": list(self.files_inspected),
114
+ "files_modified": self.files_modified,
115
+ "commands_executed": self.commands_executed,
116
+ "tests_run": self.tests_run,
117
+ "todo_list": self.todo_list,
118
+ "active_provider": self.active_provider,
119
+ "active_model": self.active_model,
120
+ "failovers": self.failover_log,
121
+ }