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,487 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CCT UI events — the public contract between UI components.
|
|
3
|
+
|
|
4
|
+
Components never call each other directly (ConversationView never
|
|
5
|
+
calls a Composer method; Composer never reaches into ConversationView;
|
|
6
|
+
PermissionCard never touches ConversationView). Everything crosses
|
|
7
|
+
component boundaries as one of these Message subclasses, posted with
|
|
8
|
+
`self.post_message(...)` and handled by a parent's `on_<event_name>`
|
|
9
|
+
method (Textual's normal message-bubbling convention) or centrally in
|
|
10
|
+
calc_terminal/ui/app.py's CCTApp, which is the only place allowed to
|
|
11
|
+
coordinate more than one component at once.
|
|
12
|
+
|
|
13
|
+
Every event here is a plain data carrier: attributes only, no methods
|
|
14
|
+
that do work. Deciding what an event *means* (allow this action, call
|
|
15
|
+
the AI, switch the theme) is business logic and lives outside ui/.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
if __name__ == "__main__":
|
|
19
|
+
print("This is a library file and is not meant to be run directly.")
|
|
20
|
+
import sys
|
|
21
|
+
sys.exit(1)
|
|
22
|
+
|
|
23
|
+
TEXTUAL_AVAILABLE = True
|
|
24
|
+
try:
|
|
25
|
+
from textual.message import Message
|
|
26
|
+
except Exception:
|
|
27
|
+
TEXTUAL_AVAILABLE = False
|
|
28
|
+
|
|
29
|
+
class Message: # minimal stand-in so `class X(Message)` below still
|
|
30
|
+
"""Textual isn't installed — these event classes still need to
|
|
31
|
+
exist so `from .events import X` doesn't raise ImportError
|
|
32
|
+
before the caller gets a chance to check TEXTUAL_AVAILABLE."""
|
|
33
|
+
|
|
34
|
+
def __init__(self):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# ------------------------------------------------------------- messaging --
|
|
39
|
+
class MessageSubmitted(Message):
|
|
40
|
+
"""Composer -> app. The user submitted the composer (Send / Ctrl+Enter)."""
|
|
41
|
+
|
|
42
|
+
def __init__(self, text, attachments=None):
|
|
43
|
+
self.text = text
|
|
44
|
+
self.attachments = attachments or []
|
|
45
|
+
super().__init__()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class MessageStarted(Message):
|
|
49
|
+
"""App -> conversation. A new turn is being created for `turn_id`.
|
|
50
|
+
`prompt` is the text that triggered it — optional, and only used by
|
|
51
|
+
the composer's status bar for smart stage detection (thinking.py),
|
|
52
|
+
so existing callers that don't pass it keep working unchanged."""
|
|
53
|
+
|
|
54
|
+
def __init__(self, turn_id, role="assistant", prompt=""):
|
|
55
|
+
self.turn_id = turn_id
|
|
56
|
+
self.role = role
|
|
57
|
+
self.prompt = prompt
|
|
58
|
+
super().__init__()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class MessageChunk(Message):
|
|
62
|
+
"""App -> conversation. One streamed token/fragment for `turn_id`."""
|
|
63
|
+
|
|
64
|
+
def __init__(self, turn_id, text):
|
|
65
|
+
self.turn_id = turn_id
|
|
66
|
+
self.text = text
|
|
67
|
+
super().__init__()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class MessageFinished(Message):
|
|
71
|
+
"""App -> conversation. `turn_id` is complete; `full_text` is final."""
|
|
72
|
+
|
|
73
|
+
def __init__(self, turn_id, full_text, duration=0.0, usage=None):
|
|
74
|
+
self.turn_id = turn_id
|
|
75
|
+
self.full_text = full_text
|
|
76
|
+
self.duration = duration
|
|
77
|
+
self.usage = usage or {}
|
|
78
|
+
super().__init__()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ------------------------------------------------------------- streaming --
|
|
82
|
+
class StreamingStarted(Message):
|
|
83
|
+
"""Brackets a streaming session (drives the footer's live indicator)."""
|
|
84
|
+
|
|
85
|
+
def __init__(self, turn_id):
|
|
86
|
+
self.turn_id = turn_id
|
|
87
|
+
super().__init__()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class StreamingFinished(Message):
|
|
91
|
+
def __init__(self, turn_id):
|
|
92
|
+
self.turn_id = turn_id
|
|
93
|
+
super().__init__()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class AgentActivity(Message):
|
|
97
|
+
"""App -> conversation (v0.7.9.0): a real backend agent-state
|
|
98
|
+
crossing for the CAT Agent ASCII indicator. `state` is one of:
|
|
99
|
+
|
|
100
|
+
thinking model request in flight / processing results
|
|
101
|
+
tool_execution an agent tool is running
|
|
102
|
+
waiting_permission blocked on the user's permission decision
|
|
103
|
+
|
|
104
|
+
The indicator starts with MessageStarted (thinking), may flip to
|
|
105
|
+
tool_execution/waiting_permission and back while the agent loop
|
|
106
|
+
runs, and is always removed by MessageFinished — which covers the
|
|
107
|
+
success, error AND cancelled exits, because every one of those
|
|
108
|
+
paths still posts MessageFinished (partial text kept on cancel).
|
|
109
|
+
This is deliberately a UI-only carrier: it never rides along in
|
|
110
|
+
model requests and never becomes conversation content."""
|
|
111
|
+
|
|
112
|
+
STATES = ("thinking", "tool_execution", "tool_running",
|
|
113
|
+
"waiting_permission")
|
|
114
|
+
|
|
115
|
+
def __init__(self, turn_id, state="thinking"):
|
|
116
|
+
self.turn_id = turn_id
|
|
117
|
+
self.state = state if state in self.STATES else "thinking"
|
|
118
|
+
super().__init__()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# ------------------------------------------------------------ permissions --
|
|
122
|
+
class PermissionRequested(Message):
|
|
123
|
+
"""Backend -> app. `request_id` is a one-shot token the eventual
|
|
124
|
+
PermissionGranted/Denied must echo back so the right pending call
|
|
125
|
+
resolves (see calc_terminal/permissions.py's manager.decide)."""
|
|
126
|
+
|
|
127
|
+
def __init__(self, request_id, key, action_text, reason):
|
|
128
|
+
self.request_id = request_id
|
|
129
|
+
self.key = key
|
|
130
|
+
self.action_text = action_text
|
|
131
|
+
self.reason = reason
|
|
132
|
+
super().__init__()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class PermissionGranted(Message):
|
|
136
|
+
def __init__(self, request_id, key, remember=False):
|
|
137
|
+
self.request_id = request_id
|
|
138
|
+
self.key = key
|
|
139
|
+
self.remember = remember
|
|
140
|
+
super().__init__()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class PermissionDenied(Message):
|
|
144
|
+
"""`remember=True` means Always Deny (v0.7.7 spec section 3's
|
|
145
|
+
dialog offers Deny / Always Deny / Cancel on top of the existing
|
|
146
|
+
Allow Once / Always Allow) — the denied key is recorded so future
|
|
147
|
+
requests are refused without prompting at all."""
|
|
148
|
+
|
|
149
|
+
def __init__(self, request_id, key, remember=False):
|
|
150
|
+
self.request_id = request_id
|
|
151
|
+
self.key = key
|
|
152
|
+
self.remember = remember
|
|
153
|
+
super().__init__()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class PermissionCancelled(Message):
|
|
157
|
+
"""The user picked Cancel instead of Allow/Deny — the pending call
|
|
158
|
+
is resolved as a cancellation (nothing ran, nothing was recorded as
|
|
159
|
+
denied)."""
|
|
160
|
+
|
|
161
|
+
def __init__(self, request_id, key):
|
|
162
|
+
self.request_id = request_id
|
|
163
|
+
self.key = key
|
|
164
|
+
super().__init__()
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# ------------------------------------------------------------ attachments --
|
|
168
|
+
class AttachmentAdded(Message):
|
|
169
|
+
def __init__(self, chip_id, label, kind="file"):
|
|
170
|
+
self.chip_id = chip_id
|
|
171
|
+
self.label = label
|
|
172
|
+
self.kind = kind # "file" | "paste" | "image"
|
|
173
|
+
super().__init__()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class AttachmentRemoved(Message):
|
|
177
|
+
def __init__(self, chip_id):
|
|
178
|
+
self.chip_id = chip_id
|
|
179
|
+
super().__init__()
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class PasteCollapsed(Message):
|
|
183
|
+
def __init__(self, chip_id, line_count):
|
|
184
|
+
self.chip_id = chip_id
|
|
185
|
+
self.line_count = line_count
|
|
186
|
+
super().__init__()
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class PasteExpanded(Message):
|
|
190
|
+
def __init__(self, chip_id, text):
|
|
191
|
+
self.chip_id = chip_id
|
|
192
|
+
self.text = text
|
|
193
|
+
super().__init__()
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class AttachmentChipDoubleClicked(Message):
|
|
197
|
+
"""AttachmentBar -> app. An attachment chip's label was double-
|
|
198
|
+
clicked. For .txt attachments this means "import the text into the
|
|
199
|
+
chat now" (requirement #18); single-click stays normal behavior."""
|
|
200
|
+
|
|
201
|
+
def __init__(self, chip_id, path, name):
|
|
202
|
+
self.chip_id = chip_id
|
|
203
|
+
self.path = path
|
|
204
|
+
self.name = name
|
|
205
|
+
super().__init__()
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# ----------------------------------------------------------- commands/etc --
|
|
209
|
+
class CommandExecuted(Message):
|
|
210
|
+
def __init__(self, command, args=""):
|
|
211
|
+
self.command = command
|
|
212
|
+
self.args = args
|
|
213
|
+
super().__init__()
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class ConversationLoaded(Message):
|
|
217
|
+
def __init__(self, turn_count):
|
|
218
|
+
self.turn_count = turn_count
|
|
219
|
+
super().__init__()
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class ConversationSaved(Message):
|
|
223
|
+
def __init__(self, path):
|
|
224
|
+
self.path = path
|
|
225
|
+
super().__init__()
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class ConversationCleared(Message):
|
|
229
|
+
def __init__(self):
|
|
230
|
+
super().__init__()
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class NotebookChanged(Message):
|
|
234
|
+
def __init__(self, mode):
|
|
235
|
+
self.mode = mode
|
|
236
|
+
super().__init__()
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class WorkspaceChanged(Message):
|
|
240
|
+
def __init__(self, workspace):
|
|
241
|
+
self.workspace = workspace
|
|
242
|
+
super().__init__()
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class ModelChanged(Message):
|
|
246
|
+
def __init__(self, model_name):
|
|
247
|
+
self.model_name = model_name
|
|
248
|
+
super().__init__()
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class ThemeChanged(Message):
|
|
252
|
+
def __init__(self, theme_name):
|
|
253
|
+
self.theme_name = theme_name
|
|
254
|
+
super().__init__()
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class ErrorOccurred(Message):
|
|
258
|
+
def __init__(self, message, detail=""):
|
|
259
|
+
self.message = message
|
|
260
|
+
self.detail = detail
|
|
261
|
+
super().__init__()
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class RetryRequested(Message):
|
|
265
|
+
def __init__(self, turn_id):
|
|
266
|
+
self.turn_id = turn_id
|
|
267
|
+
super().__init__()
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class SessionEnded(Message):
|
|
271
|
+
def __init__(self):
|
|
272
|
+
super().__init__()
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# ------------------------------------------------------- v0.7 IDE events --
|
|
276
|
+
class PermissionModeChanged(Message):
|
|
277
|
+
"""HeaderPermissionSelector -> app. `mode` is one of
|
|
278
|
+
permissions.MODES ('ask' | 'restricted' | 'full')."""
|
|
279
|
+
|
|
280
|
+
def __init__(self, mode):
|
|
281
|
+
self.mode = mode
|
|
282
|
+
super().__init__()
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class FolderOpened(Message):
|
|
286
|
+
"""Sidebar/app -> app. A project folder was opened as the IDE
|
|
287
|
+
workspace root (spec section 16's "Open Folder")."""
|
|
288
|
+
|
|
289
|
+
def __init__(self, path):
|
|
290
|
+
self.path = path
|
|
291
|
+
super().__init__()
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
class SidebarToggled(Message):
|
|
295
|
+
def __init__(self, visible):
|
|
296
|
+
self.visible = visible
|
|
297
|
+
super().__init__()
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class FileOpenRequested(Message):
|
|
301
|
+
"""Sidebar (or drag & drop) -> app: open this path in the editor."""
|
|
302
|
+
|
|
303
|
+
def __init__(self, path):
|
|
304
|
+
self.path = path
|
|
305
|
+
super().__init__()
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class FileSaved(Message):
|
|
309
|
+
def __init__(self, path):
|
|
310
|
+
self.path = path
|
|
311
|
+
super().__init__()
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
class MessageRewritten(Message):
|
|
315
|
+
"""StickyComposer -> app: posted instead of MessageSubmitted when
|
|
316
|
+
the composer was in Rewrite edit mode (see
|
|
317
|
+
StickyComposer.start_edit, triggered by the context menu's
|
|
318
|
+
'rewrite' action) and the user pressed Enter to save.
|
|
319
|
+
|
|
320
|
+
Deliberately a separate event rather than an optional field on
|
|
321
|
+
MessageSubmitted: a plain submit always APPENDS a new user turn
|
|
322
|
+
and a new assistant turn (on_message_submitted), while this always
|
|
323
|
+
REPLACES the existing `turn_id`'s text and regenerates its paired
|
|
324
|
+
assistant reply in place (on_message_rewritten /
|
|
325
|
+
CCTApp._regenerate_turn) — different enough handling that
|
|
326
|
+
overloading one event with an if-branch would just move the
|
|
327
|
+
duplicate-vs-replace bug this fixes from the UI into the event
|
|
328
|
+
handler instead of removing it."""
|
|
329
|
+
|
|
330
|
+
def __init__(self, turn_id, text, attachments=None):
|
|
331
|
+
self.turn_id = turn_id
|
|
332
|
+
self.text = text
|
|
333
|
+
self.attachments = attachments or []
|
|
334
|
+
super().__init__()
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
class MessageContextAction(Message):
|
|
338
|
+
"""ConversationItem (via context_menu.MessageContextMenu) -> app.
|
|
339
|
+
`action` is one of: 'new_session', 'retry:<mode>' (mode is one of
|
|
340
|
+
ai_modes.MODE_ORDER), 'rewrite', 'revert', 'fork' — 'copy' is
|
|
341
|
+
deliberately NOT posted here: it needs no cross-component
|
|
342
|
+
coordination (clipboard + a local confirmation flash), so
|
|
343
|
+
ConversationItem handles it entirely itself and never posts this
|
|
344
|
+
for it. `turn_id` is the turn the menu was opened on."""
|
|
345
|
+
|
|
346
|
+
def __init__(self, action, turn_id):
|
|
347
|
+
self.action = action
|
|
348
|
+
self.turn_id = turn_id
|
|
349
|
+
super().__init__()
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
class NavAction(Message):
|
|
353
|
+
"""NavPanel (opened from the header's menu button) -> app. `action`
|
|
354
|
+
is one of the panel's item ids: 'dashboard', 'open_folder',
|
|
355
|
+
'recent_workspaces', 'mcp_servers', 'customize_ai', 'diff_viewer',
|
|
356
|
+
'settings', 'themes', 'extensions', 'keyboard_shortcuts', 'help'.
|
|
357
|
+
Deliberately a single generic event (like MessageContextAction)
|
|
358
|
+
rather than one Message subclass per item — eleven near-identical
|
|
359
|
+
classes would add nothing an `action` string doesn't already say,
|
|
360
|
+
and CCTApp.on_nav_action is the one place allowed to decide what
|
|
361
|
+
each id actually does."""
|
|
362
|
+
|
|
363
|
+
def __init__(self, action):
|
|
364
|
+
self.action = action
|
|
365
|
+
super().__init__()
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class SettingChanged(Message):
|
|
369
|
+
"""Merged Settings window (v0.7.6 Patch 1) -> app. `category` is
|
|
370
|
+
one of the setting ids the window renders ('perm_mode', 'theme',
|
|
371
|
+
'ai_mode', 'model', 'workspace', 'autosave', 'sound', 'animation',
|
|
372
|
+
'anim_speed', 'precision', 'clear_history'); `value` is the
|
|
373
|
+
proposed new value for choice-style categories (the mode/theme/AI
|
|
374
|
+
mode key, etc.) and None for pure actions and toggles. The window
|
|
375
|
+
never applies anything itself — CCTApp.on_setting_changed is the
|
|
376
|
+
only place that decides what a change means, then asks the window
|
|
377
|
+
to re-render its current values."""
|
|
378
|
+
|
|
379
|
+
def __init__(self, category, value=None):
|
|
380
|
+
self.category = category
|
|
381
|
+
self.value = value
|
|
382
|
+
super().__init__()
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
class WorkspaceFilesChanged(Message):
|
|
386
|
+
"""Backend (agent tool loop) -> app: one or more files/folders on
|
|
387
|
+
disk were created/deleted/renamed by the AI during this turn, so the
|
|
388
|
+
Explorer tree (and, if the affected path is open, the Editor) need
|
|
389
|
+
to refresh. Deliberately coarse (a set of touched paths, not a
|
|
390
|
+
typed per-file event bus) — see CHANGELOG_v0.7.2 for why the full
|
|
391
|
+
"Workspace Event Bus" the spec describes is out of scope for this
|
|
392
|
+
slice."""
|
|
393
|
+
|
|
394
|
+
def __init__(self, paths=None):
|
|
395
|
+
self.paths = list(paths or [])
|
|
396
|
+
super().__init__()
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
class EditorTabsChanged(Message):
|
|
400
|
+
"""EditorPane -> WorkspaceShell (and anything else listening): the
|
|
401
|
+
set of open file tabs changed (a file was opened or closed).
|
|
402
|
+
`count` is the number of open *files* (the placeholder Welcome tab
|
|
403
|
+
never counts). The shell uses this to decide whether the editor
|
|
404
|
+
pane should claim layout space at all and whether the narrow-
|
|
405
|
+
terminal Chat/Files switcher needs to exist — the shell never
|
|
406
|
+
reaches into EditorPane's private dicts for that, this event is the
|
|
407
|
+
only channel."""
|
|
408
|
+
|
|
409
|
+
def __init__(self, count):
|
|
410
|
+
self.count = count
|
|
411
|
+
super().__init__()
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
class ChatRequested(Message):
|
|
415
|
+
"""WorkspaceShell's Chat/Files switcher -> app: the user asked to
|
|
416
|
+
see the AI chat (the one-click switch on narrow terminals, or the
|
|
417
|
+
editor's Esc "back to chat" step). The shell flips the panes
|
|
418
|
+
itself; the app only uses this to re-focus the composer so the user
|
|
419
|
+
can start typing right away — no other coordination crosses this
|
|
420
|
+
boundary."""
|
|
421
|
+
|
|
422
|
+
def __init__(self):
|
|
423
|
+
super().__init__()
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
# --------------------------------------------------------------------------
|
|
427
|
+
# v0.7.10 Live Web Preview — editor ⇄ browser pane events
|
|
428
|
+
#
|
|
429
|
+
class PreviewRequested(Message):
|
|
430
|
+
"""EditorPane (▷ pill) / explorer -> app: preview this file as a
|
|
431
|
+
website. `path` is an absolute filesystem path to an HTML file (or
|
|
432
|
+
any file inside the site) — the controller detects the entry point
|
|
433
|
+
and web root from it."""
|
|
434
|
+
|
|
435
|
+
def __init__(self, path=None):
|
|
436
|
+
self.path = path
|
|
437
|
+
super().__init__()
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
class PreviewClosed(Message):
|
|
441
|
+
"""PreviewPanel (⏻) / app -> shell: close Web Preview and return
|
|
442
|
+
the right pane to the Code Editor. The server/browser teardown and
|
|
443
|
+
any confirmation happen on the app side; the shell only flips its
|
|
444
|
+
WorkspaceMode when this arrives."""
|
|
445
|
+
|
|
446
|
+
def __init__(self):
|
|
447
|
+
super().__init__()
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
class BuildActivity(Message):
|
|
451
|
+
"""Preview subsystem -> UI: one lightweight line of what CAT is
|
|
452
|
+
doing to the website ('● Writing style.css', '✓ Preview
|
|
453
|
+
synchronized'). Rendered in the PreviewPanel's activity strip —
|
|
454
|
+
NEVER dumped into the chat conversation (spec section 16)."""
|
|
455
|
+
|
|
456
|
+
def __init__(self, text, done=False):
|
|
457
|
+
self.text = text
|
|
458
|
+
self.done = done
|
|
459
|
+
super().__init__()
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
# v0.7.10 Live Diff Viewer events
|
|
463
|
+
class DiffViewerRequested(Message):
|
|
464
|
+
"""EditorPane -> app: show the diff viewer for a file. `path` is
|
|
465
|
+
the absolute path to the file, `diff` is the computed diff lines."""
|
|
466
|
+
|
|
467
|
+
def __init__(self, path, diff=None):
|
|
468
|
+
self.path = path
|
|
469
|
+
self.diff = diff or []
|
|
470
|
+
super().__init__()
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
class DiffViewerClosed(Message):
|
|
474
|
+
"""Diff viewer -> app: close the diff viewer panel."""
|
|
475
|
+
|
|
476
|
+
def __init__(self):
|
|
477
|
+
super().__init__()
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
class FileChangeTracked(Message):
|
|
481
|
+
"""Editor -> app: a file was modified and the change was tracked.
|
|
482
|
+
Used for Live File Automation (#13) to notify other components."""
|
|
483
|
+
|
|
484
|
+
def __init__(self, path, change_type="modified"):
|
|
485
|
+
self.path = path
|
|
486
|
+
self.change_type = change_type # "created", "modified", "deleted"
|
|
487
|
+
super().__init__()
|