kollabor 0.4.9__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.
- core/__init__.py +18 -0
- core/application.py +578 -0
- core/cli.py +193 -0
- core/commands/__init__.py +43 -0
- core/commands/executor.py +277 -0
- core/commands/menu_renderer.py +319 -0
- core/commands/parser.py +186 -0
- core/commands/registry.py +331 -0
- core/commands/system_commands.py +479 -0
- core/config/__init__.py +7 -0
- core/config/llm_task_config.py +110 -0
- core/config/loader.py +501 -0
- core/config/manager.py +112 -0
- core/config/plugin_config_manager.py +346 -0
- core/config/plugin_schema.py +424 -0
- core/config/service.py +399 -0
- core/effects/__init__.py +1 -0
- core/events/__init__.py +12 -0
- core/events/bus.py +129 -0
- core/events/executor.py +154 -0
- core/events/models.py +258 -0
- core/events/processor.py +176 -0
- core/events/registry.py +289 -0
- core/fullscreen/__init__.py +19 -0
- core/fullscreen/command_integration.py +290 -0
- core/fullscreen/components/__init__.py +12 -0
- core/fullscreen/components/animation.py +258 -0
- core/fullscreen/components/drawing.py +160 -0
- core/fullscreen/components/matrix_components.py +177 -0
- core/fullscreen/manager.py +302 -0
- core/fullscreen/plugin.py +204 -0
- core/fullscreen/renderer.py +282 -0
- core/fullscreen/session.py +324 -0
- core/io/__init__.py +52 -0
- core/io/buffer_manager.py +362 -0
- core/io/config_status_view.py +272 -0
- core/io/core_status_views.py +410 -0
- core/io/input_errors.py +313 -0
- core/io/input_handler.py +2655 -0
- core/io/input_mode_manager.py +402 -0
- core/io/key_parser.py +344 -0
- core/io/layout.py +587 -0
- core/io/message_coordinator.py +204 -0
- core/io/message_renderer.py +601 -0
- core/io/modal_interaction_handler.py +315 -0
- core/io/raw_input_processor.py +946 -0
- core/io/status_renderer.py +845 -0
- core/io/terminal_renderer.py +586 -0
- core/io/terminal_state.py +551 -0
- core/io/visual_effects.py +734 -0
- core/llm/__init__.py +26 -0
- core/llm/api_communication_service.py +863 -0
- core/llm/conversation_logger.py +473 -0
- core/llm/conversation_manager.py +414 -0
- core/llm/file_operations_executor.py +1401 -0
- core/llm/hook_system.py +402 -0
- core/llm/llm_service.py +1629 -0
- core/llm/mcp_integration.py +386 -0
- core/llm/message_display_service.py +450 -0
- core/llm/model_router.py +214 -0
- core/llm/plugin_sdk.py +396 -0
- core/llm/response_parser.py +848 -0
- core/llm/response_processor.py +364 -0
- core/llm/tool_executor.py +520 -0
- core/logging/__init__.py +19 -0
- core/logging/setup.py +208 -0
- core/models/__init__.py +5 -0
- core/models/base.py +23 -0
- core/plugins/__init__.py +13 -0
- core/plugins/collector.py +212 -0
- core/plugins/discovery.py +386 -0
- core/plugins/factory.py +263 -0
- core/plugins/registry.py +152 -0
- core/storage/__init__.py +5 -0
- core/storage/state_manager.py +84 -0
- core/ui/__init__.py +6 -0
- core/ui/config_merger.py +176 -0
- core/ui/config_widgets.py +369 -0
- core/ui/live_modal_renderer.py +276 -0
- core/ui/modal_actions.py +162 -0
- core/ui/modal_overlay_renderer.py +373 -0
- core/ui/modal_renderer.py +591 -0
- core/ui/modal_state_manager.py +443 -0
- core/ui/widget_integration.py +222 -0
- core/ui/widgets/__init__.py +27 -0
- core/ui/widgets/base_widget.py +136 -0
- core/ui/widgets/checkbox.py +85 -0
- core/ui/widgets/dropdown.py +140 -0
- core/ui/widgets/label.py +78 -0
- core/ui/widgets/slider.py +185 -0
- core/ui/widgets/text_input.py +224 -0
- core/utils/__init__.py +11 -0
- core/utils/config_utils.py +656 -0
- core/utils/dict_utils.py +212 -0
- core/utils/error_utils.py +275 -0
- core/utils/key_reader.py +171 -0
- core/utils/plugin_utils.py +267 -0
- core/utils/prompt_renderer.py +151 -0
- kollabor-0.4.9.dist-info/METADATA +298 -0
- kollabor-0.4.9.dist-info/RECORD +128 -0
- kollabor-0.4.9.dist-info/WHEEL +5 -0
- kollabor-0.4.9.dist-info/entry_points.txt +2 -0
- kollabor-0.4.9.dist-info/licenses/LICENSE +21 -0
- kollabor-0.4.9.dist-info/top_level.txt +4 -0
- kollabor_cli_main.py +20 -0
- plugins/__init__.py +1 -0
- plugins/enhanced_input/__init__.py +18 -0
- plugins/enhanced_input/box_renderer.py +103 -0
- plugins/enhanced_input/box_styles.py +142 -0
- plugins/enhanced_input/color_engine.py +165 -0
- plugins/enhanced_input/config.py +150 -0
- plugins/enhanced_input/cursor_manager.py +72 -0
- plugins/enhanced_input/geometry.py +81 -0
- plugins/enhanced_input/state.py +130 -0
- plugins/enhanced_input/text_processor.py +115 -0
- plugins/enhanced_input_plugin.py +385 -0
- plugins/fullscreen/__init__.py +9 -0
- plugins/fullscreen/example_plugin.py +327 -0
- plugins/fullscreen/matrix_plugin.py +132 -0
- plugins/hook_monitoring_plugin.py +1299 -0
- plugins/query_enhancer_plugin.py +350 -0
- plugins/save_conversation_plugin.py +502 -0
- plugins/system_commands_plugin.py +93 -0
- plugins/tmux_plugin.py +795 -0
- plugins/workflow_enforcement_plugin.py +629 -0
- system_prompt/default.md +1286 -0
- system_prompt/default_win.md +265 -0
- system_prompt/example_with_trender.md +47 -0
core/events/models.py
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""Event system data models and enums for Kollabor CLI."""
|
|
2
|
+
|
|
3
|
+
from collections import defaultdict, deque
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class HookPriority(Enum):
|
|
11
|
+
"""Priority levels for hook execution."""
|
|
12
|
+
SYSTEM = 1000
|
|
13
|
+
SECURITY = 900
|
|
14
|
+
PREPROCESSING = 500
|
|
15
|
+
LLM = 100
|
|
16
|
+
POSTPROCESSING = 50
|
|
17
|
+
DISPLAY = 10
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class HookStatus(Enum):
|
|
21
|
+
"""Status states for hook execution."""
|
|
22
|
+
PENDING = "pending"
|
|
23
|
+
STARTING = "starting"
|
|
24
|
+
WORKING = "working"
|
|
25
|
+
COMPLETED = "completed"
|
|
26
|
+
FAILED = "failed"
|
|
27
|
+
TIMEOUT = "timeout"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class EventType(Enum):
|
|
31
|
+
"""Event types for the hook system."""
|
|
32
|
+
# User input events
|
|
33
|
+
USER_INPUT_PRE = "user_input_pre"
|
|
34
|
+
USER_INPUT = "user_input"
|
|
35
|
+
USER_INPUT_POST = "user_input_post"
|
|
36
|
+
|
|
37
|
+
# Key press events
|
|
38
|
+
KEY_PRESS_PRE = "key_press_pre"
|
|
39
|
+
KEY_PRESS = "key_press"
|
|
40
|
+
KEY_PRESS_POST = "key_press_post"
|
|
41
|
+
|
|
42
|
+
# Paste events
|
|
43
|
+
PASTE_DETECTED = "paste_detected"
|
|
44
|
+
|
|
45
|
+
# LLM events
|
|
46
|
+
LLM_REQUEST_PRE = "llm_request_pre"
|
|
47
|
+
LLM_REQUEST = "llm_request"
|
|
48
|
+
LLM_REQUEST_POST = "llm_request_post"
|
|
49
|
+
LLM_RESPONSE_PRE = "llm_response_pre"
|
|
50
|
+
LLM_RESPONSE = "llm_response"
|
|
51
|
+
LLM_RESPONSE_POST = "llm_response_post"
|
|
52
|
+
LLM_THINKING = "llm_thinking"
|
|
53
|
+
CANCEL_REQUEST = "cancel_request"
|
|
54
|
+
|
|
55
|
+
# Tool events
|
|
56
|
+
TOOL_CALL_PRE = "tool_call_pre"
|
|
57
|
+
TOOL_CALL = "tool_call"
|
|
58
|
+
TOOL_CALL_POST = "tool_call_post"
|
|
59
|
+
|
|
60
|
+
# System events
|
|
61
|
+
SYSTEM_STARTUP = "system_startup"
|
|
62
|
+
SYSTEM_SHUTDOWN = "system_shutdown"
|
|
63
|
+
RENDER_FRAME = "render_frame"
|
|
64
|
+
|
|
65
|
+
# Input rendering events
|
|
66
|
+
INPUT_RENDER_PRE = "input_render_pre"
|
|
67
|
+
INPUT_RENDER = "input_render"
|
|
68
|
+
INPUT_RENDER_POST = "input_render_post"
|
|
69
|
+
|
|
70
|
+
# Command menu events
|
|
71
|
+
COMMAND_MENU_SHOW = "command_menu_show"
|
|
72
|
+
COMMAND_MENU_NAVIGATE = "command_menu_navigate"
|
|
73
|
+
COMMAND_MENU_SELECT = "command_menu_select"
|
|
74
|
+
COMMAND_MENU_HIDE = "command_menu_hide"
|
|
75
|
+
COMMAND_MENU_RENDER = "command_menu_render"
|
|
76
|
+
|
|
77
|
+
# Status display events
|
|
78
|
+
STATUS_VIEW_CHANGED = "status_view_changed"
|
|
79
|
+
STATUS_CONTENT_UPDATE = "status_content_update"
|
|
80
|
+
STATUS_BLOCK_RESIZE = "status_block_resize"
|
|
81
|
+
|
|
82
|
+
# Slash command events
|
|
83
|
+
SLASH_COMMAND_DETECTED = "slash_command_detected"
|
|
84
|
+
SLASH_COMMAND_EXECUTE = "slash_command_execute"
|
|
85
|
+
SLASH_COMMAND_COMPLETE = "slash_command_complete"
|
|
86
|
+
SLASH_COMMAND_ERROR = "slash_command_error"
|
|
87
|
+
|
|
88
|
+
# Command output display events
|
|
89
|
+
COMMAND_OUTPUT_DISPLAY = "command_output_display"
|
|
90
|
+
|
|
91
|
+
# Command menu events (enhanced)
|
|
92
|
+
COMMAND_MENU_FILTER = "command_menu_filter"
|
|
93
|
+
|
|
94
|
+
# Modal events
|
|
95
|
+
MODAL_TRIGGER = "modal_trigger"
|
|
96
|
+
STATUS_MODAL_TRIGGER = "status_modal_trigger"
|
|
97
|
+
STATUS_MODAL_RENDER = "status_modal_render"
|
|
98
|
+
LIVE_MODAL_TRIGGER = "live_modal_trigger"
|
|
99
|
+
|
|
100
|
+
# Rendering control events
|
|
101
|
+
PAUSE_RENDERING = "pause_rendering"
|
|
102
|
+
RESUME_RENDERING = "resume_rendering"
|
|
103
|
+
MODAL_SHOW = "modal_show"
|
|
104
|
+
MODAL_HIDE = "modal_hide"
|
|
105
|
+
MODAL_SAVE = "modal_save"
|
|
106
|
+
FULLSCREEN_INPUT = "fullscreen_input"
|
|
107
|
+
COMMAND_MENU_ACTIVATE = "command_menu_activate"
|
|
108
|
+
|
|
109
|
+
# Status area takeover events
|
|
110
|
+
STATUS_TAKEOVER_START = "status_takeover_start"
|
|
111
|
+
STATUS_TAKEOVER_NAVIGATE = "status_takeover_navigate"
|
|
112
|
+
STATUS_TAKEOVER_ACTION = "status_takeover_action"
|
|
113
|
+
STATUS_TAKEOVER_END = "status_takeover_end"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@dataclass
|
|
117
|
+
class Hook:
|
|
118
|
+
"""Hook definition for the event system.
|
|
119
|
+
|
|
120
|
+
Attributes:
|
|
121
|
+
name: Unique name for the hook.
|
|
122
|
+
plugin_name: Name of the plugin that owns this hook.
|
|
123
|
+
event_type: Type of event this hook responds to.
|
|
124
|
+
priority: Execution priority (higher numbers execute first).
|
|
125
|
+
callback: Async function to call when event occurs.
|
|
126
|
+
enabled: Whether the hook is currently enabled.
|
|
127
|
+
timeout: Maximum execution time in seconds.
|
|
128
|
+
retry_attempts: Number of retry attempts on failure.
|
|
129
|
+
error_action: Action to take on error ("continue" or "stop").
|
|
130
|
+
status: Current execution status.
|
|
131
|
+
status_area: Status area identifier.
|
|
132
|
+
icon_set: Icons for different states.
|
|
133
|
+
"""
|
|
134
|
+
name: str
|
|
135
|
+
plugin_name: str
|
|
136
|
+
event_type: EventType
|
|
137
|
+
priority: int
|
|
138
|
+
callback: Callable
|
|
139
|
+
enabled: bool = True
|
|
140
|
+
timeout: int = 30
|
|
141
|
+
retry_attempts: int = 3
|
|
142
|
+
error_action: str = "continue"
|
|
143
|
+
status: HookStatus = HookStatus.PENDING
|
|
144
|
+
status_area: str = "A"
|
|
145
|
+
icon_set: Dict[str, str] = field(default_factory=lambda: {
|
|
146
|
+
"thinking": "[THINK]", "processing": "[PROC]", "complete": "[OK]", "error": "[ERR]"
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass
|
|
151
|
+
class Event:
|
|
152
|
+
"""Event data structure for the hook system.
|
|
153
|
+
|
|
154
|
+
Attributes:
|
|
155
|
+
type: Type of event.
|
|
156
|
+
data: Event-specific data.
|
|
157
|
+
source: Source that generated the event.
|
|
158
|
+
timestamp: When the event was created.
|
|
159
|
+
processed: Whether the event has been processed.
|
|
160
|
+
cancelled: Whether the event was cancelled during processing.
|
|
161
|
+
result: Results from hook processing.
|
|
162
|
+
"""
|
|
163
|
+
type: EventType
|
|
164
|
+
data: Dict[str, Any]
|
|
165
|
+
source: str
|
|
166
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
167
|
+
processed: bool = False
|
|
168
|
+
cancelled: bool = False
|
|
169
|
+
result: Dict[str, Any] = field(default_factory=dict)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# Slash Command System Models
|
|
173
|
+
|
|
174
|
+
class CommandMode(Enum):
|
|
175
|
+
"""Different interaction modes for slash commands."""
|
|
176
|
+
NORMAL = "normal" # Regular input mode
|
|
177
|
+
INSTANT = "instant" # /clear - executes immediately
|
|
178
|
+
MENU_POPUP = "menu_popup" # Shows command menu overlay
|
|
179
|
+
STATUS_TAKEOVER = "status_takeover" # /agents - takes over status area
|
|
180
|
+
STATUS_MODAL = "status_modal" # Modal within status area only
|
|
181
|
+
INLINE_INPUT = "inline_input" # /save [filename] - inline parameters
|
|
182
|
+
MODAL = "modal" # Modal overlay mode
|
|
183
|
+
LIVE_MODAL = "live_modal" # Live-updating modal (e.g., tmux view)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class CommandCategory(Enum):
|
|
187
|
+
"""Categories for organizing commands."""
|
|
188
|
+
SYSTEM = "system"
|
|
189
|
+
CONVERSATION = "conversation"
|
|
190
|
+
AGENT = "agent"
|
|
191
|
+
DEVELOPMENT = "development"
|
|
192
|
+
FILE = "file"
|
|
193
|
+
TASK = "task"
|
|
194
|
+
CUSTOM = "custom"
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@dataclass
|
|
198
|
+
class UIConfig:
|
|
199
|
+
"""Configuration for command UI interfaces."""
|
|
200
|
+
type: str # "list", "tree", "form", "table", "menu", "modal"
|
|
201
|
+
navigation: List[str] = field(default_factory=lambda: ["↑↓", "Enter", "Esc"])
|
|
202
|
+
height: int = 10
|
|
203
|
+
width: Optional[int] = None
|
|
204
|
+
scrollable: bool = True
|
|
205
|
+
title: str = ""
|
|
206
|
+
footer: str = ""
|
|
207
|
+
modal_config: Optional[Dict[str, Any]] = None # Modal-specific configuration
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@dataclass
|
|
211
|
+
class ParameterDefinition:
|
|
212
|
+
"""Definition for command parameters."""
|
|
213
|
+
name: str
|
|
214
|
+
type: str # "string", "int", "bool", "file", "choice"
|
|
215
|
+
description: str
|
|
216
|
+
required: bool = False
|
|
217
|
+
default: Any = None
|
|
218
|
+
choices: Optional[List[str]] = None
|
|
219
|
+
validation: Optional[str] = None
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@dataclass
|
|
223
|
+
class CommandDefinition:
|
|
224
|
+
"""Complete definition of a slash command."""
|
|
225
|
+
name: str
|
|
226
|
+
description: str
|
|
227
|
+
handler: Callable
|
|
228
|
+
plugin_name: str
|
|
229
|
+
aliases: List[str] = field(default_factory=list)
|
|
230
|
+
mode: CommandMode = CommandMode.INSTANT
|
|
231
|
+
category: CommandCategory = CommandCategory.CUSTOM
|
|
232
|
+
parameters: List[ParameterDefinition] = field(default_factory=list)
|
|
233
|
+
ui_config: Optional[UIConfig] = None
|
|
234
|
+
icon: str = ""
|
|
235
|
+
hidden: bool = False
|
|
236
|
+
enabled: bool = True
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@dataclass
|
|
240
|
+
class SlashCommand:
|
|
241
|
+
"""Parsed slash command from user input."""
|
|
242
|
+
name: str
|
|
243
|
+
args: List[str] = field(default_factory=list)
|
|
244
|
+
raw_input: str = ""
|
|
245
|
+
parameters: Dict[str, Any] = field(default_factory=dict)
|
|
246
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@dataclass
|
|
250
|
+
class CommandResult:
|
|
251
|
+
"""Result from command execution."""
|
|
252
|
+
success: bool
|
|
253
|
+
message: str = ""
|
|
254
|
+
data: Dict[str, Any] = field(default_factory=dict)
|
|
255
|
+
status_ui: Optional[Any] = None # UI component for status takeover
|
|
256
|
+
ui_config: Optional['UIConfig'] = None # UI configuration for modal/status display
|
|
257
|
+
display_type: str = "info" # "info", "success", "warning", "error"
|
|
258
|
+
should_clear_input: bool = True
|
core/events/processor.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""Event processor for handling pre/post event processing logic."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Dict, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
from .models import Event, EventType
|
|
7
|
+
from .executor import HookExecutor
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .registry import HookRegistry
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class EventProcessor:
|
|
17
|
+
"""Handles pre/post event processing with clean separation of concerns.
|
|
18
|
+
|
|
19
|
+
This class manages the complex logic of processing events through
|
|
20
|
+
their pre, main, and post phases while maintaining data flow integrity.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, hook_registry: 'HookRegistry', hook_executor: HookExecutor):
|
|
24
|
+
"""Initialize the event processor.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
hook_registry: Registry for managing hooks.
|
|
28
|
+
hook_executor: Executor for individual hooks.
|
|
29
|
+
"""
|
|
30
|
+
self.hook_registry = hook_registry
|
|
31
|
+
self.hook_executor = hook_executor
|
|
32
|
+
|
|
33
|
+
# Pre/post event type mappings
|
|
34
|
+
self.pre_post_map = {
|
|
35
|
+
EventType.USER_INPUT: (EventType.USER_INPUT_PRE, EventType.USER_INPUT_POST),
|
|
36
|
+
EventType.KEY_PRESS: (EventType.KEY_PRESS_PRE, EventType.KEY_PRESS_POST),
|
|
37
|
+
EventType.LLM_REQUEST: (EventType.LLM_REQUEST_PRE, EventType.LLM_REQUEST_POST),
|
|
38
|
+
EventType.LLM_RESPONSE: (EventType.LLM_RESPONSE_PRE, EventType.LLM_RESPONSE_POST),
|
|
39
|
+
EventType.TOOL_CALL: (EventType.TOOL_CALL_PRE, EventType.TOOL_CALL_POST),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
logger.debug("EventProcessor initialized")
|
|
43
|
+
|
|
44
|
+
async def process_event_with_phases(self, event_type: EventType, data: Dict[str, Any], source: str) -> Dict[str, Any]:
|
|
45
|
+
"""Process an event through pre, main, and post phases.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
event_type: Type of event to process.
|
|
49
|
+
data: Event data.
|
|
50
|
+
source: Source of the event.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Results from all phases of processing.
|
|
54
|
+
"""
|
|
55
|
+
results = {
|
|
56
|
+
"pre": {},
|
|
57
|
+
"main": {},
|
|
58
|
+
"post": {},
|
|
59
|
+
"cancelled": False,
|
|
60
|
+
"source": source,
|
|
61
|
+
"event_type": event_type.value
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
event_data = data.copy()
|
|
65
|
+
pre_event_type, post_event_type = self.pre_post_map.get(event_type, (None, None))
|
|
66
|
+
|
|
67
|
+
# Phase 1: PRE hooks
|
|
68
|
+
if pre_event_type:
|
|
69
|
+
pre_results = await self._process_phase(pre_event_type, event_data, source, "PRE")
|
|
70
|
+
results["pre"] = pre_results
|
|
71
|
+
|
|
72
|
+
if pre_results.get("cancelled", False):
|
|
73
|
+
results["cancelled"] = True
|
|
74
|
+
logger.info(f"Event {event_type.value} cancelled during PRE phase")
|
|
75
|
+
return results
|
|
76
|
+
|
|
77
|
+
# Update data from PRE phase
|
|
78
|
+
event_data = pre_results.get("final_data", event_data)
|
|
79
|
+
|
|
80
|
+
# Phase 2: MAIN event
|
|
81
|
+
main_results = await self._process_phase(event_type, event_data, source, "MAIN")
|
|
82
|
+
results["main"] = main_results
|
|
83
|
+
|
|
84
|
+
if main_results.get("cancelled", False):
|
|
85
|
+
results["cancelled"] = True
|
|
86
|
+
logger.info(f"Event {event_type.value} cancelled during MAIN phase")
|
|
87
|
+
return results
|
|
88
|
+
|
|
89
|
+
# Update data from MAIN phase
|
|
90
|
+
event_data = main_results.get("final_data", event_data)
|
|
91
|
+
|
|
92
|
+
# Phase 3: POST hooks (only if main wasn't cancelled)
|
|
93
|
+
if post_event_type and not main_results.get("cancelled", False):
|
|
94
|
+
post_results = await self._process_phase(post_event_type, event_data, source, "POST")
|
|
95
|
+
results["post"] = post_results
|
|
96
|
+
|
|
97
|
+
if post_results.get("cancelled", False):
|
|
98
|
+
results["cancelled"] = True
|
|
99
|
+
logger.info(f"Event {event_type.value} cancelled during POST phase")
|
|
100
|
+
|
|
101
|
+
return results
|
|
102
|
+
|
|
103
|
+
async def _process_phase(self, event_type: EventType, data: Dict[str, Any], source: str, phase_name: str) -> Dict[str, Any]:
|
|
104
|
+
"""Process a single phase (PRE, MAIN, or POST) of an event.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
event_type: Type of event for this phase.
|
|
108
|
+
data: Event data.
|
|
109
|
+
source: Source of the event.
|
|
110
|
+
phase_name: Name of the phase for logging.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Results from processing this phase.
|
|
114
|
+
"""
|
|
115
|
+
phase_results = {
|
|
116
|
+
"phase": phase_name,
|
|
117
|
+
"event_type": event_type.value,
|
|
118
|
+
"hook_results": [],
|
|
119
|
+
"cancelled": False,
|
|
120
|
+
"final_data": data.copy(),
|
|
121
|
+
"stats": {}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# Get hooks for this event type
|
|
125
|
+
hooks = self.hook_registry.get_hooks_for_event(event_type)
|
|
126
|
+
|
|
127
|
+
if not hooks:
|
|
128
|
+
logger.debug(f"No hooks registered for {event_type.value} ({phase_name} phase)")
|
|
129
|
+
phase_results["stats"] = {"total_hooks": 0}
|
|
130
|
+
return phase_results
|
|
131
|
+
|
|
132
|
+
# Create event object
|
|
133
|
+
event = Event(type=event_type, data=data.copy(), source=source)
|
|
134
|
+
# Execute hooks in priority order
|
|
135
|
+
for hook in hooks:
|
|
136
|
+
if event.cancelled:
|
|
137
|
+
logger.debug(f"Skipping remaining hooks due to event cancellation")
|
|
138
|
+
break
|
|
139
|
+
|
|
140
|
+
hook_result = await self.hook_executor.execute_hook(hook, event)
|
|
141
|
+
phase_results["hook_results"].append(hook_result)
|
|
142
|
+
|
|
143
|
+
# Update hook status in registry
|
|
144
|
+
hook_key = hook_result["hook_key"]
|
|
145
|
+
if hook_result["success"]:
|
|
146
|
+
self.hook_registry.update_hook_status(hook_key, "completed")
|
|
147
|
+
elif hook_result.get("error") == "timeout":
|
|
148
|
+
self.hook_registry.update_hook_status(hook_key, "timeout")
|
|
149
|
+
elif hook_result.get("error"):
|
|
150
|
+
self.hook_registry.update_hook_status(hook_key, "failed")
|
|
151
|
+
|
|
152
|
+
# Update final results
|
|
153
|
+
phase_results["cancelled"] = event.cancelled
|
|
154
|
+
phase_results["final_data"] = event.data
|
|
155
|
+
phase_results["stats"] = self.hook_executor.get_execution_stats(phase_results["hook_results"])
|
|
156
|
+
|
|
157
|
+
return phase_results
|
|
158
|
+
|
|
159
|
+
def add_event_type_mapping(self, main_event: EventType, pre_event: EventType, post_event: EventType) -> None:
|
|
160
|
+
"""Add a new event type mapping for pre/post processing.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
main_event: The main event type.
|
|
164
|
+
pre_event: The pre-processing event type.
|
|
165
|
+
post_event: The post-processing event type.
|
|
166
|
+
"""
|
|
167
|
+
self.pre_post_map[main_event] = (pre_event, post_event)
|
|
168
|
+
logger.info(f"Added event type mapping: {main_event.value} -> {pre_event.value}/{post_event.value}")
|
|
169
|
+
|
|
170
|
+
def get_supported_event_types(self) -> Dict[EventType, Tuple[Optional[EventType], Optional[EventType]]]:
|
|
171
|
+
"""Get all supported event types and their pre/post mappings.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
Dictionary mapping main events to their pre/post event types.
|
|
175
|
+
"""
|
|
176
|
+
return self.pre_post_map.copy()
|