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.
- calc_terminal/__init__.py +14 -0
- calc_terminal/__main__.py +14 -0
- calc_terminal/activity.py +1334 -0
- calc_terminal/agent.py +3387 -0
- calc_terminal/agent_runtime.py +519 -0
- calc_terminal/ai_context.py +447 -0
- calc_terminal/ai_modes.py +752 -0
- calc_terminal/ai_personalization.py +286 -0
- calc_terminal/ai_preview_feedback.py +213 -0
- calc_terminal/aicore.py +2572 -0
- calc_terminal/anim.py +367 -0
- calc_terminal/app.py +3685 -0
- calc_terminal/art.py +639 -0
- calc_terminal/atomsim.py +368 -0
- calc_terminal/attachments.py +743 -0
- calc_terminal/benchmark_system.py +414 -0
- calc_terminal/browser/__init__.py +36 -0
- calc_terminal/browser/browser_state.py +346 -0
- calc_terminal/browser/devserver.py +176 -0
- calc_terminal/browser/engine.py +494 -0
- calc_terminal/browser/navigation.py +84 -0
- calc_terminal/browser/preview.py +429 -0
- calc_terminal/browser/preview_entry.py +95 -0
- calc_terminal/browser/project_detector.py +144 -0
- calc_terminal/browser/server.py +449 -0
- calc_terminal/browser/state.py +75 -0
- calc_terminal/browser/watcher.py +99 -0
- calc_terminal/browser_gui/__init__.py +1 -0
- calc_terminal/browser_gui/__main__.py +3 -0
- calc_terminal/browser_gui/launcher.py +173 -0
- calc_terminal/browser_gui/playwright_browser.py +117 -0
- calc_terminal/browser_gui/qt_browser.py +1501 -0
- calc_terminal/browser_gui/webview_browser.py +57 -0
- calc_terminal/capabilities/__init__.py +35 -0
- calc_terminal/capabilities/adapters/__init__.py +33 -0
- calc_terminal/capabilities/adapters/bioinformatics.py +204 -0
- calc_terminal/capabilities/adapters/browser_adapter.py +205 -0
- calc_terminal/capabilities/adapters/filesystem.py +206 -0
- calc_terminal/capabilities/adapters/git_adapter.py +202 -0
- calc_terminal/capabilities/adapters/jupyter_adapter.py +138 -0
- calc_terminal/capabilities/adapters/ml_frameworks.py +158 -0
- calc_terminal/capabilities/adapters/platforms.py +200 -0
- calc_terminal/capabilities/adapters/python_exec.py +93 -0
- calc_terminal/capabilities/adapters/quantum_adapter.py +150 -0
- calc_terminal/capabilities/adapters/scientific_comp.py +123 -0
- calc_terminal/capabilities/adapters/structural_bio.py +161 -0
- calc_terminal/capabilities/adapters/terminal.py +99 -0
- calc_terminal/capabilities/bus.py +178 -0
- calc_terminal/capabilities/discovery.py +207 -0
- calc_terminal/capabilities/schema.py +221 -0
- calc_terminal/cat.ico +0 -0
- calc_terminal/cat_browser.py +2018 -0
- calc_terminal/chat_store.py +703 -0
- calc_terminal/cli.py +1178 -0
- calc_terminal/code_editor.py +640 -0
- calc_terminal/collaboration.py +723 -0
- calc_terminal/commands_data.py +139 -0
- calc_terminal/compatibility_engine.py +352 -0
- calc_terminal/compute/__init__.py +31 -0
- calc_terminal/compute/fabric.py +350 -0
- calc_terminal/config.py +227 -0
- calc_terminal/core/__init__.py +41 -0
- calc_terminal/core/checkpoint.py +156 -0
- calc_terminal/core/input/__init__.py +45 -0
- calc_terminal/core/mode_registry.py +300 -0
- calc_terminal/core/project_graph.py +172 -0
- calc_terminal/core/recovery.py +129 -0
- calc_terminal/core/security_layer.py +112 -0
- calc_terminal/core/task_graph.py +202 -0
- calc_terminal/core/unified_runtime.py +184 -0
- calc_terminal/core/verification.py +257 -0
- calc_terminal/customization.py +1566 -0
- calc_terminal/derivations.py +153 -0
- calc_terminal/device_control.py +263 -0
- calc_terminal/diagnostics/__init__.py +27 -0
- calc_terminal/diagnostics/doctor_engine.py +382 -0
- calc_terminal/diagnostics/self_test.py +247 -0
- calc_terminal/doctor.py +519 -0
- calc_terminal/easter_eggs.py +274 -0
- calc_terminal/editor/__init__.py +1 -0
- calc_terminal/editor/actions.py +263 -0
- calc_terminal/editor/commands.py +160 -0
- calc_terminal/editor/shortcuts.py +226 -0
- calc_terminal/engine.py +259 -0
- calc_terminal/errors.py +120 -0
- calc_terminal/event_stream.py +146 -0
- calc_terminal/eventbus.py +133 -0
- calc_terminal/extensions.py +733 -0
- calc_terminal/fallback_cli.py +1321 -0
- calc_terminal/first_run.py +265 -0
- calc_terminal/fomoji_auth.py +1043 -0
- calc_terminal/formulas.py +82 -0
- calc_terminal/fs_cache.py +121 -0
- calc_terminal/fs_watcher.py +277 -0
- calc_terminal/game.py +193 -0
- calc_terminal/gen1.py +5 -0
- calc_terminal/generators.py +245 -0
- calc_terminal/gestures/__init__.py +42 -0
- calc_terminal/gestures/bindings.py +175 -0
- calc_terminal/gestures/manager.py +477 -0
- calc_terminal/goodbye.py +363 -0
- calc_terminal/gpu3d.py +290 -0
- calc_terminal/graphs.py +358 -0
- calc_terminal/hardware_analyzer.py +440 -0
- calc_terminal/host/__init__.py +30 -0
- calc_terminal/host/browser_manager.py +187 -0
- calc_terminal/host/desktop.py +1386 -0
- calc_terminal/host/launcher.py +395 -0
- calc_terminal/host/terminal.py +279 -0
- calc_terminal/identity.py +216 -0
- calc_terminal/input/__init__.py +54 -0
- calc_terminal/input/capabilities.py +258 -0
- calc_terminal/input/focus.py +87 -0
- calc_terminal/input/gestures.py +64 -0
- calc_terminal/input/pointer.py +114 -0
- calc_terminal/input/touch.py +345 -0
- calc_terminal/keys.py +84 -0
- calc_terminal/live_automation.py +165 -0
- calc_terminal/mathtext.py +433 -0
- calc_terminal/mcp.py +386 -0
- calc_terminal/memory.py +337 -0
- calc_terminal/memory_v2.py +479 -0
- calc_terminal/metrics.py +333 -0
- calc_terminal/mode_detection.py +146 -0
- calc_terminal/model.py +2431 -0
- calc_terminal/model_router.py +665 -0
- calc_terminal/models/__init__.py +0 -0
- calc_terminal/models/active_state.py +187 -0
- calc_terminal/models/dynamic_registry.py +584 -0
- calc_terminal/models/manager.py +781 -0
- calc_terminal/models/model_metadata.json +3526 -0
- calc_terminal/models/profiles.py +194 -0
- calc_terminal/models/registry.py +265 -0
- calc_terminal/models/schema.py +197 -0
- calc_terminal/models/validator.py +287 -0
- calc_terminal/models/verification_engine.py +368 -0
- calc_terminal/native_picker.py +215 -0
- calc_terminal/ollama_catalog.py +279 -0
- calc_terminal/ollama_download.py +233 -0
- calc_terminal/orchestrator.py +304 -0
- calc_terminal/package_research.py +322 -0
- calc_terminal/packages.py +1024 -0
- calc_terminal/pc_specs.py +116 -0
- calc_terminal/permissions.py +334 -0
- calc_terminal/pet.py +106 -0
- calc_terminal/pipeline.py +505 -0
- calc_terminal/platform/__init__.py +491 -0
- calc_terminal/platform/desktop.py +491 -0
- calc_terminal/platform/web.py +781 -0
- calc_terminal/preview/__init__.py +1 -0
- calc_terminal/preview/dev_server.py +303 -0
- calc_terminal/preview/diagnostics.py +131 -0
- calc_terminal/preview/live_reload.py +66 -0
- calc_terminal/preview/manager.py +129 -0
- calc_terminal/project_stats.py +209 -0
- calc_terminal/projects.py +328 -0
- calc_terminal/providers/__init__.py +0 -0
- calc_terminal/providers/adapters/__init__.py +80 -0
- calc_terminal/providers/adapters/anthropic_adapter.py +127 -0
- calc_terminal/providers/adapters/base.py +106 -0
- calc_terminal/providers/adapters/chinese_adapters.py +420 -0
- calc_terminal/providers/adapters/gemini_adapter.py +101 -0
- calc_terminal/providers/adapters/ollama_adapter.py +83 -0
- calc_terminal/providers/adapters/openai_adapter.py +159 -0
- calc_terminal/providers/adapters/other_adapters.py +246 -0
- calc_terminal/providers/anthropic_provider.py +172 -0
- calc_terminal/providers/auto_update.py +416 -0
- calc_terminal/providers/base_provider.py +105 -0
- calc_terminal/providers/discovery_manager.py +207 -0
- calc_terminal/providers/gemini_provider.py +178 -0
- calc_terminal/providers/lifecycle.py +767 -0
- calc_terminal/providers/ollama_adapter.py +707 -0
- calc_terminal/providers/openai_provider.py +254 -0
- calc_terminal/providers/provider_manager.py +1827 -0
- calc_terminal/providers/providers.json +4075 -0
- calc_terminal/reactionsim.py +279 -0
- calc_terminal/registry.py +337 -0
- calc_terminal/report.py +162 -0
- calc_terminal/research/__init__.py +45 -0
- calc_terminal/research/artifact_intel.py +126 -0
- calc_terminal/research/data_lineage.py +123 -0
- calc_terminal/research/experiment_ledger.py +303 -0
- calc_terminal/research/reproducibility.py +131 -0
- calc_terminal/resilience/__init__.py +47 -0
- calc_terminal/resilience/agent_state.py +121 -0
- calc_terminal/resilience/capability_matcher.py +174 -0
- calc_terminal/resilience/circuit_breaker.py +158 -0
- calc_terminal/resilience/failover_engine.py +230 -0
- calc_terminal/resilience/health_monitor.py +192 -0
- calc_terminal/resilience/ollama_adapter.py +125 -0
- calc_terminal/resilience/orchestrator.py +312 -0
- calc_terminal/resilience/types.py +134 -0
- calc_terminal/sandbox.py +98 -0
- calc_terminal/scires.py +558 -0
- calc_terminal/security_scanner.py +126 -0
- calc_terminal/session.py +294 -0
- calc_terminal/sim3d.py +206 -0
- calc_terminal/solver.py +276 -0
- calc_terminal/sound.py +127 -0
- calc_terminal/task_reports.py +287 -0
- calc_terminal/terminal_host.py +201 -0
- calc_terminal/terminal_identity.py +411 -0
- calc_terminal/test_ai_mode_reliability.py +344 -0
- calc_terminal/test_browser.py +368 -0
- calc_terminal/test_code_editor_upgrade.py +485 -0
- calc_terminal/test_customization.py +1148 -0
- calc_terminal/test_customization_ui.py +612 -0
- calc_terminal/test_dynamic_registry.py +304 -0
- calc_terminal/test_extensions.py +436 -0
- calc_terminal/test_overhaul.py +557 -0
- calc_terminal/test_project_detect.py +255 -0
- calc_terminal/test_root_cause_fix.py +527 -0
- calc_terminal/test_stability.py +532 -0
- calc_terminal/test_terminal_identity.py +132 -0
- calc_terminal/test_v079_speed.py +460 -0
- calc_terminal/theme.py +1107 -0
- calc_terminal/timeline.py +139 -0
- calc_terminal/todos.py +246 -0
- calc_terminal/tool_call_normalizer.py +419 -0
- calc_terminal/tui.py +104 -0
- calc_terminal/ui/__init__.py +8 -0
- calc_terminal/ui/activity_panel.py +231 -0
- calc_terminal/ui/activity_stream_panel.py +238 -0
- calc_terminal/ui/animations.py +122 -0
- calc_terminal/ui/app.py +7271 -0
- calc_terminal/ui/attach_panel.py +597 -0
- calc_terminal/ui/attachments.py +424 -0
- calc_terminal/ui/backup_panel.py +810 -0
- calc_terminal/ui/browser_shell.py +887 -0
- calc_terminal/ui/cat_agent.py +357 -0
- calc_terminal/ui/chats_panel.py +899 -0
- calc_terminal/ui/command_palette.py +125 -0
- calc_terminal/ui/command_palette_modal.py +166 -0
- calc_terminal/ui/composer.py +1141 -0
- calc_terminal/ui/context_menu.py +197 -0
- calc_terminal/ui/conversation.py +1435 -0
- calc_terminal/ui/customization_panel.py +1229 -0
- calc_terminal/ui/dashboard.py +404 -0
- calc_terminal/ui/design_system.py +557 -0
- calc_terminal/ui/diff_panel.py +213 -0
- calc_terminal/ui/editor.py +2102 -0
- calc_terminal/ui/empty_state.py +302 -0
- calc_terminal/ui/events.py +487 -0
- calc_terminal/ui/extensions_panel.py +815 -0
- calc_terminal/ui/footer.py +166 -0
- calc_terminal/ui/gestures_panel.py +383 -0
- calc_terminal/ui/goodbye_screen.py +100 -0
- calc_terminal/ui/header.py +1034 -0
- calc_terminal/ui/help_panel.py +254 -0
- calc_terminal/ui/live_activities.py +914 -0
- calc_terminal/ui/mcp_panel.py +570 -0
- calc_terminal/ui/memory_center.py +524 -0
- calc_terminal/ui/mode_colors_panel.py +525 -0
- calc_terminal/ui/nav_screens.py +747 -0
- calc_terminal/ui/ollama_panel.py +536 -0
- calc_terminal/ui/palette.py +221 -0
- calc_terminal/ui/permission_panel.py +269 -0
- calc_terminal/ui/personalization_panel.py +517 -0
- calc_terminal/ui/personalize_center.py +1568 -0
- calc_terminal/ui/preview_panel.py +441 -0
- calc_terminal/ui/resizers.py +402 -0
- calc_terminal/ui/sidebar.py +1285 -0
- calc_terminal/ui/statusbar.py +168 -0
- calc_terminal/ui/theme_css.py +1396 -0
- calc_terminal/ui/thinking.py +226 -0
- calc_terminal/ui/timeline_panel.py +102 -0
- calc_terminal/ui/todo_panel.py +193 -0
- calc_terminal/ui/viewport.py +136 -0
- calc_terminal/ui/vision_panel.py +489 -0
- calc_terminal/ui/welcome_modal.py +343 -0
- calc_terminal/ui/widgets.py +160 -0
- calc_terminal/ui/workspace.py +831 -0
- calc_terminal/viewers/__init__.py +1 -0
- calc_terminal/viewers/document_viewer.py +252 -0
- calc_terminal/viewers/image_viewer.py +241 -0
- calc_terminal/viewers/pdf_viewer.py +203 -0
- calc_terminal/viewers/presentation_viewer.py +164 -0
- calc_terminal/viewers/registry.py +120 -0
- calc_terminal/viewers/spreadsheet_viewer.py +204 -0
- calc_terminal/vision/__init__.py +89 -0
- calc_terminal/vision/analysis.py +194 -0
- calc_terminal/vision/annotations.py +297 -0
- calc_terminal/vision/capture.py +171 -0
- calc_terminal/vision/context.py +231 -0
- calc_terminal/vision/correlation.py +169 -0
- calc_terminal/vision/cursor.py +258 -0
- calc_terminal/vision/events.py +66 -0
- calc_terminal/vision/frame_pipeline.py +259 -0
- calc_terminal/vision/priority.py +218 -0
- calc_terminal/vision/provider.py +180 -0
- calc_terminal/vision/safety.py +149 -0
- calc_terminal/vision/session.py +281 -0
- calc_terminal/vision/verify.py +162 -0
- calc_terminal/vision.py +514 -0
- calc_terminal/vscode_integration.py +113 -0
- calc_terminal/web/__init__.py +8 -0
- calc_terminal/web/cat_runtime.py +710 -0
- calc_terminal/web/server.py +2891 -0
- calc_terminal/web/static/css/app.css +3152 -0
- calc_terminal/web/static/icons/badge-72.png +0 -0
- calc_terminal/web/static/icons/cat.ico +0 -0
- calc_terminal/web/static/icons/icon-128.png +0 -0
- calc_terminal/web/static/icons/icon-144.png +0 -0
- calc_terminal/web/static/icons/icon-152.png +0 -0
- calc_terminal/web/static/icons/icon-192.png +0 -0
- calc_terminal/web/static/icons/icon-384.png +0 -0
- calc_terminal/web/static/icons/icon-512.png +0 -0
- calc_terminal/web/static/icons/icon-72.png +0 -0
- calc_terminal/web/static/icons/icon-96.png +0 -0
- calc_terminal/web/static/icons/icon.svg +34 -0
- calc_terminal/web/static/icons/new-project.png +0 -0
- calc_terminal/web/static/icons/open-project.png +0 -0
- calc_terminal/web/static/index.html +734 -0
- calc_terminal/web/static/js/app.js +2403 -0
- calc_terminal/web/static/manifest.json +88 -0
- calc_terminal/web/static/sw.js +230 -0
- calc_terminal/workflow_engine.py +769 -0
- calc_terminal/workspace.py +593 -0
- calc_terminal/workspace_index.py +385 -0
- cct_cli-0.7.9.0.dist-info/METADATA +210 -0
- cct_cli-0.7.9.0.dist-info/RECORD +325 -0
- cct_cli-0.7.9.0.dist-info/WHEEL +5 -0
- cct_cli-0.7.9.0.dist-info/entry_points.txt +4 -0
- cct_cli-0.7.9.0.dist-info/licenses/LICENSE +21 -0
- 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
|
+
}
|