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,134 @@
1
+ """
2
+ CAT — Backup Provider & Resilience System: Core Types & Enums
3
+ Author: Kazi Zillani
4
+
5
+ Defines failure categories, security levels, cost policies, task requirements,
6
+ and health state contracts for CAT's unified AI Orchestrator.
7
+ """
8
+
9
+ from dataclasses import dataclass, field
10
+ from enum import Enum
11
+ from typing import Any, Dict, List, Optional, Set
12
+
13
+
14
+ class FailureType(str, Enum):
15
+ """Normalized taxonomy of provider and model failure modes."""
16
+ TRANSIENT = "transient" # Network blip, reset, 502/503 -> retryable
17
+ TIMEOUT = "timeout" # Total or idle read timeout -> retryable/failover
18
+ RATE_LIMIT = "rate_limit" # 429, request cap exceeded -> immediate failover
19
+ QUOTA_EXHAUSTED = "quota_exhausted" # Insufficient funds/credits -> immediate failover
20
+ AUTH_FAILURE = "auth_failure" # 401, 403, invalid key -> immediate failover
21
+ MODEL_UNAVAILABLE = "model_unavailable" # 404, not loaded, deprecated -> model fallback
22
+ CONTEXT_TOO_LARGE = "context_too_large" # Token overflow -> high-ctx model fallback
23
+ TOOL_CALLING_FAILURE = "tool_calling_failure" # Invalid function-calling or parsing collapse
24
+ CAPABILITY_MISMATCH = "capability_mismatch" # Missing vision/tools/coding -> skip target
25
+ PROVIDER_OUTAGE = "provider_outage" # Host completely unreachable / 500 storm
26
+ SAFETY_RESTRICTION = "safety_restriction" # Refusal or safety block -> fallback
27
+ SERVER_ERROR = "server_error" # Internal 500 from provider
28
+ UNKNOWN = "unknown"
29
+
30
+
31
+ class SecurityLevel(str, Enum):
32
+ """Configurable security modes for automatic failover."""
33
+ STRICT = "strict" # Only explicitly approved backups, no auto-switch without permission
34
+ BALANCED = "balanced" # Configured backups may be used automatically; strict privacy rules
35
+ AUTOMATIC = "automatic" # CAT may select any enabled compatible provider adhering to privacy
36
+
37
+
38
+ class CostPolicy(str, Enum):
39
+ """Cost-aware fallback constraints."""
40
+ ANY = "any" # Any available provider
41
+ FREE_ONLY = "free_only" # Strictly free providers (e.g. free tiers, Ollama local)
42
+ LOCAL_ONLY = "local_only" # Strictly zero-cost local hardware inference
43
+
44
+
45
+ @dataclass
46
+ class TaskRequirements:
47
+ """Explicit capability requirements for a task turn."""
48
+ vision: bool = False
49
+ tools: bool = False
50
+ coding: bool = False
51
+ reasoning: bool = False
52
+ long_context: bool = False
53
+ min_context: int = 0
54
+ structured_output: bool = False
55
+ audio: bool = False
56
+ preferred_provider: Optional[str] = None
57
+ preferred_model: Optional[str] = None
58
+
59
+ def matches(self, caps: Any) -> bool:
60
+ """Check whether a ModelCapabilities object or dict satisfies this task."""
61
+ if isinstance(caps, dict):
62
+ if self.vision and not caps.get("vision", False):
63
+ return False
64
+ if self.tools and not caps.get("tools", True):
65
+ return False
66
+ if self.min_context and caps.get("context_window", 32000) < self.min_context:
67
+ return False
68
+ if self.long_context and caps.get("context_window", 32000) < 60000:
69
+ return False
70
+ return True
71
+
72
+ # Assume ModelCapabilities dataclass
73
+ if self.vision and not getattr(caps, "vision", False):
74
+ return False
75
+ if self.tools and not getattr(caps, "tools", True):
76
+ return False
77
+ ctx = getattr(caps, "context_window", 32000)
78
+ if self.min_context and ctx < self.min_context:
79
+ return False
80
+ if self.long_context and ctx < 60000:
81
+ return False
82
+ return True
83
+
84
+
85
+ @dataclass
86
+ class ProviderHealthState:
87
+ """Real-time observed health metrics for a provider/model endpoint."""
88
+ provider: str
89
+ model: str
90
+ status: str = "disconnected" # healthy, slow, rate_limited, offline, local, disconnected
91
+ latency_ms: Optional[float] = None
92
+ consecutive_failures: int = 0
93
+ circuit_open: bool = False
94
+ cooldown_until: float = 0.0
95
+ last_error: str = ""
96
+ last_success: float = 0.0
97
+ last_checked: float = 0.0
98
+ success_count: int = 0
99
+ failure_count: int = 0
100
+
101
+ @property
102
+ def is_healthy(self) -> bool:
103
+ import time
104
+ if self.circuit_open and time.time() < self.cooldown_until:
105
+ return False
106
+ return self.status in ("healthy", "local", "disconnected", "slow")
107
+
108
+ def to_dict(self) -> dict:
109
+ return {
110
+ "provider": self.provider,
111
+ "model": self.model,
112
+ "status": self.status,
113
+ "latency_ms": round(self.latency_ms, 1) if self.latency_ms is not None else None,
114
+ "consecutive_failures": self.consecutive_failures,
115
+ "circuit_open": self.circuit_open,
116
+ "cooldown_until": self.cooldown_until,
117
+ "last_error": self.last_error,
118
+ "last_success": self.last_success,
119
+ "success_count": self.success_count,
120
+ "failure_count": self.failure_count,
121
+ }
122
+
123
+
124
+ @dataclass
125
+ class CandidateTarget:
126
+ """A scored, capability-checked candidate provider/model for failover."""
127
+ provider: str
128
+ model: str
129
+ config: dict
130
+ entry: Optional[dict] = None
131
+ is_backup: bool = False
132
+ is_local: bool = False
133
+ score: float = 0.0
134
+ tier: int = 1 # 1 = same-provider fallback model, 2 = cross-provider backup
@@ -0,0 +1,98 @@
1
+ """
2
+ CCT Sandbox — runs /codepad Python in a subprocess with real OS resource
3
+ limits, not just a timeout. On POSIX (Linux/macOS) this sets hard
4
+ RLIMIT_CPU, RLIMIT_AS (address space) and RLIMIT_NPROC via a
5
+ `preexec_fn`, so a runaway loop or a fork-bomb attempt genuinely gets
6
+ killed by the kernel rather than merely by an outer wall-clock timeout.
7
+
8
+ On Windows there's no equivalent rlimit API, so this honestly falls
9
+ back to timeout-only isolation (same as before) and says so — it does
10
+ not pretend to sandbox on a platform where it structurally can't.
11
+ """
12
+
13
+ import os
14
+ import subprocess
15
+ import sys
16
+ import tempfile
17
+
18
+ CPU_SECONDS = 10
19
+ MEMORY_BYTES = 256 * 1024 * 1024 # 256MB
20
+ MAX_PROCESSES = 16
21
+ WALL_TIMEOUT = 15
22
+
23
+ _IS_POSIX = os.name == "posix"
24
+
25
+ if _IS_POSIX:
26
+ import resource
27
+
28
+ def _limit_resources():
29
+ try:
30
+ resource.setrlimit(resource.RLIMIT_CPU, (CPU_SECONDS, CPU_SECONDS))
31
+ except Exception:
32
+ pass
33
+ try:
34
+ resource.setrlimit(resource.RLIMIT_AS, (MEMORY_BYTES, MEMORY_BYTES))
35
+ except Exception:
36
+ pass
37
+ try:
38
+ resource.setrlimit(resource.RLIMIT_NPROC, (MAX_PROCESSES, MAX_PROCESSES))
39
+ except Exception:
40
+ pass
41
+ else:
42
+ _limit_resources = None
43
+
44
+
45
+ def sandbox_available():
46
+ return _IS_POSIX
47
+
48
+
49
+ def run_sandboxed(code, timeout=WALL_TIMEOUT):
50
+ """Runs `code` as a standalone Python script in a subprocess. Returns
51
+ a dict: {stdout, stderr, returncode, timed_out, killed_by_limit,
52
+ sandboxed}. Never raises."""
53
+ tmp_path = None
54
+ try:
55
+ with tempfile.NamedTemporaryFile("w", suffix=".py", delete=False, encoding="utf-8") as tmp:
56
+ tmp.write(code)
57
+ tmp_path = tmp.name
58
+
59
+ kwargs = dict(capture_output=True, text=True, timeout=timeout)
60
+ if _IS_POSIX:
61
+ kwargs["preexec_fn"] = _limit_resources
62
+ result = subprocess.run([sys.executable, tmp_path], **kwargs)
63
+
64
+ killed_by_limit = _IS_POSIX and result.returncode < 0
65
+ return {
66
+ "stdout": result.stdout,
67
+ "stderr": result.stderr,
68
+ "returncode": result.returncode,
69
+ "timed_out": False,
70
+ "killed_by_limit": killed_by_limit,
71
+ "sandboxed": _IS_POSIX,
72
+ }
73
+ except subprocess.TimeoutExpired as e:
74
+ return {
75
+ "stdout": e.stdout or "",
76
+ "stderr": (e.stderr or "") + f"\n[sandbox] killed after {timeout}s wall-clock timeout.",
77
+ "returncode": -1,
78
+ "timed_out": True,
79
+ "killed_by_limit": False,
80
+ "sandboxed": _IS_POSIX,
81
+ }
82
+ except Exception as e:
83
+ return {
84
+ "stdout": "", "stderr": f"[sandbox] could not run: {e}",
85
+ "returncode": -1, "timed_out": False, "killed_by_limit": False,
86
+ "sandboxed": _IS_POSIX,
87
+ }
88
+ finally:
89
+ if tmp_path:
90
+ try:
91
+ os.unlink(tmp_path)
92
+ except Exception:
93
+ pass
94
+ try:
95
+ from . import terminal_identity
96
+ terminal_identity.set_terminal_title()
97
+ except Exception:
98
+ pass