Skip to content

Commit b4cf502

Browse files
committed
feat(plugins): Add comprehensive VSCode-like hook system with 24+ hook types
This commit dramatically enhances the plugin system to be comparable to VSCode plugins with comprehensive event hooks and interception capabilities. New Hook Types Added: - Prompt Injection: PromptInjectHook for AI prompt modification - AI Response: AiResponseBeforeHook, AiResponseStreamHook, AiResponseAfterHook - File Operations: FileOperationBeforeHook/AfterHook with deny capability - Command Execution: CommandExecuteBeforeHook/AfterHook with output replacement - Input Interception: InputInterceptHook with autocomplete and text expansion - Error Handling: ErrorHandleHook with recovery actions - Config Changes: ConfigChangedHook with pattern matching - Session Lifecycle: SessionStartHook, SessionEndHook with context injection - Workspace: WorkspaceChangedHook with project type detection - Clipboard: ClipboardCopyHook, ClipboardPasteHook - UI Rendering: UiRenderHook for custom widgets - Focus Changes: FocusChangeHook for app focus events Event System Expansion (13 → 40+ events): - Session: SessionResumed - Tool: ToolExecutionAborted - AI: AiResponseStarted, AiResponseChunk, AiResponseCompleted, AiResponseError - File: FileCreated, FileDeleted, FileRenamed - Command: CommandStarted, CommandCompleted - Config: AgentChanged - Workspace: WorkspaceChanged, ProjectDetected - Plugin: PluginInitialized, PluginDisabled, PluginEnabled, PluginMessage - Error: ErrorOccurred - Clipboard: ClipboardCopy, ClipboardPaste - Focus: FocusGained, FocusLost - Permission: PermissionRequested, PermissionGranted, PermissionDenied Key Features: - All hooks support priority ordering for execution order control - Pattern matching for targeted hook triggers - Flow control: Continue/Skip/Abort/Replace for all hooks - Inter-plugin communication via PluginMessage event - Complete Input/Output type system with proper serialization - Full backward compatibility with existing plugin code
1 parent e4ee3e3 commit b4cf502

4 files changed

Lines changed: 1796 additions & 16 deletions

File tree

cortex-plugins/src/events.rs

