1use crate::jsonrpc::RequestId;
27use crate::protocol::{
28 methods, AccountLoginCompletedNotification, AccountRateLimitsUpdatedNotification,
29 AccountUpdatedNotification, AgentMessageDeltaNotification, AppListUpdatedNotification,
30 CommandExecOutputDeltaNotification, CommandExecutionOutputDeltaNotification,
31 CommandExecutionRequestApprovalParams, ConfigWarningNotification, ContextCompactedNotification,
32 DeprecationNoticeNotification, ErrorNotification,
33 ExternalAgentConfigImportCompletedNotification, FileChangeOutputDeltaNotification,
34 FileChangePatchUpdatedNotification, FileChangeRequestApprovalParams, FsChangedNotification,
35 FuzzyFileSearchSessionCompletedNotification, FuzzyFileSearchSessionUpdatedNotification,
36 GuardianWarningNotification, HookCompletedNotification, HookStartedNotification,
37 ItemCompletedNotification, ItemGuardianApprovalReviewCompletedNotification,
38 ItemGuardianApprovalReviewStartedNotification, ItemStartedNotification,
39 McpServerOauthLoginCompletedNotification, McpServerStatusUpdatedNotification,
40 McpToolCallProgressNotification, ModelReroutedNotification, ModelVerificationNotification,
41 PlanDeltaNotification, ProcessExitedNotification, ProcessOutputDeltaNotification,
42 ReasoningSummaryPartAddedNotification, ReasoningSummaryTextDeltaNotification,
43 ReasoningTextDeltaNotification, RemoteControlStatusChangedNotification,
44 ServerRequestResolvedNotification, SkillsChangedNotification, TerminalInteractionNotification,
45 ThreadArchivedNotification, ThreadClosedNotification, ThreadGoalClearedNotification,
46 ThreadGoalUpdatedNotification, ThreadNameUpdatedNotification, ThreadRealtimeClosedNotification,
47 ThreadRealtimeErrorNotification, ThreadRealtimeItemAddedNotification,
48 ThreadRealtimeOutputAudioDeltaNotification, ThreadRealtimeSdpNotification,
49 ThreadRealtimeStartedNotification, ThreadRealtimeTranscriptDeltaNotification,
50 ThreadRealtimeTranscriptDoneNotification, ThreadStartedNotification,
51 ThreadStatusChangedNotification, ThreadTokenUsageUpdatedNotification,
52 ThreadUnarchivedNotification, TurnCompletedNotification, TurnDiffUpdatedNotification,
53 TurnPlanUpdatedNotification, TurnStartedNotification, WarningNotification,
54 WindowsSandboxSetupCompletedNotification, WindowsWorldWritableWarningNotification,
55};
56use serde::{Deserialize, Deserializer, Serialize, Serializer};
57use serde_json::Value;
58
59#[derive(Debug, Clone)]
65pub enum Notification {
66 ThreadStarted(ThreadStartedNotification),
68 ThreadStatusChanged(ThreadStatusChangedNotification),
70 ThreadTokenUsageUpdated(ThreadTokenUsageUpdatedNotification),
72 TurnStarted(TurnStartedNotification),
74 TurnCompleted(TurnCompletedNotification),
76 ItemStarted(ItemStartedNotification),
78 ItemCompleted(ItemCompletedNotification),
80 AgentMessageDelta(AgentMessageDeltaNotification),
82 CmdOutputDelta(CommandExecutionOutputDeltaNotification),
84 FileChangeOutputDelta(FileChangeOutputDeltaNotification),
86 ReasoningDelta(ReasoningSummaryTextDeltaNotification),
88 Error(ErrorNotification),
90 AccountRateLimitsUpdated(AccountRateLimitsUpdatedNotification),
92 McpServerStartupStatusUpdated(McpServerStatusUpdatedNotification),
94 RemoteControlStatusChanged(RemoteControlStatusChangedNotification),
96 McpServerOauthLoginCompleted(McpServerOauthLoginCompletedNotification),
98 FileChangePatchUpdated(FileChangePatchUpdatedNotification),
100 PlanDelta(PlanDeltaNotification),
102 TurnPlanUpdated(TurnPlanUpdatedNotification),
104 TurnDiffUpdated(TurnDiffUpdatedNotification),
106 ReasoningSummaryPartAdded(ReasoningSummaryPartAddedNotification),
108 ReasoningTextDelta(ReasoningTextDeltaNotification),
110 AccountLoginCompleted(AccountLoginCompletedNotification),
112 DeprecationNotice(DeprecationNoticeNotification),
114 GuardianWarning(GuardianWarningNotification),
116 Warning(WarningNotification),
118 ThreadArchived(ThreadArchivedNotification),
120 ThreadClosed(ThreadClosedNotification),
122 ThreadUnarchived(ThreadUnarchivedNotification),
124 ThreadGoalCleared(ThreadGoalClearedNotification),
126 ThreadNameUpdated(ThreadNameUpdatedNotification),
128 SkillsChanged(SkillsChangedNotification),
130 FsChanged(FsChangedNotification),
132 ConfigWarning(ConfigWarningNotification),
134 AccountUpdated(AccountUpdatedNotification),
136 AppListUpdated(AppListUpdatedNotification),
138 CommandExecOutputDelta(CommandExecOutputDeltaNotification),
140 ExternalAgentConfigImportCompleted(ExternalAgentConfigImportCompletedNotification),
142 FuzzyFileSearchSessionCompleted(FuzzyFileSearchSessionCompletedNotification),
144 FuzzyFileSearchSessionUpdated(FuzzyFileSearchSessionUpdatedNotification),
146 HookCompleted(HookCompletedNotification),
148 HookStarted(HookStartedNotification),
150 ItemGuardianApprovalReviewCompleted(ItemGuardianApprovalReviewCompletedNotification),
152 ItemGuardianApprovalReviewStarted(ItemGuardianApprovalReviewStartedNotification),
154 TerminalInteraction(TerminalInteractionNotification),
156 McpToolCallProgress(McpToolCallProgressNotification),
158 ModelRerouted(ModelReroutedNotification),
160 ModelVerification(ModelVerificationNotification),
162 ProcessExited(ProcessExitedNotification),
164 ProcessOutputDelta(ProcessOutputDeltaNotification),
166 ServerRequestResolved(ServerRequestResolvedNotification),
168 ContextCompacted(ContextCompactedNotification),
170 ThreadGoalUpdated(ThreadGoalUpdatedNotification),
172 ThreadRealtimeClosed(ThreadRealtimeClosedNotification),
174 ThreadRealtimeError(ThreadRealtimeErrorNotification),
176 ThreadRealtimeItemAdded(ThreadRealtimeItemAddedNotification),
178 ThreadRealtimeOutputAudioDelta(ThreadRealtimeOutputAudioDeltaNotification),
180 ThreadRealtimeSdp(ThreadRealtimeSdpNotification),
182 ThreadRealtimeStarted(ThreadRealtimeStartedNotification),
184 ThreadRealtimeTranscriptDelta(ThreadRealtimeTranscriptDeltaNotification),
186 ThreadRealtimeTranscriptDone(ThreadRealtimeTranscriptDoneNotification),
188 WindowsWorldWritableWarning(WindowsWorldWritableWarningNotification),
190 WindowsSandboxSetupCompleted(WindowsSandboxSetupCompletedNotification),
192 Unknown {
196 method: String,
197 params: Option<Value>,
198 },
199}
200
201impl Notification {
202 pub fn method(&self) -> &str {
204 match self {
205 Self::ThreadStarted(_) => methods::THREAD_STARTED,
206 Self::ThreadStatusChanged(_) => methods::THREAD_STATUS_CHANGED,
207 Self::ThreadTokenUsageUpdated(_) => methods::THREAD_TOKEN_USAGE_UPDATED,
208 Self::TurnStarted(_) => methods::TURN_STARTED,
209 Self::TurnCompleted(_) => methods::TURN_COMPLETED,
210 Self::ItemStarted(_) => methods::ITEM_STARTED,
211 Self::ItemCompleted(_) => methods::ITEM_COMPLETED,
212 Self::AgentMessageDelta(_) => methods::AGENT_MESSAGE_DELTA,
213 Self::CmdOutputDelta(_) => methods::CMD_OUTPUT_DELTA,
214 Self::FileChangeOutputDelta(_) => methods::FILE_CHANGE_OUTPUT_DELTA,
215 Self::ReasoningDelta(_) => methods::REASONING_DELTA,
216 Self::Error(_) => methods::ERROR,
217 Self::AccountRateLimitsUpdated(_) => methods::ACCOUNT_RATE_LIMITS_UPDATED,
218 Self::McpServerStartupStatusUpdated(_) => methods::MCP_SERVER_STARTUP_STATUS_UPDATED,
219 Self::RemoteControlStatusChanged(_) => methods::REMOTE_CONTROL_STATUS_CHANGED,
220 Self::McpServerOauthLoginCompleted(_) => methods::MCP_SERVER_OAUTH_LOGIN_COMPLETED,
221 Self::FileChangePatchUpdated(_) => methods::FILE_CHANGE_PATCH_UPDATED,
222 Self::PlanDelta(_) => methods::PLAN_DELTA,
223 Self::TurnPlanUpdated(_) => methods::TURN_PLAN_UPDATED,
224 Self::TurnDiffUpdated(_) => methods::TURN_DIFF_UPDATED,
225 Self::ReasoningSummaryPartAdded(_) => methods::REASONING_SUMMARY_PART_ADDED,
226 Self::ReasoningTextDelta(_) => methods::REASONING_TEXT_DELTA,
227 Self::AccountLoginCompleted(_) => methods::ACCOUNT_LOGIN_COMPLETED,
228 Self::DeprecationNotice(_) => methods::DEPRECATION_NOTICE,
229 Self::GuardianWarning(_) => methods::GUARDIAN_WARNING,
230 Self::Warning(_) => methods::WARNING,
231 Self::ThreadArchived(_) => methods::THREAD_ARCHIVED,
232 Self::ThreadClosed(_) => methods::THREAD_CLOSED,
233 Self::ThreadUnarchived(_) => methods::THREAD_UNARCHIVED,
234 Self::ThreadGoalCleared(_) => methods::THREAD_GOAL_CLEARED,
235 Self::ThreadNameUpdated(_) => methods::THREAD_NAME_UPDATED,
236 Self::SkillsChanged(_) => methods::SKILLS_CHANGED,
237 Self::FsChanged(_) => methods::FS_CHANGED,
238 Self::ConfigWarning(_) => methods::CONFIG_WARNING,
239 Self::AccountUpdated(_) => methods::ACCOUNT_UPDATED,
240 Self::AppListUpdated(_) => methods::APP_LIST_UPDATED,
241 Self::CommandExecOutputDelta(_) => methods::COMMAND_EXEC_OUTPUT_DELTA,
242 Self::ExternalAgentConfigImportCompleted(_) => {
243 methods::EXTERNAL_AGENT_CONFIG_IMPORT_COMPLETED
244 }
245 Self::FuzzyFileSearchSessionCompleted(_) => {
246 methods::FUZZY_FILE_SEARCH_SESSION_COMPLETED
247 }
248 Self::FuzzyFileSearchSessionUpdated(_) => methods::FUZZY_FILE_SEARCH_SESSION_UPDATED,
249 Self::HookCompleted(_) => methods::HOOK_COMPLETED,
250 Self::HookStarted(_) => methods::HOOK_STARTED,
251 Self::ItemGuardianApprovalReviewCompleted(_) => {
252 methods::ITEM_AUTO_APPROVAL_REVIEW_COMPLETED
253 }
254 Self::ItemGuardianApprovalReviewStarted(_) => {
255 methods::ITEM_AUTO_APPROVAL_REVIEW_STARTED
256 }
257 Self::TerminalInteraction(_) => methods::ITEM_COMMAND_EXEC_TERMINAL_INTERACTION,
258 Self::McpToolCallProgress(_) => methods::ITEM_MCP_TOOL_CALL_PROGRESS,
259 Self::ModelRerouted(_) => methods::MODEL_REROUTED,
260 Self::ModelVerification(_) => methods::MODEL_VERIFICATION,
261 Self::ProcessExited(_) => methods::PROCESS_EXITED,
262 Self::ProcessOutputDelta(_) => methods::PROCESS_OUTPUT_DELTA,
263 Self::ServerRequestResolved(_) => methods::SERVER_REQUEST_RESOLVED,
264 Self::ContextCompacted(_) => methods::THREAD_COMPACTED,
265 Self::ThreadGoalUpdated(_) => methods::THREAD_GOAL_UPDATED,
266 Self::ThreadRealtimeClosed(_) => methods::THREAD_REALTIME_CLOSED,
267 Self::ThreadRealtimeError(_) => methods::THREAD_REALTIME_ERROR,
268 Self::ThreadRealtimeItemAdded(_) => methods::THREAD_REALTIME_ITEM_ADDED,
269 Self::ThreadRealtimeOutputAudioDelta(_) => methods::THREAD_REALTIME_OUTPUT_AUDIO_DELTA,
270 Self::ThreadRealtimeSdp(_) => methods::THREAD_REALTIME_SDP,
271 Self::ThreadRealtimeStarted(_) => methods::THREAD_REALTIME_STARTED,
272 Self::ThreadRealtimeTranscriptDelta(_) => methods::THREAD_REALTIME_TRANSCRIPT_DELTA,
273 Self::ThreadRealtimeTranscriptDone(_) => methods::THREAD_REALTIME_TRANSCRIPT_DONE,
274 Self::WindowsWorldWritableWarning(_) => methods::WINDOWS_WORLD_WRITABLE_WARNING,
275 Self::WindowsSandboxSetupCompleted(_) => methods::WINDOWS_SANDBOX_SETUP_COMPLETED,
276 Self::Unknown { method, .. } => method,
277 }
278 }
279
280 pub fn is_unknown(&self) -> bool {
282 matches!(self, Self::Unknown { .. })
283 }
284
285 pub fn from_envelope(method: &str, params: Option<Value>) -> Result<Self, serde_json::Error> {
291 let params_value = params.clone().unwrap_or(Value::Null);
292 match method {
293 methods::THREAD_STARTED => {
294 serde_json::from_value(params_value).map(Self::ThreadStarted)
295 }
296 methods::THREAD_STATUS_CHANGED => {
297 serde_json::from_value(params_value).map(Self::ThreadStatusChanged)
298 }
299 methods::THREAD_TOKEN_USAGE_UPDATED => {
300 serde_json::from_value(params_value).map(Self::ThreadTokenUsageUpdated)
301 }
302 methods::TURN_STARTED => serde_json::from_value(params_value).map(Self::TurnStarted),
303 methods::TURN_COMPLETED => {
304 serde_json::from_value(params_value).map(Self::TurnCompleted)
305 }
306 methods::ITEM_STARTED => serde_json::from_value(params_value).map(Self::ItemStarted),
307 methods::ITEM_COMPLETED => {
308 serde_json::from_value(params_value).map(Self::ItemCompleted)
309 }
310 methods::AGENT_MESSAGE_DELTA => {
311 serde_json::from_value(params_value).map(Self::AgentMessageDelta)
312 }
313 methods::CMD_OUTPUT_DELTA => {
314 serde_json::from_value(params_value).map(Self::CmdOutputDelta)
315 }
316 methods::FILE_CHANGE_OUTPUT_DELTA => {
317 serde_json::from_value(params_value).map(Self::FileChangeOutputDelta)
318 }
319 methods::REASONING_DELTA => {
320 serde_json::from_value(params_value).map(Self::ReasoningDelta)
321 }
322 methods::ERROR => serde_json::from_value(params_value).map(Self::Error),
323 methods::ACCOUNT_RATE_LIMITS_UPDATED => {
324 serde_json::from_value(params_value).map(Self::AccountRateLimitsUpdated)
325 }
326 methods::MCP_SERVER_STARTUP_STATUS_UPDATED => {
327 serde_json::from_value(params_value).map(Self::McpServerStartupStatusUpdated)
328 }
329 methods::REMOTE_CONTROL_STATUS_CHANGED => {
330 serde_json::from_value(params_value).map(Self::RemoteControlStatusChanged)
331 }
332 methods::MCP_SERVER_OAUTH_LOGIN_COMPLETED => {
333 serde_json::from_value(params_value).map(Self::McpServerOauthLoginCompleted)
334 }
335 methods::FILE_CHANGE_PATCH_UPDATED => {
336 serde_json::from_value(params_value).map(Self::FileChangePatchUpdated)
337 }
338 methods::PLAN_DELTA => serde_json::from_value(params_value).map(Self::PlanDelta),
339 methods::TURN_PLAN_UPDATED => {
340 serde_json::from_value(params_value).map(Self::TurnPlanUpdated)
341 }
342 methods::TURN_DIFF_UPDATED => {
343 serde_json::from_value(params_value).map(Self::TurnDiffUpdated)
344 }
345 methods::REASONING_SUMMARY_PART_ADDED => {
346 serde_json::from_value(params_value).map(Self::ReasoningSummaryPartAdded)
347 }
348 methods::REASONING_TEXT_DELTA => {
349 serde_json::from_value(params_value).map(Self::ReasoningTextDelta)
350 }
351 methods::ACCOUNT_LOGIN_COMPLETED => {
352 serde_json::from_value(params_value).map(Self::AccountLoginCompleted)
353 }
354 methods::DEPRECATION_NOTICE => {
355 serde_json::from_value(params_value).map(Self::DeprecationNotice)
356 }
357 methods::GUARDIAN_WARNING => {
358 serde_json::from_value(params_value).map(Self::GuardianWarning)
359 }
360 methods::WARNING => serde_json::from_value(params_value).map(Self::Warning),
361 methods::THREAD_ARCHIVED => {
362 serde_json::from_value(params_value).map(Self::ThreadArchived)
363 }
364 methods::THREAD_CLOSED => serde_json::from_value(params_value).map(Self::ThreadClosed),
365 methods::THREAD_UNARCHIVED => {
366 serde_json::from_value(params_value).map(Self::ThreadUnarchived)
367 }
368 methods::THREAD_GOAL_CLEARED => {
369 serde_json::from_value(params_value).map(Self::ThreadGoalCleared)
370 }
371 methods::THREAD_NAME_UPDATED => {
372 serde_json::from_value(params_value).map(Self::ThreadNameUpdated)
373 }
374 methods::SKILLS_CHANGED => {
375 serde_json::from_value(params_value).map(Self::SkillsChanged)
376 }
377 methods::FS_CHANGED => serde_json::from_value(params_value).map(Self::FsChanged),
378 methods::CONFIG_WARNING => {
379 serde_json::from_value(params_value).map(Self::ConfigWarning)
380 }
381 methods::ACCOUNT_UPDATED => {
382 serde_json::from_value(params_value).map(Self::AccountUpdated)
383 }
384 methods::APP_LIST_UPDATED => {
385 serde_json::from_value(params_value).map(Self::AppListUpdated)
386 }
387 methods::COMMAND_EXEC_OUTPUT_DELTA => {
388 serde_json::from_value(params_value).map(Self::CommandExecOutputDelta)
389 }
390 methods::EXTERNAL_AGENT_CONFIG_IMPORT_COMPLETED => {
391 serde_json::from_value(params_value).map(Self::ExternalAgentConfigImportCompleted)
392 }
393 methods::FUZZY_FILE_SEARCH_SESSION_COMPLETED => {
394 serde_json::from_value(params_value).map(Self::FuzzyFileSearchSessionCompleted)
395 }
396 methods::FUZZY_FILE_SEARCH_SESSION_UPDATED => {
397 serde_json::from_value(params_value).map(Self::FuzzyFileSearchSessionUpdated)
398 }
399 methods::HOOK_COMPLETED => {
400 serde_json::from_value(params_value).map(Self::HookCompleted)
401 }
402 methods::HOOK_STARTED => serde_json::from_value(params_value).map(Self::HookStarted),
403 methods::ITEM_AUTO_APPROVAL_REVIEW_COMPLETED => {
404 serde_json::from_value(params_value).map(Self::ItemGuardianApprovalReviewCompleted)
405 }
406 methods::ITEM_AUTO_APPROVAL_REVIEW_STARTED => {
407 serde_json::from_value(params_value).map(Self::ItemGuardianApprovalReviewStarted)
408 }
409 methods::ITEM_COMMAND_EXEC_TERMINAL_INTERACTION => {
410 serde_json::from_value(params_value).map(Self::TerminalInteraction)
411 }
412 methods::ITEM_MCP_TOOL_CALL_PROGRESS => {
413 serde_json::from_value(params_value).map(Self::McpToolCallProgress)
414 }
415 methods::MODEL_REROUTED => {
416 serde_json::from_value(params_value).map(Self::ModelRerouted)
417 }
418 methods::MODEL_VERIFICATION => {
419 serde_json::from_value(params_value).map(Self::ModelVerification)
420 }
421 methods::PROCESS_EXITED => {
422 serde_json::from_value(params_value).map(Self::ProcessExited)
423 }
424 methods::PROCESS_OUTPUT_DELTA => {
425 serde_json::from_value(params_value).map(Self::ProcessOutputDelta)
426 }
427 methods::SERVER_REQUEST_RESOLVED => {
428 serde_json::from_value(params_value).map(Self::ServerRequestResolved)
429 }
430 methods::THREAD_COMPACTED => {
431 serde_json::from_value(params_value).map(Self::ContextCompacted)
432 }
433 methods::THREAD_GOAL_UPDATED => {
434 serde_json::from_value(params_value).map(Self::ThreadGoalUpdated)
435 }
436 methods::THREAD_REALTIME_CLOSED => {
437 serde_json::from_value(params_value).map(Self::ThreadRealtimeClosed)
438 }
439 methods::THREAD_REALTIME_ERROR => {
440 serde_json::from_value(params_value).map(Self::ThreadRealtimeError)
441 }
442 methods::THREAD_REALTIME_ITEM_ADDED => {
443 serde_json::from_value(params_value).map(Self::ThreadRealtimeItemAdded)
444 }
445 methods::THREAD_REALTIME_OUTPUT_AUDIO_DELTA => {
446 serde_json::from_value(params_value).map(Self::ThreadRealtimeOutputAudioDelta)
447 }
448 methods::THREAD_REALTIME_SDP => {
449 serde_json::from_value(params_value).map(Self::ThreadRealtimeSdp)
450 }
451 methods::THREAD_REALTIME_STARTED => {
452 serde_json::from_value(params_value).map(Self::ThreadRealtimeStarted)
453 }
454 methods::THREAD_REALTIME_TRANSCRIPT_DELTA => {
455 serde_json::from_value(params_value).map(Self::ThreadRealtimeTranscriptDelta)
456 }
457 methods::THREAD_REALTIME_TRANSCRIPT_DONE => {
458 serde_json::from_value(params_value).map(Self::ThreadRealtimeTranscriptDone)
459 }
460 methods::WINDOWS_WORLD_WRITABLE_WARNING => {
461 serde_json::from_value(params_value).map(Self::WindowsWorldWritableWarning)
462 }
463 methods::WINDOWS_SANDBOX_SETUP_COMPLETED => {
464 serde_json::from_value(params_value).map(Self::WindowsSandboxSetupCompleted)
465 }
466 _ => Ok(Self::Unknown {
467 method: method.to_string(),
468 params,
469 }),
470 }
471 }
472
473 pub fn into_envelope(self) -> Result<(String, Option<Value>), serde_json::Error> {
475 fn pack<T: Serialize>(
476 method: &str,
477 v: &T,
478 ) -> Result<(String, Option<Value>), serde_json::Error> {
479 Ok((method.to_string(), Some(serde_json::to_value(v)?)))
480 }
481 match &self {
482 Self::ThreadStarted(v) => pack(methods::THREAD_STARTED, v),
483 Self::ThreadStatusChanged(v) => pack(methods::THREAD_STATUS_CHANGED, v),
484 Self::ThreadTokenUsageUpdated(v) => pack(methods::THREAD_TOKEN_USAGE_UPDATED, v),
485 Self::TurnStarted(v) => pack(methods::TURN_STARTED, v),
486 Self::TurnCompleted(v) => pack(methods::TURN_COMPLETED, v),
487 Self::ItemStarted(v) => pack(methods::ITEM_STARTED, v),
488 Self::ItemCompleted(v) => pack(methods::ITEM_COMPLETED, v),
489 Self::AgentMessageDelta(v) => pack(methods::AGENT_MESSAGE_DELTA, v),
490 Self::CmdOutputDelta(v) => pack(methods::CMD_OUTPUT_DELTA, v),
491 Self::FileChangeOutputDelta(v) => pack(methods::FILE_CHANGE_OUTPUT_DELTA, v),
492 Self::ReasoningDelta(v) => pack(methods::REASONING_DELTA, v),
493 Self::Error(v) => pack(methods::ERROR, v),
494 Self::AccountRateLimitsUpdated(v) => pack(methods::ACCOUNT_RATE_LIMITS_UPDATED, v),
495 Self::McpServerStartupStatusUpdated(v) => {
496 pack(methods::MCP_SERVER_STARTUP_STATUS_UPDATED, v)
497 }
498 Self::RemoteControlStatusChanged(v) => pack(methods::REMOTE_CONTROL_STATUS_CHANGED, v),
499 Self::McpServerOauthLoginCompleted(v) => {
500 pack(methods::MCP_SERVER_OAUTH_LOGIN_COMPLETED, v)
501 }
502 Self::FileChangePatchUpdated(v) => pack(methods::FILE_CHANGE_PATCH_UPDATED, v),
503 Self::PlanDelta(v) => pack(methods::PLAN_DELTA, v),
504 Self::TurnPlanUpdated(v) => pack(methods::TURN_PLAN_UPDATED, v),
505 Self::TurnDiffUpdated(v) => pack(methods::TURN_DIFF_UPDATED, v),
506 Self::ReasoningSummaryPartAdded(v) => pack(methods::REASONING_SUMMARY_PART_ADDED, v),
507 Self::ReasoningTextDelta(v) => pack(methods::REASONING_TEXT_DELTA, v),
508 Self::AccountLoginCompleted(v) => pack(methods::ACCOUNT_LOGIN_COMPLETED, v),
509 Self::DeprecationNotice(v) => pack(methods::DEPRECATION_NOTICE, v),
510 Self::GuardianWarning(v) => pack(methods::GUARDIAN_WARNING, v),
511 Self::Warning(v) => pack(methods::WARNING, v),
512 Self::ThreadArchived(v) => pack(methods::THREAD_ARCHIVED, v),
513 Self::ThreadClosed(v) => pack(methods::THREAD_CLOSED, v),
514 Self::ThreadUnarchived(v) => pack(methods::THREAD_UNARCHIVED, v),
515 Self::ThreadGoalCleared(v) => pack(methods::THREAD_GOAL_CLEARED, v),
516 Self::ThreadNameUpdated(v) => pack(methods::THREAD_NAME_UPDATED, v),
517 Self::SkillsChanged(v) => pack(methods::SKILLS_CHANGED, v),
518 Self::FsChanged(v) => pack(methods::FS_CHANGED, v),
519 Self::ConfigWarning(v) => pack(methods::CONFIG_WARNING, v),
520 Self::AccountUpdated(v) => pack(methods::ACCOUNT_UPDATED, v),
521 Self::AppListUpdated(v) => pack(methods::APP_LIST_UPDATED, v),
522 Self::CommandExecOutputDelta(v) => pack(methods::COMMAND_EXEC_OUTPUT_DELTA, v),
523 Self::ExternalAgentConfigImportCompleted(v) => {
524 pack(methods::EXTERNAL_AGENT_CONFIG_IMPORT_COMPLETED, v)
525 }
526 Self::FuzzyFileSearchSessionCompleted(v) => {
527 pack(methods::FUZZY_FILE_SEARCH_SESSION_COMPLETED, v)
528 }
529 Self::FuzzyFileSearchSessionUpdated(v) => {
530 pack(methods::FUZZY_FILE_SEARCH_SESSION_UPDATED, v)
531 }
532 Self::HookCompleted(v) => pack(methods::HOOK_COMPLETED, v),
533 Self::HookStarted(v) => pack(methods::HOOK_STARTED, v),
534 Self::ItemGuardianApprovalReviewCompleted(v) => {
535 pack(methods::ITEM_AUTO_APPROVAL_REVIEW_COMPLETED, v)
536 }
537 Self::ItemGuardianApprovalReviewStarted(v) => {
538 pack(methods::ITEM_AUTO_APPROVAL_REVIEW_STARTED, v)
539 }
540 Self::TerminalInteraction(v) => {
541 pack(methods::ITEM_COMMAND_EXEC_TERMINAL_INTERACTION, v)
542 }
543 Self::McpToolCallProgress(v) => pack(methods::ITEM_MCP_TOOL_CALL_PROGRESS, v),
544 Self::ModelRerouted(v) => pack(methods::MODEL_REROUTED, v),
545 Self::ModelVerification(v) => pack(methods::MODEL_VERIFICATION, v),
546 Self::ProcessExited(v) => pack(methods::PROCESS_EXITED, v),
547 Self::ProcessOutputDelta(v) => pack(methods::PROCESS_OUTPUT_DELTA, v),
548 Self::ServerRequestResolved(v) => pack(methods::SERVER_REQUEST_RESOLVED, v),
549 Self::ContextCompacted(v) => pack(methods::THREAD_COMPACTED, v),
550 Self::ThreadGoalUpdated(v) => pack(methods::THREAD_GOAL_UPDATED, v),
551 Self::ThreadRealtimeClosed(v) => pack(methods::THREAD_REALTIME_CLOSED, v),
552 Self::ThreadRealtimeError(v) => pack(methods::THREAD_REALTIME_ERROR, v),
553 Self::ThreadRealtimeItemAdded(v) => pack(methods::THREAD_REALTIME_ITEM_ADDED, v),
554 Self::ThreadRealtimeOutputAudioDelta(v) => {
555 pack(methods::THREAD_REALTIME_OUTPUT_AUDIO_DELTA, v)
556 }
557 Self::ThreadRealtimeSdp(v) => pack(methods::THREAD_REALTIME_SDP, v),
558 Self::ThreadRealtimeStarted(v) => pack(methods::THREAD_REALTIME_STARTED, v),
559 Self::ThreadRealtimeTranscriptDelta(v) => {
560 pack(methods::THREAD_REALTIME_TRANSCRIPT_DELTA, v)
561 }
562 Self::ThreadRealtimeTranscriptDone(v) => {
563 pack(methods::THREAD_REALTIME_TRANSCRIPT_DONE, v)
564 }
565 Self::WindowsWorldWritableWarning(v) => {
566 pack(methods::WINDOWS_WORLD_WRITABLE_WARNING, v)
567 }
568 Self::WindowsSandboxSetupCompleted(v) => {
569 pack(methods::WINDOWS_SANDBOX_SETUP_COMPLETED, v)
570 }
571 Self::Unknown { method, params } => Ok((method.clone(), params.clone())),
572 }
573 }
574}
575
576impl Serialize for Notification {
577 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
578 let (method, params) = self
579 .clone()
580 .into_envelope()
581 .map_err(serde::ser::Error::custom)?;
582 let mut env = serde_json::Map::new();
583 env.insert("method".to_string(), Value::String(method));
584 if let Some(p) = params {
585 env.insert("params".to_string(), p);
586 }
587 Value::Object(env).serialize(serializer)
588 }
589}
590
591impl<'de> Deserialize<'de> for Notification {
592 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
593 let value = Value::deserialize(deserializer)?;
594 let method = value
595 .get("method")
596 .and_then(|v| v.as_str())
597 .ok_or_else(|| serde::de::Error::missing_field("method"))?
598 .to_string();
599 let params = value.get("params").cloned();
600 Self::from_envelope(&method, params).map_err(serde::de::Error::custom)
601 }
602}
603
604#[derive(Debug, Clone)]
611pub enum ServerRequest {
612 CmdExecApproval(CommandExecutionRequestApprovalParams),
614 FileChangeApproval(FileChangeRequestApprovalParams),
616 ToolRequestUserInput(crate::protocol::ToolRequestUserInputParams),
618 McpServerElicitationRequest(crate::protocol::McpServerElicitationRequestParams),
620 PermissionsRequestApproval(crate::protocol::PermissionsRequestApprovalParams),
622 ItemToolCall(crate::protocol::DynamicToolCallParams),
624 ChatgptAuthTokensRefresh(crate::protocol::ChatgptAuthTokensRefreshParams),
626 AttestationGenerate(crate::protocol::AttestationGenerateParams),
628 ApplyPatchApproval(crate::protocol::ApplyPatchApprovalParams),
630 ExecCommandApproval(crate::protocol::ExecCommandApprovalParams),
632 Unknown {
634 method: String,
635 params: Option<Value>,
636 },
637}
638
639impl ServerRequest {
640 pub fn method(&self) -> &str {
642 match self {
643 Self::CmdExecApproval(_) => methods::CMD_EXEC_APPROVAL,
644 Self::FileChangeApproval(_) => methods::FILE_CHANGE_APPROVAL,
645 Self::ToolRequestUserInput(_) => methods::TOOL_REQUEST_USER_INPUT,
646 Self::McpServerElicitationRequest(_) => methods::MCP_SERVER_ELICITATION_REQUEST,
647 Self::PermissionsRequestApproval(_) => methods::PERMISSIONS_REQUEST_APPROVAL,
648 Self::ItemToolCall(_) => methods::ITEM_TOOL_CALL,
649 Self::ChatgptAuthTokensRefresh(_) => methods::CHATGPT_AUTH_TOKENS_REFRESH,
650 Self::AttestationGenerate(_) => methods::ATTESTATION_GENERATE,
651 Self::ApplyPatchApproval(_) => methods::APPLY_PATCH_APPROVAL,
652 Self::ExecCommandApproval(_) => methods::EXEC_COMMAND_APPROVAL,
653 Self::Unknown { method, .. } => method,
654 }
655 }
656
657 pub fn is_unknown(&self) -> bool {
659 matches!(self, Self::Unknown { .. })
660 }
661
662 pub fn from_envelope(method: &str, params: Option<Value>) -> Result<Self, serde_json::Error> {
664 let params_value = params.clone().unwrap_or(Value::Null);
665 match method {
666 methods::CMD_EXEC_APPROVAL => {
667 serde_json::from_value(params_value).map(Self::CmdExecApproval)
668 }
669 methods::FILE_CHANGE_APPROVAL => {
670 serde_json::from_value(params_value).map(Self::FileChangeApproval)
671 }
672 methods::TOOL_REQUEST_USER_INPUT => {
673 serde_json::from_value(params_value).map(Self::ToolRequestUserInput)
674 }
675 methods::MCP_SERVER_ELICITATION_REQUEST => {
676 serde_json::from_value(params_value).map(Self::McpServerElicitationRequest)
677 }
678 methods::PERMISSIONS_REQUEST_APPROVAL => {
679 serde_json::from_value(params_value).map(Self::PermissionsRequestApproval)
680 }
681 methods::ITEM_TOOL_CALL => serde_json::from_value(params_value).map(Self::ItemToolCall),
682 methods::CHATGPT_AUTH_TOKENS_REFRESH => {
683 serde_json::from_value(params_value).map(Self::ChatgptAuthTokensRefresh)
684 }
685 methods::ATTESTATION_GENERATE => {
686 serde_json::from_value(params_value).map(Self::AttestationGenerate)
687 }
688 methods::APPLY_PATCH_APPROVAL => {
689 serde_json::from_value(params_value).map(Self::ApplyPatchApproval)
690 }
691 methods::EXEC_COMMAND_APPROVAL => {
692 serde_json::from_value(params_value).map(Self::ExecCommandApproval)
693 }
694 _ => Ok(Self::Unknown {
695 method: method.to_string(),
696 params,
697 }),
698 }
699 }
700}
701
702#[derive(Debug, Clone)]
709pub enum ServerMessage {
710 Notification(Notification),
712 Request {
714 id: RequestId,
715 request: ServerRequest,
716 },
717}
718
719impl ServerMessage {
720 pub fn is_unknown(&self) -> bool {
722 match self {
723 Self::Notification(n) => n.is_unknown(),
724 Self::Request { request, .. } => request.is_unknown(),
725 }
726 }
727}
728
729#[cfg(test)]
730mod tests {
731 use super::*;
732
733 #[test]
734 fn test_notification_unknown_method_routes_to_unknown_variant() {
735 let n = Notification::from_envelope("foo/bar", Some(serde_json::json!({"x": 1})))
736 .expect("unknown methods do not error");
737 match n {
738 Notification::Unknown { method, params } => {
739 assert_eq!(method, "foo/bar");
740 assert_eq!(params, Some(serde_json::json!({"x": 1})));
741 }
742 other => panic!("expected Unknown, got {:?}", other),
743 }
744 }
745
746 #[test]
747 fn test_notification_known_method_with_bad_params_errors() {
748 let err = Notification::from_envelope("thread/started", Some(serde_json::json!({})));
750 assert!(err.is_err());
751 }
752
753 #[test]
754 fn test_notification_round_trip_envelope() {
755 let wire = serde_json::json!({
756 "method": "item/agentMessage/delta",
757 "params": {"threadId": "t1", "turnId": "u1", "itemId": "i1", "delta": "hi"},
758 });
759 let n: Notification = serde_json::from_value(wire.clone()).unwrap();
760 assert!(matches!(n, Notification::AgentMessageDelta(_)));
761 let back = serde_json::to_value(&n).unwrap();
762 assert_eq!(back, wire);
763 }
764}