1use crate::jsonrpc::RequestId;
27use crate::protocol::{
28 methods, AccountLoginCompletedNotification, AccountRateLimitsUpdatedNotification,
29 AccountUpdatedNotification, AgentMessageDeltaNotification, AppListUpdatedNotification,
30 CmdOutputDeltaNotification, CommandExecOutputDeltaNotification, CommandExecutionApprovalParams,
31 ConfigWarningNotification, ContextCompactedNotification, DeprecationNoticeNotification,
32 ErrorNotification, ExternalAgentConfigImportCompletedNotification, FileChangeApprovalParams,
33 FileChangeOutputDeltaNotification, FileChangePatchUpdatedNotification, FsChangedNotification,
34 FuzzyFileSearchSessionCompletedNotification, FuzzyFileSearchSessionUpdatedNotification,
35 GuardianWarningNotification, HookCompletedNotification, HookStartedNotification,
36 ItemCompletedNotification, ItemGuardianApprovalReviewCompletedNotification,
37 ItemGuardianApprovalReviewStartedNotification, ItemStartedNotification,
38 McpServerOauthLoginCompletedNotification, McpServerStartupStatusUpdatedNotification,
39 McpToolCallProgressNotification, ModelReroutedNotification, ModelVerificationNotification,
40 PlanDeltaNotification, ProcessExitedNotification, ProcessOutputDeltaNotification,
41 ReasoningDeltaNotification, ReasoningSummaryPartAddedNotification,
42 ReasoningTextDeltaNotification, RemoteControlStatusChangedNotification,
43 ServerRequestResolvedNotification, SkillsChangedNotification, TerminalInteractionNotification,
44 ThreadArchivedNotification, ThreadClosedNotification, ThreadGoalClearedNotification,
45 ThreadGoalUpdatedNotification, ThreadNameUpdatedNotification, ThreadRealtimeClosedNotification,
46 ThreadRealtimeErrorNotification, ThreadRealtimeItemAddedNotification,
47 ThreadRealtimeOutputAudioDeltaNotification, ThreadRealtimeSdpNotification,
48 ThreadRealtimeStartedNotification, ThreadRealtimeTranscriptDeltaNotification,
49 ThreadRealtimeTranscriptDoneNotification, ThreadStartedNotification,
50 ThreadStatusChangedNotification, ThreadTokenUsageUpdatedNotification,
51 ThreadUnarchivedNotification, TurnCompletedNotification, TurnDiffUpdatedNotification,
52 TurnPlanUpdatedNotification, TurnStartedNotification, WarningNotification,
53 WindowsSandboxSetupCompletedNotification, WindowsWorldWritableWarningNotification,
54};
55use serde::{Deserialize, Deserializer, Serialize, Serializer};
56use serde_json::Value;
57
58#[derive(Debug, Clone)]
64pub enum Notification {
65 ThreadStarted(ThreadStartedNotification),
67 ThreadStatusChanged(ThreadStatusChangedNotification),
69 ThreadTokenUsageUpdated(ThreadTokenUsageUpdatedNotification),
71 TurnStarted(TurnStartedNotification),
73 TurnCompleted(TurnCompletedNotification),
75 ItemStarted(ItemStartedNotification),
77 ItemCompleted(ItemCompletedNotification),
79 AgentMessageDelta(AgentMessageDeltaNotification),
81 CmdOutputDelta(CmdOutputDeltaNotification),
83 FileChangeOutputDelta(FileChangeOutputDeltaNotification),
85 ReasoningDelta(ReasoningDeltaNotification),
87 Error(ErrorNotification),
89 AccountRateLimitsUpdated(AccountRateLimitsUpdatedNotification),
91 McpServerStartupStatusUpdated(McpServerStartupStatusUpdatedNotification),
93 RemoteControlStatusChanged(RemoteControlStatusChangedNotification),
95 McpServerOauthLoginCompleted(McpServerOauthLoginCompletedNotification),
97 FileChangePatchUpdated(FileChangePatchUpdatedNotification),
99 PlanDelta(PlanDeltaNotification),
101 TurnPlanUpdated(TurnPlanUpdatedNotification),
103 TurnDiffUpdated(TurnDiffUpdatedNotification),
105 ReasoningSummaryPartAdded(ReasoningSummaryPartAddedNotification),
107 ReasoningTextDelta(ReasoningTextDeltaNotification),
109 AccountLoginCompleted(AccountLoginCompletedNotification),
111 DeprecationNotice(DeprecationNoticeNotification),
113 GuardianWarning(GuardianWarningNotification),
115 Warning(WarningNotification),
117 ThreadArchived(ThreadArchivedNotification),
119 ThreadClosed(ThreadClosedNotification),
121 ThreadUnarchived(ThreadUnarchivedNotification),
123 ThreadGoalCleared(ThreadGoalClearedNotification),
125 ThreadNameUpdated(ThreadNameUpdatedNotification),
127 SkillsChanged(SkillsChangedNotification),
129 FsChanged(FsChangedNotification),
131 ConfigWarning(ConfigWarningNotification),
133 AccountUpdated(AccountUpdatedNotification),
135 AppListUpdated(AppListUpdatedNotification),
137 CommandExecOutputDelta(CommandExecOutputDeltaNotification),
139 ExternalAgentConfigImportCompleted(ExternalAgentConfigImportCompletedNotification),
141 FuzzyFileSearchSessionCompleted(FuzzyFileSearchSessionCompletedNotification),
143 FuzzyFileSearchSessionUpdated(FuzzyFileSearchSessionUpdatedNotification),
145 HookCompleted(HookCompletedNotification),
147 HookStarted(HookStartedNotification),
149 ItemGuardianApprovalReviewCompleted(ItemGuardianApprovalReviewCompletedNotification),
151 ItemGuardianApprovalReviewStarted(ItemGuardianApprovalReviewStartedNotification),
153 TerminalInteraction(TerminalInteractionNotification),
155 McpToolCallProgress(McpToolCallProgressNotification),
157 ModelRerouted(ModelReroutedNotification),
159 ModelVerification(ModelVerificationNotification),
161 ProcessExited(ProcessExitedNotification),
163 ProcessOutputDelta(ProcessOutputDeltaNotification),
165 ServerRequestResolved(ServerRequestResolvedNotification),
167 ContextCompacted(ContextCompactedNotification),
169 ThreadGoalUpdated(ThreadGoalUpdatedNotification),
171 ThreadRealtimeClosed(ThreadRealtimeClosedNotification),
173 ThreadRealtimeError(ThreadRealtimeErrorNotification),
175 ThreadRealtimeItemAdded(ThreadRealtimeItemAddedNotification),
177 ThreadRealtimeOutputAudioDelta(ThreadRealtimeOutputAudioDeltaNotification),
179 ThreadRealtimeSdp(ThreadRealtimeSdpNotification),
181 ThreadRealtimeStarted(ThreadRealtimeStartedNotification),
183 ThreadRealtimeTranscriptDelta(ThreadRealtimeTranscriptDeltaNotification),
185 ThreadRealtimeTranscriptDone(ThreadRealtimeTranscriptDoneNotification),
187 WindowsWorldWritableWarning(WindowsWorldWritableWarningNotification),
189 WindowsSandboxSetupCompleted(WindowsSandboxSetupCompletedNotification),
191 Unknown {
195 method: String,
196 params: Option<Value>,
197 },
198}
199
200impl Notification {
201 pub fn method(&self) -> &str {
203 match self {
204 Self::ThreadStarted(_) => methods::THREAD_STARTED,
205 Self::ThreadStatusChanged(_) => methods::THREAD_STATUS_CHANGED,
206 Self::ThreadTokenUsageUpdated(_) => methods::THREAD_TOKEN_USAGE_UPDATED,
207 Self::TurnStarted(_) => methods::TURN_STARTED,
208 Self::TurnCompleted(_) => methods::TURN_COMPLETED,
209 Self::ItemStarted(_) => methods::ITEM_STARTED,
210 Self::ItemCompleted(_) => methods::ITEM_COMPLETED,
211 Self::AgentMessageDelta(_) => methods::AGENT_MESSAGE_DELTA,
212 Self::CmdOutputDelta(_) => methods::CMD_OUTPUT_DELTA,
213 Self::FileChangeOutputDelta(_) => methods::FILE_CHANGE_OUTPUT_DELTA,
214 Self::ReasoningDelta(_) => methods::REASONING_DELTA,
215 Self::Error(_) => methods::ERROR,
216 Self::AccountRateLimitsUpdated(_) => methods::ACCOUNT_RATE_LIMITS_UPDATED,
217 Self::McpServerStartupStatusUpdated(_) => methods::MCP_SERVER_STARTUP_STATUS_UPDATED,
218 Self::RemoteControlStatusChanged(_) => methods::REMOTE_CONTROL_STATUS_CHANGED,
219 Self::McpServerOauthLoginCompleted(_) => methods::MCP_SERVER_OAUTH_LOGIN_COMPLETED,
220 Self::FileChangePatchUpdated(_) => methods::FILE_CHANGE_PATCH_UPDATED,
221 Self::PlanDelta(_) => methods::PLAN_DELTA,
222 Self::TurnPlanUpdated(_) => methods::TURN_PLAN_UPDATED,
223 Self::TurnDiffUpdated(_) => methods::TURN_DIFF_UPDATED,
224 Self::ReasoningSummaryPartAdded(_) => methods::REASONING_SUMMARY_PART_ADDED,
225 Self::ReasoningTextDelta(_) => methods::REASONING_TEXT_DELTA,
226 Self::AccountLoginCompleted(_) => methods::ACCOUNT_LOGIN_COMPLETED,
227 Self::DeprecationNotice(_) => methods::DEPRECATION_NOTICE,
228 Self::GuardianWarning(_) => methods::GUARDIAN_WARNING,
229 Self::Warning(_) => methods::WARNING,
230 Self::ThreadArchived(_) => methods::THREAD_ARCHIVED,
231 Self::ThreadClosed(_) => methods::THREAD_CLOSED,
232 Self::ThreadUnarchived(_) => methods::THREAD_UNARCHIVED,
233 Self::ThreadGoalCleared(_) => methods::THREAD_GOAL_CLEARED,
234 Self::ThreadNameUpdated(_) => methods::THREAD_NAME_UPDATED,
235 Self::SkillsChanged(_) => methods::SKILLS_CHANGED,
236 Self::FsChanged(_) => methods::FS_CHANGED,
237 Self::ConfigWarning(_) => methods::CONFIG_WARNING,
238 Self::AccountUpdated(_) => methods::ACCOUNT_UPDATED,
239 Self::AppListUpdated(_) => methods::APP_LIST_UPDATED,
240 Self::CommandExecOutputDelta(_) => methods::COMMAND_EXEC_OUTPUT_DELTA,
241 Self::ExternalAgentConfigImportCompleted(_) => {
242 methods::EXTERNAL_AGENT_CONFIG_IMPORT_COMPLETED
243 }
244 Self::FuzzyFileSearchSessionCompleted(_) => {
245 methods::FUZZY_FILE_SEARCH_SESSION_COMPLETED
246 }
247 Self::FuzzyFileSearchSessionUpdated(_) => methods::FUZZY_FILE_SEARCH_SESSION_UPDATED,
248 Self::HookCompleted(_) => methods::HOOK_COMPLETED,
249 Self::HookStarted(_) => methods::HOOK_STARTED,
250 Self::ItemGuardianApprovalReviewCompleted(_) => {
251 methods::ITEM_AUTO_APPROVAL_REVIEW_COMPLETED
252 }
253 Self::ItemGuardianApprovalReviewStarted(_) => {
254 methods::ITEM_AUTO_APPROVAL_REVIEW_STARTED
255 }
256 Self::TerminalInteraction(_) => methods::ITEM_COMMAND_EXEC_TERMINAL_INTERACTION,
257 Self::McpToolCallProgress(_) => methods::ITEM_MCP_TOOL_CALL_PROGRESS,
258 Self::ModelRerouted(_) => methods::MODEL_REROUTED,
259 Self::ModelVerification(_) => methods::MODEL_VERIFICATION,
260 Self::ProcessExited(_) => methods::PROCESS_EXITED,
261 Self::ProcessOutputDelta(_) => methods::PROCESS_OUTPUT_DELTA,
262 Self::ServerRequestResolved(_) => methods::SERVER_REQUEST_RESOLVED,
263 Self::ContextCompacted(_) => methods::THREAD_COMPACTED,
264 Self::ThreadGoalUpdated(_) => methods::THREAD_GOAL_UPDATED,
265 Self::ThreadRealtimeClosed(_) => methods::THREAD_REALTIME_CLOSED,
266 Self::ThreadRealtimeError(_) => methods::THREAD_REALTIME_ERROR,
267 Self::ThreadRealtimeItemAdded(_) => methods::THREAD_REALTIME_ITEM_ADDED,
268 Self::ThreadRealtimeOutputAudioDelta(_) => methods::THREAD_REALTIME_OUTPUT_AUDIO_DELTA,
269 Self::ThreadRealtimeSdp(_) => methods::THREAD_REALTIME_SDP,
270 Self::ThreadRealtimeStarted(_) => methods::THREAD_REALTIME_STARTED,
271 Self::ThreadRealtimeTranscriptDelta(_) => methods::THREAD_REALTIME_TRANSCRIPT_DELTA,
272 Self::ThreadRealtimeTranscriptDone(_) => methods::THREAD_REALTIME_TRANSCRIPT_DONE,
273 Self::WindowsWorldWritableWarning(_) => methods::WINDOWS_WORLD_WRITABLE_WARNING,
274 Self::WindowsSandboxSetupCompleted(_) => methods::WINDOWS_SANDBOX_SETUP_COMPLETED,
275 Self::Unknown { method, .. } => method,
276 }
277 }
278
279 pub fn is_unknown(&self) -> bool {
281 matches!(self, Self::Unknown { .. })
282 }
283
284 pub fn from_envelope(method: &str, params: Option<Value>) -> Result<Self, serde_json::Error> {
290 let params_value = params.clone().unwrap_or(Value::Null);
291 match method {
292 methods::THREAD_STARTED => {
293 serde_json::from_value(params_value).map(Self::ThreadStarted)
294 }
295 methods::THREAD_STATUS_CHANGED => {
296 serde_json::from_value(params_value).map(Self::ThreadStatusChanged)
297 }
298 methods::THREAD_TOKEN_USAGE_UPDATED => {
299 serde_json::from_value(params_value).map(Self::ThreadTokenUsageUpdated)
300 }
301 methods::TURN_STARTED => serde_json::from_value(params_value).map(Self::TurnStarted),
302 methods::TURN_COMPLETED => {
303 serde_json::from_value(params_value).map(Self::TurnCompleted)
304 }
305 methods::ITEM_STARTED => serde_json::from_value(params_value).map(Self::ItemStarted),
306 methods::ITEM_COMPLETED => {
307 serde_json::from_value(params_value).map(Self::ItemCompleted)
308 }
309 methods::AGENT_MESSAGE_DELTA => {
310 serde_json::from_value(params_value).map(Self::AgentMessageDelta)
311 }
312 methods::CMD_OUTPUT_DELTA => {
313 serde_json::from_value(params_value).map(Self::CmdOutputDelta)
314 }
315 methods::FILE_CHANGE_OUTPUT_DELTA => {
316 serde_json::from_value(params_value).map(Self::FileChangeOutputDelta)
317 }
318 methods::REASONING_DELTA => {
319 serde_json::from_value(params_value).map(Self::ReasoningDelta)
320 }
321 methods::ERROR => serde_json::from_value(params_value).map(Self::Error),
322 methods::ACCOUNT_RATE_LIMITS_UPDATED => {
323 serde_json::from_value(params_value).map(Self::AccountRateLimitsUpdated)
324 }
325 methods::MCP_SERVER_STARTUP_STATUS_UPDATED => {
326 serde_json::from_value(params_value).map(Self::McpServerStartupStatusUpdated)
327 }
328 methods::REMOTE_CONTROL_STATUS_CHANGED => {
329 serde_json::from_value(params_value).map(Self::RemoteControlStatusChanged)
330 }
331 methods::MCP_SERVER_OAUTH_LOGIN_COMPLETED => {
332 serde_json::from_value(params_value).map(Self::McpServerOauthLoginCompleted)
333 }
334 methods::FILE_CHANGE_PATCH_UPDATED => {
335 serde_json::from_value(params_value).map(Self::FileChangePatchUpdated)
336 }
337 methods::PLAN_DELTA => serde_json::from_value(params_value).map(Self::PlanDelta),
338 methods::TURN_PLAN_UPDATED => {
339 serde_json::from_value(params_value).map(Self::TurnPlanUpdated)
340 }
341 methods::TURN_DIFF_UPDATED => {
342 serde_json::from_value(params_value).map(Self::TurnDiffUpdated)
343 }
344 methods::REASONING_SUMMARY_PART_ADDED => {
345 serde_json::from_value(params_value).map(Self::ReasoningSummaryPartAdded)
346 }
347 methods::REASONING_TEXT_DELTA => {
348 serde_json::from_value(params_value).map(Self::ReasoningTextDelta)
349 }
350 methods::ACCOUNT_LOGIN_COMPLETED => {
351 serde_json::from_value(params_value).map(Self::AccountLoginCompleted)
352 }
353 methods::DEPRECATION_NOTICE => {
354 serde_json::from_value(params_value).map(Self::DeprecationNotice)
355 }
356 methods::GUARDIAN_WARNING => {
357 serde_json::from_value(params_value).map(Self::GuardianWarning)
358 }
359 methods::WARNING => serde_json::from_value(params_value).map(Self::Warning),
360 methods::THREAD_ARCHIVED => {
361 serde_json::from_value(params_value).map(Self::ThreadArchived)
362 }
363 methods::THREAD_CLOSED => serde_json::from_value(params_value).map(Self::ThreadClosed),
364 methods::THREAD_UNARCHIVED => {
365 serde_json::from_value(params_value).map(Self::ThreadUnarchived)
366 }
367 methods::THREAD_GOAL_CLEARED => {
368 serde_json::from_value(params_value).map(Self::ThreadGoalCleared)
369 }
370 methods::THREAD_NAME_UPDATED => {
371 serde_json::from_value(params_value).map(Self::ThreadNameUpdated)
372 }
373 methods::SKILLS_CHANGED => {
374 serde_json::from_value(params_value).map(Self::SkillsChanged)
375 }
376 methods::FS_CHANGED => serde_json::from_value(params_value).map(Self::FsChanged),
377 methods::CONFIG_WARNING => {
378 serde_json::from_value(params_value).map(Self::ConfigWarning)
379 }
380 methods::ACCOUNT_UPDATED => {
381 serde_json::from_value(params_value).map(Self::AccountUpdated)
382 }
383 methods::APP_LIST_UPDATED => {
384 serde_json::from_value(params_value).map(Self::AppListUpdated)
385 }
386 methods::COMMAND_EXEC_OUTPUT_DELTA => {
387 serde_json::from_value(params_value).map(Self::CommandExecOutputDelta)
388 }
389 methods::EXTERNAL_AGENT_CONFIG_IMPORT_COMPLETED => {
390 serde_json::from_value(params_value).map(Self::ExternalAgentConfigImportCompleted)
391 }
392 methods::FUZZY_FILE_SEARCH_SESSION_COMPLETED => {
393 serde_json::from_value(params_value).map(Self::FuzzyFileSearchSessionCompleted)
394 }
395 methods::FUZZY_FILE_SEARCH_SESSION_UPDATED => {
396 serde_json::from_value(params_value).map(Self::FuzzyFileSearchSessionUpdated)
397 }
398 methods::HOOK_COMPLETED => {
399 serde_json::from_value(params_value).map(Self::HookCompleted)
400 }
401 methods::HOOK_STARTED => serde_json::from_value(params_value).map(Self::HookStarted),
402 methods::ITEM_AUTO_APPROVAL_REVIEW_COMPLETED => {
403 serde_json::from_value(params_value).map(Self::ItemGuardianApprovalReviewCompleted)
404 }
405 methods::ITEM_AUTO_APPROVAL_REVIEW_STARTED => {
406 serde_json::from_value(params_value).map(Self::ItemGuardianApprovalReviewStarted)
407 }
408 methods::ITEM_COMMAND_EXEC_TERMINAL_INTERACTION => {
409 serde_json::from_value(params_value).map(Self::TerminalInteraction)
410 }
411 methods::ITEM_MCP_TOOL_CALL_PROGRESS => {
412 serde_json::from_value(params_value).map(Self::McpToolCallProgress)
413 }
414 methods::MODEL_REROUTED => {
415 serde_json::from_value(params_value).map(Self::ModelRerouted)
416 }
417 methods::MODEL_VERIFICATION => {
418 serde_json::from_value(params_value).map(Self::ModelVerification)
419 }
420 methods::PROCESS_EXITED => {
421 serde_json::from_value(params_value).map(Self::ProcessExited)
422 }
423 methods::PROCESS_OUTPUT_DELTA => {
424 serde_json::from_value(params_value).map(Self::ProcessOutputDelta)
425 }
426 methods::SERVER_REQUEST_RESOLVED => {
427 serde_json::from_value(params_value).map(Self::ServerRequestResolved)
428 }
429 methods::THREAD_COMPACTED => {
430 serde_json::from_value(params_value).map(Self::ContextCompacted)
431 }
432 methods::THREAD_GOAL_UPDATED => {
433 serde_json::from_value(params_value).map(Self::ThreadGoalUpdated)
434 }
435 methods::THREAD_REALTIME_CLOSED => {
436 serde_json::from_value(params_value).map(Self::ThreadRealtimeClosed)
437 }
438 methods::THREAD_REALTIME_ERROR => {
439 serde_json::from_value(params_value).map(Self::ThreadRealtimeError)
440 }
441 methods::THREAD_REALTIME_ITEM_ADDED => {
442 serde_json::from_value(params_value).map(Self::ThreadRealtimeItemAdded)
443 }
444 methods::THREAD_REALTIME_OUTPUT_AUDIO_DELTA => {
445 serde_json::from_value(params_value).map(Self::ThreadRealtimeOutputAudioDelta)
446 }
447 methods::THREAD_REALTIME_SDP => {
448 serde_json::from_value(params_value).map(Self::ThreadRealtimeSdp)
449 }
450 methods::THREAD_REALTIME_STARTED => {
451 serde_json::from_value(params_value).map(Self::ThreadRealtimeStarted)
452 }
453 methods::THREAD_REALTIME_TRANSCRIPT_DELTA => {
454 serde_json::from_value(params_value).map(Self::ThreadRealtimeTranscriptDelta)
455 }
456 methods::THREAD_REALTIME_TRANSCRIPT_DONE => {
457 serde_json::from_value(params_value).map(Self::ThreadRealtimeTranscriptDone)
458 }
459 methods::WINDOWS_WORLD_WRITABLE_WARNING => {
460 serde_json::from_value(params_value).map(Self::WindowsWorldWritableWarning)
461 }
462 methods::WINDOWS_SANDBOX_SETUP_COMPLETED => {
463 serde_json::from_value(params_value).map(Self::WindowsSandboxSetupCompleted)
464 }
465 _ => Ok(Self::Unknown {
466 method: method.to_string(),
467 params,
468 }),
469 }
470 }
471
472 pub fn into_envelope(self) -> Result<(String, Option<Value>), serde_json::Error> {
474 fn pack<T: Serialize>(
475 method: &str,
476 v: &T,
477 ) -> Result<(String, Option<Value>), serde_json::Error> {
478 Ok((method.to_string(), Some(serde_json::to_value(v)?)))
479 }
480 match &self {
481 Self::ThreadStarted(v) => pack(methods::THREAD_STARTED, v),
482 Self::ThreadStatusChanged(v) => pack(methods::THREAD_STATUS_CHANGED, v),
483 Self::ThreadTokenUsageUpdated(v) => pack(methods::THREAD_TOKEN_USAGE_UPDATED, v),
484 Self::TurnStarted(v) => pack(methods::TURN_STARTED, v),
485 Self::TurnCompleted(v) => pack(methods::TURN_COMPLETED, v),
486 Self::ItemStarted(v) => pack(methods::ITEM_STARTED, v),
487 Self::ItemCompleted(v) => pack(methods::ITEM_COMPLETED, v),
488 Self::AgentMessageDelta(v) => pack(methods::AGENT_MESSAGE_DELTA, v),
489 Self::CmdOutputDelta(v) => pack(methods::CMD_OUTPUT_DELTA, v),
490 Self::FileChangeOutputDelta(v) => pack(methods::FILE_CHANGE_OUTPUT_DELTA, v),
491 Self::ReasoningDelta(v) => pack(methods::REASONING_DELTA, v),
492 Self::Error(v) => pack(methods::ERROR, v),
493 Self::AccountRateLimitsUpdated(v) => pack(methods::ACCOUNT_RATE_LIMITS_UPDATED, v),
494 Self::McpServerStartupStatusUpdated(v) => {
495 pack(methods::MCP_SERVER_STARTUP_STATUS_UPDATED, v)
496 }
497 Self::RemoteControlStatusChanged(v) => pack(methods::REMOTE_CONTROL_STATUS_CHANGED, v),
498 Self::McpServerOauthLoginCompleted(v) => {
499 pack(methods::MCP_SERVER_OAUTH_LOGIN_COMPLETED, v)
500 }
501 Self::FileChangePatchUpdated(v) => pack(methods::FILE_CHANGE_PATCH_UPDATED, v),
502 Self::PlanDelta(v) => pack(methods::PLAN_DELTA, v),
503 Self::TurnPlanUpdated(v) => pack(methods::TURN_PLAN_UPDATED, v),
504 Self::TurnDiffUpdated(v) => pack(methods::TURN_DIFF_UPDATED, v),
505 Self::ReasoningSummaryPartAdded(v) => pack(methods::REASONING_SUMMARY_PART_ADDED, v),
506 Self::ReasoningTextDelta(v) => pack(methods::REASONING_TEXT_DELTA, v),
507 Self::AccountLoginCompleted(v) => pack(methods::ACCOUNT_LOGIN_COMPLETED, v),
508 Self::DeprecationNotice(v) => pack(methods::DEPRECATION_NOTICE, v),
509 Self::GuardianWarning(v) => pack(methods::GUARDIAN_WARNING, v),
510 Self::Warning(v) => pack(methods::WARNING, v),
511 Self::ThreadArchived(v) => pack(methods::THREAD_ARCHIVED, v),
512 Self::ThreadClosed(v) => pack(methods::THREAD_CLOSED, v),
513 Self::ThreadUnarchived(v) => pack(methods::THREAD_UNARCHIVED, v),
514 Self::ThreadGoalCleared(v) => pack(methods::THREAD_GOAL_CLEARED, v),
515 Self::ThreadNameUpdated(v) => pack(methods::THREAD_NAME_UPDATED, v),
516 Self::SkillsChanged(v) => pack(methods::SKILLS_CHANGED, v),
517 Self::FsChanged(v) => pack(methods::FS_CHANGED, v),
518 Self::ConfigWarning(v) => pack(methods::CONFIG_WARNING, v),
519 Self::AccountUpdated(v) => pack(methods::ACCOUNT_UPDATED, v),
520 Self::AppListUpdated(v) => pack(methods::APP_LIST_UPDATED, v),
521 Self::CommandExecOutputDelta(v) => pack(methods::COMMAND_EXEC_OUTPUT_DELTA, v),
522 Self::ExternalAgentConfigImportCompleted(v) => {
523 pack(methods::EXTERNAL_AGENT_CONFIG_IMPORT_COMPLETED, v)
524 }
525 Self::FuzzyFileSearchSessionCompleted(v) => {
526 pack(methods::FUZZY_FILE_SEARCH_SESSION_COMPLETED, v)
527 }
528 Self::FuzzyFileSearchSessionUpdated(v) => {
529 pack(methods::FUZZY_FILE_SEARCH_SESSION_UPDATED, v)
530 }
531 Self::HookCompleted(v) => pack(methods::HOOK_COMPLETED, v),
532 Self::HookStarted(v) => pack(methods::HOOK_STARTED, v),
533 Self::ItemGuardianApprovalReviewCompleted(v) => {
534 pack(methods::ITEM_AUTO_APPROVAL_REVIEW_COMPLETED, v)
535 }
536 Self::ItemGuardianApprovalReviewStarted(v) => {
537 pack(methods::ITEM_AUTO_APPROVAL_REVIEW_STARTED, v)
538 }
539 Self::TerminalInteraction(v) => {
540 pack(methods::ITEM_COMMAND_EXEC_TERMINAL_INTERACTION, v)
541 }
542 Self::McpToolCallProgress(v) => pack(methods::ITEM_MCP_TOOL_CALL_PROGRESS, v),
543 Self::ModelRerouted(v) => pack(methods::MODEL_REROUTED, v),
544 Self::ModelVerification(v) => pack(methods::MODEL_VERIFICATION, v),
545 Self::ProcessExited(v) => pack(methods::PROCESS_EXITED, v),
546 Self::ProcessOutputDelta(v) => pack(methods::PROCESS_OUTPUT_DELTA, v),
547 Self::ServerRequestResolved(v) => pack(methods::SERVER_REQUEST_RESOLVED, v),
548 Self::ContextCompacted(v) => pack(methods::THREAD_COMPACTED, v),
549 Self::ThreadGoalUpdated(v) => pack(methods::THREAD_GOAL_UPDATED, v),
550 Self::ThreadRealtimeClosed(v) => pack(methods::THREAD_REALTIME_CLOSED, v),
551 Self::ThreadRealtimeError(v) => pack(methods::THREAD_REALTIME_ERROR, v),
552 Self::ThreadRealtimeItemAdded(v) => pack(methods::THREAD_REALTIME_ITEM_ADDED, v),
553 Self::ThreadRealtimeOutputAudioDelta(v) => {
554 pack(methods::THREAD_REALTIME_OUTPUT_AUDIO_DELTA, v)
555 }
556 Self::ThreadRealtimeSdp(v) => pack(methods::THREAD_REALTIME_SDP, v),
557 Self::ThreadRealtimeStarted(v) => pack(methods::THREAD_REALTIME_STARTED, v),
558 Self::ThreadRealtimeTranscriptDelta(v) => {
559 pack(methods::THREAD_REALTIME_TRANSCRIPT_DELTA, v)
560 }
561 Self::ThreadRealtimeTranscriptDone(v) => {
562 pack(methods::THREAD_REALTIME_TRANSCRIPT_DONE, v)
563 }
564 Self::WindowsWorldWritableWarning(v) => {
565 pack(methods::WINDOWS_WORLD_WRITABLE_WARNING, v)
566 }
567 Self::WindowsSandboxSetupCompleted(v) => {
568 pack(methods::WINDOWS_SANDBOX_SETUP_COMPLETED, v)
569 }
570 Self::Unknown { method, params } => Ok((method.clone(), params.clone())),
571 }
572 }
573}
574
575impl Serialize for Notification {
576 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
577 let (method, params) = self
578 .clone()
579 .into_envelope()
580 .map_err(serde::ser::Error::custom)?;
581 let mut env = serde_json::Map::new();
582 env.insert("method".to_string(), Value::String(method));
583 if let Some(p) = params {
584 env.insert("params".to_string(), p);
585 }
586 Value::Object(env).serialize(serializer)
587 }
588}
589
590impl<'de> Deserialize<'de> for Notification {
591 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
592 let value = Value::deserialize(deserializer)?;
593 let method = value
594 .get("method")
595 .and_then(|v| v.as_str())
596 .ok_or_else(|| serde::de::Error::missing_field("method"))?
597 .to_string();
598 let params = value.get("params").cloned();
599 Self::from_envelope(&method, params).map_err(serde::de::Error::custom)
600 }
601}
602
603#[derive(Debug, Clone)]
610pub enum ServerRequest {
611 CmdExecApproval(CommandExecutionApprovalParams),
613 FileChangeApproval(FileChangeApprovalParams),
615 Unknown {
617 method: String,
618 params: Option<Value>,
619 },
620}
621
622impl ServerRequest {
623 pub fn method(&self) -> &str {
625 match self {
626 Self::CmdExecApproval(_) => methods::CMD_EXEC_APPROVAL,
627 Self::FileChangeApproval(_) => methods::FILE_CHANGE_APPROVAL,
628 Self::Unknown { method, .. } => method,
629 }
630 }
631
632 pub fn is_unknown(&self) -> bool {
634 matches!(self, Self::Unknown { .. })
635 }
636
637 pub fn from_envelope(method: &str, params: Option<Value>) -> Result<Self, serde_json::Error> {
639 let params_value = params.clone().unwrap_or(Value::Null);
640 match method {
641 methods::CMD_EXEC_APPROVAL => {
642 serde_json::from_value(params_value).map(Self::CmdExecApproval)
643 }
644 methods::FILE_CHANGE_APPROVAL => {
645 serde_json::from_value(params_value).map(Self::FileChangeApproval)
646 }
647 _ => Ok(Self::Unknown {
648 method: method.to_string(),
649 params,
650 }),
651 }
652 }
653}
654
655#[derive(Debug, Clone)]
662pub enum ServerMessage {
663 Notification(Notification),
665 Request {
667 id: RequestId,
668 request: ServerRequest,
669 },
670}
671
672impl ServerMessage {
673 pub fn is_unknown(&self) -> bool {
675 match self {
676 Self::Notification(n) => n.is_unknown(),
677 Self::Request { request, .. } => request.is_unknown(),
678 }
679 }
680}
681
682#[cfg(test)]
683mod tests {
684 use super::*;
685
686 #[test]
687 fn test_notification_unknown_method_routes_to_unknown_variant() {
688 let n = Notification::from_envelope("foo/bar", Some(serde_json::json!({"x": 1})))
689 .expect("unknown methods do not error");
690 match n {
691 Notification::Unknown { method, params } => {
692 assert_eq!(method, "foo/bar");
693 assert_eq!(params, Some(serde_json::json!({"x": 1})));
694 }
695 other => panic!("expected Unknown, got {:?}", other),
696 }
697 }
698
699 #[test]
700 fn test_notification_known_method_with_bad_params_errors() {
701 let err = Notification::from_envelope("thread/started", Some(serde_json::json!({})));
703 assert!(err.is_err());
704 }
705
706 #[test]
707 fn test_notification_round_trip_envelope() {
708 let wire = serde_json::json!({
709 "method": "item/agentMessage/delta",
710 "params": {"threadId": "t1", "itemId": "i1", "delta": "hi"},
711 });
712 let n: Notification = serde_json::from_value(wire.clone()).unwrap();
713 assert!(matches!(n, Notification::AgentMessageDelta(_)));
714 let back = serde_json::to_value(&n).unwrap();
715 assert_eq!(back, wire);
716 }
717}