Lines changed: 262 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use uuid::Uuid;
1313
#[derive(Debug, Clone, Serialize, Deserialize)]
1414
#[serde(tag = "type", rename_all = "snake_case")]
1515
pub enum Event {
16+
// ========== Session Events ==========
1617
/// Session started
1718
SessionStarted {
1819
session_id: String,
@@ -26,6 +27,13 @@ pub enum Event {
2627
duration_ms: u64,
2728
},
2829

30+
/// Session resumed
31+
SessionResumed {
32+
session_id: String,
33+
message_count: usize,
34+
},
35+
36+
// ========== Tool Events ==========
2937
/// Tool execution started
3038
ToolExecutionStarted {
3139
session_id: String,
@@ -42,6 +50,15 @@ pub enum Event {
4250
duration_ms: u64,
4351
},
4452

53+
/// Tool was aborted
54+
ToolExecutionAborted {
55+
session_id: String,
56+
tool_name: String,
57+
call_id: String,
58+
reason: String,
59+
},
60+
61+
// ========== Chat/AI Events ==========
4562
/// Chat message received
4663
ChatMessageReceived {
4764
session_id: String,
@@ -55,20 +72,117 @@ pub enum Event {
5572
message_id: String,
5673
},
5774

75+
/// AI response started
76+
AiResponseStarted {
77+
session_id: String,
78+
request_id: String,
79+
model: String,
80+
},
81+
82+
/// AI response streaming chunk
83+
AiResponseChunk {
84+
session_id: String,
85+
request_id: String,
86+
chunk_index: usize,
87+
},
88+
89+
/// AI response completed
90+
AiResponseCompleted {
91+
session_id: String,
92+
request_id: String,
93+
model: String,
94+
duration_ms: u64,
95+
tokens_used: Option<u32>,
96+
},
97+
98+
/// AI response error
99+
AiResponseError {
100+
session_id: String,
101+
request_id: String,
102+
error: String,
103+
},
104+
105+
// ========== File Events ==========
58106
/// File edited
59107
FileEdited {
60108
session_id: String,
61109
file_path: String,
62110
change_type: FileChangeType,
63111
},
64112

113+
/// File created
114+
FileCreated {
115+
session_id: String,
116+
file_path: String,
117+
},
118+
119+
/// File deleted
120+
FileDeleted {
121+
session_id: String,
122+
file_path: String,
123+
},
124+
125+
/// File renamed
126+
FileRenamed {
127+
session_id: String,
128+
old_path: String,
129+
new_path: String,
130+
},
131+
132+
// ========== Command Events ==========
133+
/// Command execution started
134+
CommandStarted {
135+
session_id: String,
136+
command: String,
137+
args: Vec<String>,
138+
},
139+
140+
/// Command execution completed
141+
CommandCompleted {
142+
session_id: String,
143+
command: String,
144+
success: bool,
145+
duration_ms: u64,
146+
},
147+
148+
// ========== Config Events ==========
65149
/// Model changed
66150
ModelChanged {
67151
session_id: String,
68152
old_model: Option<String>,
69153
new_model: String,
70154
},
71155

156+
/// Configuration changed
157+
ConfigChanged {
158+
key: String,
159+
old_value: Option<serde_json::Value>,
160+
new_value: serde_json::Value,
161+
},
162+
163+
/// Agent changed
164+
AgentChanged {
165+
session_id: String,
166+
old_agent: Option<String>,
167+
new_agent: String,
168+
},
169+
170+
// ========== Workspace Events ==========
171+
/// Working directory changed
172+
WorkspaceChanged {
173+
session_id: String,
174+
old_cwd: Option<String>,
175+
new_cwd: String,
176+
},
177+
178+
/// Project detected
179+
ProjectDetected {
180+
session_id: String,
181+
project_type: String,
182+
root_path: String,
183+
},
184+
185+
// ========== Plugin Events ==========
72186
/// Plugin loaded
73187
PluginLoaded { plugin_id: String },
74188

@@ -78,19 +192,81 @@ pub enum Event {
78192
/// Plugin error
79193
PluginError { plugin_id: String, error: String },
80194

81-
/// Configuration changed
82-
ConfigChanged {
83-
key: String,
84-
old_value: Option<serde_json::Value>,
85-
new_value: serde_json::Value,
195+
/// Plugin initialized
196+
PluginInitialized { plugin_id: String },
197+
198+
/// Plugin disabled
199+
PluginDisabled { plugin_id: String },
200+
201+
/// Plugin enabled
202+
PluginEnabled { plugin_id: String },
203+
204+
// ========== Error Events ==========
205+
/// Error occurred
206+
ErrorOccurred {
207+
session_id: String,
208+
error_type: String,
209+
message: String,
210+
recoverable: bool,
211+
},
212+
213+
// ========== Clipboard Events ==========
214+
/// Content copied to clipboard
215+
ClipboardCopy {
216+
session_id: String,
217+
content_length: usize,
218+
},
219+
220+
/// Content pasted from clipboard
221+
ClipboardPaste {
222+
session_id: String,
223+
content_length: usize,
224+
},
225+
226+
// ========== Focus Events ==========
227+
/// Application gained focus
228+
FocusGained { session_id: String },
229+
230+
/// Application lost focus
231+
FocusLost { session_id: String },
232+
233+
// ========== Permission Events ==========
234+
/// Permission requested
235+
PermissionRequested {
236+
session_id: String,
237+
permission: String,
238+
resource: String,
239+
},
240+
241+
/// Permission granted
242+
PermissionGranted {
243+
session_id: String,
244+
permission: String,
245+
resource: String,
246+
},
247+
248+
/// Permission denied
249+
PermissionDenied {
250+
session_id: String,
251+
permission: String,
252+
resource: String,
86253
},
87254

255+
// ========== Custom Events ==========
88256
/// Custom event from a plugin
89257
Custom {
90258
plugin_id: String,
91259
event_type: String,
92260
data: serde_json::Value,
93261
},
262+
263+
/// Inter-plugin message
264+
PluginMessage {
265+
source_plugin: String,
266+
target_plugin: Option<String>,
267+
message_type: String,
268+
payload: serde_json::Value,
269+
},
94270
}
95271

96272
/// File change types.
@@ -116,38 +292,117 @@ pub trait EventHandler: Send + Sync {
116292
/// Event type filter.
117293
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
118294
pub enum EventType {
295+
// Session events
119296
SessionStarted,
120297
SessionEnded,
298+
SessionResumed,
299+
// Tool events
121300
ToolExecutionStarted,
122301
ToolExecutionCompleted,
302+
ToolExecutionAborted,
303+
// Chat/AI events
123304
ChatMessageReceived,
124305
ChatMessageSent,
306+
AiResponseStarted,
307+
AiResponseChunk,
308+
AiResponseCompleted,
309+
AiResponseError,
310+
// File events
125311
FileEdited,
312+
FileCreated,
313+
FileDeleted,
314+
FileRenamed,
315+
// Command events
316+
CommandStarted,
317+
CommandCompleted,
318+
// Config events
126319
ModelChanged,
320+
ConfigChanged,
321+
AgentChanged,
322+
// Workspace events
323+
WorkspaceChanged,
324+
ProjectDetected,
325+
// Plugin events
127326
PluginLoaded,
128327
PluginUnloaded,
129328
PluginError,
130-
ConfigChanged,
329+
PluginInitialized,
330+
PluginDisabled,
331+
PluginEnabled,
332+
// Error events
333+
ErrorOccurred,
334+
// Clipboard events
335+
ClipboardCopy,
336+
ClipboardPaste,
337+
// Focus events
338+
FocusGained,
339+
FocusLost,
340+
// Permission events
341+
PermissionRequested,
342+
PermissionGranted,
343+
PermissionDenied,
344+
// Custom events
131345
Custom,
346+
PluginMessage,
347+
// Wildcard
132348
All,
133349
}
134350

135351
impl From<&Event> for EventType {
136352
fn from(event: &Event) -> Self {
137353
match event {
354+
// Session events
138355
Event::SessionStarted { .. } => EventType::SessionStarted,
139356
Event::SessionEnded { .. } => EventType::SessionEnded,
357+
Event::SessionResumed { .. } => EventType::SessionResumed,
358+
// Tool events
140359
Event::ToolExecutionStarted { .. } => EventType::ToolExecutionStarted,
141360
Event::ToolExecutionCompleted { .. } => EventType::ToolExecutionCompleted,
361+
Event::ToolExecutionAborted { .. } => EventType::ToolExecutionAborted,
362+
// Chat/AI events
142363
Event::ChatMessageReceived { .. } => EventType::ChatMessageReceived,
143364
Event::ChatMessageSent { .. } => EventType::ChatMessageSent,
365+
Event::AiResponseStarted { .. } => EventType::AiResponseStarted,
366+
Event::AiResponseChunk { .. } => EventType::AiResponseChunk,
367+
Event::AiResponseCompleted { .. } => EventType::AiResponseCompleted,
368+
Event::AiResponseError { .. } => EventType::AiResponseError,
369+
// File events
144370
Event::FileEdited { .. } => EventType::FileEdited,
371+
Event::FileCreated { .. } => EventType::FileCreated,
372+
Event::FileDeleted { .. } => EventType::FileDeleted,
373+
Event::FileRenamed { .. } => EventType::FileRenamed,
374+
// Command events
375+
Event::CommandStarted { .. } => EventType::CommandStarted,
376+
Event::CommandCompleted { .. } => EventType::CommandCompleted,
377+
// Config events
145378
Event::ModelChanged { .. } => EventType::ModelChanged,
379+
Event::ConfigChanged { .. } => EventType::ConfigChanged,
380+
Event::AgentChanged { .. } => EventType::AgentChanged,
381+
// Workspace events
382+
Event::WorkspaceChanged { .. } => EventType::WorkspaceChanged,
383+
Event::ProjectDetected { .. } => EventType::ProjectDetected,
384+
// Plugin events
146385
Event::PluginLoaded { .. } => EventType::PluginLoaded,
147386
Event::PluginUnloaded { .. } => EventType::PluginUnloaded,
148387
Event::PluginError { .. } => EventType::PluginError,
149-
Event::ConfigChanged { .. } => EventType::ConfigChanged,
388+
Event::PluginInitialized { .. } => EventType::PluginInitialized,
389+
Event::PluginDisabled { .. } => EventType::PluginDisabled,
390+
Event::PluginEnabled { .. } => EventType::PluginEnabled,
391+
// Error events
392+
Event::ErrorOccurred { .. } => EventType::ErrorOccurred,
393+
// Clipboard events
394+
Event::ClipboardCopy { .. } => EventType::ClipboardCopy,
395+
Event::ClipboardPaste { .. } => EventType::ClipboardPaste,
396+
// Focus events
397+
Event::FocusGained { .. } => EventType::FocusGained,
398+
Event::FocusLost { .. } => EventType::FocusLost,
399+
// Permission events
400+
Event::PermissionRequested { .. } => EventType::PermissionRequested,
401+
Event::PermissionGranted { .. } => EventType::PermissionGranted,
402+
Event::PermissionDenied { .. } => EventType::PermissionDenied,
403+
// Custom events
150404
Event::Custom { .. } => EventType::Custom,
405+
Event::PluginMessage { .. } => EventType::PluginMessage,
151406
}
152407
}
153408
}

0 commit comments

Comments
 (0)