1use crate::error::{Error, ParseError};
27use crate::jsonrpc::{JsonRpcMessage, JsonRpcNotification, JsonRpcRequest, RequestId};
28use crate::protocol::{
29 methods, AccountLoginCompletedNotification, AccountRateLimitsUpdatedNotification,
30 AccountUpdatedNotification, AgentMessageDeltaNotification, AppListUpdatedNotification,
31 AuthRecoveryNotification, CommandExecOutputDeltaNotification,
32 CommandExecutionOutputDeltaNotification, CommandExecutionRequestApprovalParams,
33 ConfigWarningNotification, ContextCompactedNotification, DeprecationNoticeNotification,
34 EnvironmentConnectionNotification, ErrorNotification,
35 ExternalAgentConfigImportCompletedNotification, ExternalAgentConfigImportProgressNotification,
36 FileChangeOutputDeltaNotification, FileChangePatchUpdatedNotification,
37 FileChangeRequestApprovalParams, FsChangedNotification,
38 FuzzyFileSearchSessionCompletedNotification, FuzzyFileSearchSessionUpdatedNotification,
39 GuardianWarningNotification, HookCompletedNotification, HookStartedNotification,
40 ItemCompletedNotification, ItemGuardianApprovalReviewCompletedNotification,
41 ItemGuardianApprovalReviewStartedNotification, ItemStartedNotification,
42 McpServerEventStreamNotification, McpServerOauthLoginCompletedNotification,
43 McpServerStatusUpdatedNotification, McpToolCallProgressNotification, ModelReroutedNotification,
44 ModelSafetyBufferingUpdatedNotification, ModelVerificationNotification, PlanDeltaNotification,
45 ProcessExitedNotification, ProcessOutputDeltaNotification, ProjectChangedNotification,
46 ReasoningSummaryPartAddedNotification, ReasoningSummaryTextDeltaNotification,
47 ReasoningTextDeltaNotification, RemoteControlStatusChangedNotification,
48 ServerRequestResolvedNotification, SkillsChangedNotification, StrictReviewRequiredNotification,
49 TerminalInteractionNotification, ThreadArchivedNotification,
50 ThreadAttachmentUpdatedNotification, ThreadClosedNotification, ThreadDeletedNotification,
51 ThreadGoalClearedNotification, ThreadGoalUpdatedNotification, ThreadNameUpdatedNotification,
52 ThreadProjectUpdatedNotification, ThreadQueueChangedNotification,
53 ThreadRealtimeClosedNotification, ThreadRealtimeErrorNotification,
54 ThreadRealtimeItemAddedNotification, ThreadRealtimeItemCompletedNotification,
55 ThreadRealtimeItemStartedNotification, ThreadRealtimeItemTranscriptDeltaNotification,
56 ThreadRealtimeOutputAudioDeltaNotification, ThreadRealtimeSdpNotification,
57 ThreadRealtimeStartedNotification, ThreadRealtimeTranscriptDeltaNotification,
58 ThreadRealtimeTranscriptDoneNotification, ThreadRevertedNotification,
59 ThreadSettingsUpdatedNotification, ThreadStartedNotification, ThreadStatusChangedNotification,
60 ThreadTokenUsageUpdatedNotification, ThreadUnarchivedNotification, TurnCompletedNotification,
61 TurnDiffUpdatedNotification, TurnModerationMetadataNotification, TurnPlanUpdatedNotification,
62 TurnStartedNotification, WarningNotification, WindowsSandboxSetupCompletedNotification,
63 WindowsWorldWritableWarningNotification,
64};
65use serde::{Deserialize, Deserializer, Serialize, Serializer};
66use serde_json::Value;
67
68#[derive(Debug, Clone)]
74#[allow(clippy::large_enum_variant)] pub enum Notification {
76 ThreadStarted(ThreadStartedNotification),
78 ThreadStatusChanged(ThreadStatusChangedNotification),
80 ThreadTokenUsageUpdated(ThreadTokenUsageUpdatedNotification),
82 TurnStarted(TurnStartedNotification),
84 TurnCompleted(TurnCompletedNotification),
86 ItemStarted(ItemStartedNotification),
88 ItemCompleted(ItemCompletedNotification),
90 AgentMessageDelta(AgentMessageDeltaNotification),
92 CmdOutputDelta(CommandExecutionOutputDeltaNotification),
94 FileChangeOutputDelta(FileChangeOutputDeltaNotification),
96 ReasoningDelta(ReasoningSummaryTextDeltaNotification),
98 Error(ErrorNotification),
100 AccountRateLimitsUpdated(AccountRateLimitsUpdatedNotification),
102 McpServerStartupStatusUpdated(McpServerStatusUpdatedNotification),
104 RemoteControlStatusChanged(RemoteControlStatusChangedNotification),
106 McpServerOauthLoginCompleted(McpServerOauthLoginCompletedNotification),
108 FileChangePatchUpdated(FileChangePatchUpdatedNotification),
110 PlanDelta(PlanDeltaNotification),
112 TurnPlanUpdated(TurnPlanUpdatedNotification),
114 TurnDiffUpdated(TurnDiffUpdatedNotification),
116 ReasoningSummaryPartAdded(ReasoningSummaryPartAddedNotification),
118 ReasoningTextDelta(ReasoningTextDeltaNotification),
120 AccountLoginCompleted(AccountLoginCompletedNotification),
122 DeprecationNotice(DeprecationNoticeNotification),
124 GuardianWarning(GuardianWarningNotification),
126 Warning(WarningNotification),
128 ThreadArchived(ThreadArchivedNotification),
130 ThreadClosed(ThreadClosedNotification),
132 ThreadDeleted(ThreadDeletedNotification),
134 ThreadUnarchived(ThreadUnarchivedNotification),
136 ThreadGoalCleared(ThreadGoalClearedNotification),
138 ThreadNameUpdated(ThreadNameUpdatedNotification),
140 ThreadAttachmentUpdated(ThreadAttachmentUpdatedNotification),
142 SkillsChanged(SkillsChangedNotification),
144 FsChanged(FsChangedNotification),
146 ConfigWarning(ConfigWarningNotification),
148 AccountUpdated(AccountUpdatedNotification),
150 AppListUpdated(AppListUpdatedNotification),
152 CommandExecOutputDelta(CommandExecOutputDeltaNotification),
154 ExternalAgentConfigImportCompleted(ExternalAgentConfigImportCompletedNotification),
156 FuzzyFileSearchSessionCompleted(FuzzyFileSearchSessionCompletedNotification),
158 FuzzyFileSearchSessionUpdated(FuzzyFileSearchSessionUpdatedNotification),
160 HookCompleted(HookCompletedNotification),
162 HookStarted(HookStartedNotification),
164 ItemGuardianApprovalReviewCompleted(ItemGuardianApprovalReviewCompletedNotification),
166 ItemGuardianApprovalReviewStarted(ItemGuardianApprovalReviewStartedNotification),
168 TerminalInteraction(TerminalInteractionNotification),
170 McpToolCallProgress(McpToolCallProgressNotification),
172 ModelRerouted(ModelReroutedNotification),
174 ModelVerification(ModelVerificationNotification),
176 ProcessExited(ProcessExitedNotification),
178 ProcessOutputDelta(ProcessOutputDeltaNotification),
180 ServerRequestResolved(ServerRequestResolvedNotification),
182 ContextCompacted(ContextCompactedNotification),
184 ThreadGoalUpdated(ThreadGoalUpdatedNotification),
186 ThreadRealtimeClosed(ThreadRealtimeClosedNotification),
188 ThreadRealtimeError(ThreadRealtimeErrorNotification),
190 ThreadRealtimeItemAdded(ThreadRealtimeItemAddedNotification),
192 ThreadRealtimeOutputAudioDelta(ThreadRealtimeOutputAudioDeltaNotification),
194 ThreadRealtimeSdp(ThreadRealtimeSdpNotification),
196 ThreadRealtimeStarted(ThreadRealtimeStartedNotification),
198 ThreadRealtimeTranscriptDelta(ThreadRealtimeTranscriptDeltaNotification),
200 ThreadRealtimeTranscriptDone(ThreadRealtimeTranscriptDoneNotification),
202 WindowsWorldWritableWarning(WindowsWorldWritableWarningNotification),
204 WindowsSandboxSetupCompleted(WindowsSandboxSetupCompletedNotification),
206 ThreadSettingsUpdated(ThreadSettingsUpdatedNotification),
208 TurnModerationMetadata(TurnModerationMetadataNotification),
210 ExternalAgentConfigImportProgress(ExternalAgentConfigImportProgressNotification),
212 ModelSafetyBufferingUpdated(ModelSafetyBufferingUpdatedNotification),
214 ThreadEnvironmentConnected(EnvironmentConnectionNotification),
216 ThreadEnvironmentDisconnected(EnvironmentConnectionNotification),
218 StrictReviewRequired(StrictReviewRequiredNotification),
220 ProjectChanged(ProjectChangedNotification),
222 ThreadProjectUpdated(ThreadProjectUpdatedNotification),
224 ThreadQueueChanged(ThreadQueueChangedNotification),
226 ThreadReverted(ThreadRevertedNotification),
228 McpServerEventStream(McpServerEventStreamNotification),
230 ThreadRealtimeItemStarted(ThreadRealtimeItemStartedNotification),
232 ThreadRealtimeItemCompleted(ThreadRealtimeItemCompletedNotification),
234 ThreadRealtimeItemTranscriptDelta(ThreadRealtimeItemTranscriptDeltaNotification),
236 ModelProviderAuthRecoveryStarted(AuthRecoveryNotification),
238 ModelProviderAuthRecoveryCompleted(AuthRecoveryNotification),
240 Unknown {
244 method: String,
245 params: Option<Value>,
246 },
247}
248
249impl Notification {
250 pub fn method(&self) -> &str {
252 match self {
253 Self::ThreadStarted(_) => methods::THREAD_STARTED,
254 Self::StrictReviewRequired(_) => methods::STRICT_REVIEW_REQUIRED,
255 Self::ProjectChanged(_) => methods::PROJECT_CHANGED,
256 Self::ThreadProjectUpdated(_) => methods::THREAD_PROJECT_UPDATED,
257 Self::ThreadQueueChanged(_) => methods::THREAD_QUEUE_CHANGED,
258 Self::ThreadReverted(_) => methods::THREAD_REVERTED,
259 Self::McpServerEventStream(_) => methods::MCP_SERVER_EVENT_STREAM,
260 Self::ThreadRealtimeItemStarted(_) => methods::THREAD_REALTIME_ITEM_STARTED,
261 Self::ThreadRealtimeItemCompleted(_) => methods::THREAD_REALTIME_ITEM_COMPLETED,
262 Self::ThreadRealtimeItemTranscriptDelta(_) => {
263 methods::THREAD_REALTIME_ITEM_TRANSCRIPT_DELTA
264 }
265 Self::ModelProviderAuthRecoveryStarted(_) => {
266 methods::MODEL_PROVIDER_AUTH_RECOVERY_STARTED
267 }
268 Self::ModelProviderAuthRecoveryCompleted(_) => {
269 methods::MODEL_PROVIDER_AUTH_RECOVERY_COMPLETED
270 }
271 Self::ThreadStatusChanged(_) => methods::THREAD_STATUS_CHANGED,
272 Self::ThreadTokenUsageUpdated(_) => methods::THREAD_TOKEN_USAGE_UPDATED,
273 Self::TurnStarted(_) => methods::TURN_STARTED,
274 Self::TurnCompleted(_) => methods::TURN_COMPLETED,
275 Self::ItemStarted(_) => methods::ITEM_STARTED,
276 Self::ItemCompleted(_) => methods::ITEM_COMPLETED,
277 Self::AgentMessageDelta(_) => methods::AGENT_MESSAGE_DELTA,
278 Self::CmdOutputDelta(_) => methods::CMD_OUTPUT_DELTA,
279 Self::FileChangeOutputDelta(_) => methods::FILE_CHANGE_OUTPUT_DELTA,
280 Self::ReasoningDelta(_) => methods::REASONING_DELTA,
281 Self::Error(_) => methods::ERROR,
282 Self::AccountRateLimitsUpdated(_) => methods::ACCOUNT_RATE_LIMITS_UPDATED,
283 Self::McpServerStartupStatusUpdated(_) => methods::MCP_SERVER_STARTUP_STATUS_UPDATED,
284 Self::RemoteControlStatusChanged(_) => methods::REMOTE_CONTROL_STATUS_CHANGED,
285 Self::McpServerOauthLoginCompleted(_) => methods::MCP_SERVER_OAUTH_LOGIN_COMPLETED,
286 Self::FileChangePatchUpdated(_) => methods::FILE_CHANGE_PATCH_UPDATED,
287 Self::PlanDelta(_) => methods::PLAN_DELTA,
288 Self::TurnPlanUpdated(_) => methods::TURN_PLAN_UPDATED,
289 Self::TurnDiffUpdated(_) => methods::TURN_DIFF_UPDATED,
290 Self::ReasoningSummaryPartAdded(_) => methods::REASONING_SUMMARY_PART_ADDED,
291 Self::ReasoningTextDelta(_) => methods::REASONING_TEXT_DELTA,
292 Self::AccountLoginCompleted(_) => methods::ACCOUNT_LOGIN_COMPLETED,
293 Self::DeprecationNotice(_) => methods::DEPRECATION_NOTICE,
294 Self::GuardianWarning(_) => methods::GUARDIAN_WARNING,
295 Self::Warning(_) => methods::WARNING,
296 Self::ThreadArchived(_) => methods::THREAD_ARCHIVED,
297 Self::ThreadClosed(_) => methods::THREAD_CLOSED,
298 Self::ThreadDeleted(_) => methods::THREAD_DELETED,
299 Self::ThreadUnarchived(_) => methods::THREAD_UNARCHIVED,
300 Self::ThreadGoalCleared(_) => methods::THREAD_GOAL_CLEARED,
301 Self::ThreadNameUpdated(_) => methods::THREAD_NAME_UPDATED,
302 Self::ThreadAttachmentUpdated(_) => methods::THREAD_ATTACHMENT_UPDATED,
303 Self::SkillsChanged(_) => methods::SKILLS_CHANGED,
304 Self::FsChanged(_) => methods::FS_CHANGED,
305 Self::ConfigWarning(_) => methods::CONFIG_WARNING,
306 Self::AccountUpdated(_) => methods::ACCOUNT_UPDATED,
307 Self::AppListUpdated(_) => methods::APP_LIST_UPDATED,
308 Self::CommandExecOutputDelta(_) => methods::COMMAND_EXEC_OUTPUT_DELTA,
309 Self::ExternalAgentConfigImportCompleted(_) => {
310 methods::EXTERNAL_AGENT_CONFIG_IMPORT_COMPLETED
311 }
312 Self::FuzzyFileSearchSessionCompleted(_) => {
313 methods::FUZZY_FILE_SEARCH_SESSION_COMPLETED
314 }
315 Self::FuzzyFileSearchSessionUpdated(_) => methods::FUZZY_FILE_SEARCH_SESSION_UPDATED,
316 Self::HookCompleted(_) => methods::HOOK_COMPLETED,
317 Self::HookStarted(_) => methods::HOOK_STARTED,
318 Self::ItemGuardianApprovalReviewCompleted(_) => {
319 methods::ITEM_AUTO_APPROVAL_REVIEW_COMPLETED
320 }
321 Self::ItemGuardianApprovalReviewStarted(_) => {
322 methods::ITEM_AUTO_APPROVAL_REVIEW_STARTED
323 }
324 Self::TerminalInteraction(_) => methods::ITEM_COMMAND_EXEC_TERMINAL_INTERACTION,
325 Self::McpToolCallProgress(_) => methods::ITEM_MCP_TOOL_CALL_PROGRESS,
326 Self::ModelRerouted(_) => methods::MODEL_REROUTED,
327 Self::ModelVerification(_) => methods::MODEL_VERIFICATION,
328 Self::ProcessExited(_) => methods::PROCESS_EXITED,
329 Self::ProcessOutputDelta(_) => methods::PROCESS_OUTPUT_DELTA,
330 Self::ServerRequestResolved(_) => methods::SERVER_REQUEST_RESOLVED,
331 Self::ContextCompacted(_) => methods::THREAD_COMPACTED,
332 Self::ThreadGoalUpdated(_) => methods::THREAD_GOAL_UPDATED,
333 Self::ThreadRealtimeClosed(_) => methods::THREAD_REALTIME_CLOSED,
334 Self::ThreadRealtimeError(_) => methods::THREAD_REALTIME_ERROR,
335 Self::ThreadRealtimeItemAdded(_) => methods::THREAD_REALTIME_ITEM_ADDED,
336 Self::ThreadRealtimeOutputAudioDelta(_) => methods::THREAD_REALTIME_OUTPUT_AUDIO_DELTA,
337 Self::ThreadRealtimeSdp(_) => methods::THREAD_REALTIME_SDP,
338 Self::ThreadRealtimeStarted(_) => methods::THREAD_REALTIME_STARTED,
339 Self::ThreadRealtimeTranscriptDelta(_) => methods::THREAD_REALTIME_TRANSCRIPT_DELTA,
340 Self::ThreadRealtimeTranscriptDone(_) => methods::THREAD_REALTIME_TRANSCRIPT_DONE,
341 Self::WindowsWorldWritableWarning(_) => methods::WINDOWS_WORLD_WRITABLE_WARNING,
342 Self::WindowsSandboxSetupCompleted(_) => methods::WINDOWS_SANDBOX_SETUP_COMPLETED,
343 Self::ThreadSettingsUpdated(_) => methods::THREAD_SETTINGS_UPDATED,
344 Self::TurnModerationMetadata(_) => methods::TURN_MODERATION_METADATA,
345 Self::ExternalAgentConfigImportProgress(_) => {
346 methods::EXTERNAL_AGENT_CONFIG_IMPORT_PROGRESS
347 }
348 Self::ModelSafetyBufferingUpdated(_) => methods::MODEL_SAFETY_BUFFERING_UPDATED,
349 Self::ThreadEnvironmentConnected(_) => methods::THREAD_ENVIRONMENT_CONNECTED,
350 Self::ThreadEnvironmentDisconnected(_) => methods::THREAD_ENVIRONMENT_DISCONNECTED,
351 Self::Unknown { method, .. } => method,
352 }
353 }
354
355 pub fn is_unknown(&self) -> bool {
357 matches!(self, Self::Unknown { .. })
358 }
359
360 pub fn turn_id(&self) -> Option<&str> {
372 let id = match self {
373 Self::TurnStarted(n) => n.turn.id.as_str(),
374 Self::TurnCompleted(n) => n.turn.id.as_str(),
375 Self::AgentMessageDelta(n) => n.turn_id.as_str(),
376 Self::CmdOutputDelta(n) => n.turn_id.as_str(),
377 Self::FileChangeOutputDelta(n) => n.turn_id.as_str(),
378 Self::ReasoningDelta(n) => n.turn_id.as_str(),
379 Self::Error(n) => n.turn_id.as_str(),
380 Self::FileChangePatchUpdated(n) => n.turn_id.as_str(),
381 Self::PlanDelta(n) => n.turn_id.as_str(),
382 Self::TurnPlanUpdated(n) => n.turn_id.as_str(),
383 Self::TurnDiffUpdated(n) => n.turn_id.as_str(),
384 Self::TurnModerationMetadata(n) => n.turn_id.as_str(),
385 Self::ReasoningSummaryPartAdded(n) => n.turn_id.as_str(),
386 Self::ReasoningTextDelta(n) => n.turn_id.as_str(),
387 Self::ItemStarted(n) => n.turn_id.as_str(),
388 Self::ItemCompleted(n) => n.turn_id.as_str(),
389 Self::ContextCompacted(n) => n.turn_id.as_str(),
390 Self::McpToolCallProgress(n) => n.turn_id.as_str(),
391 Self::ModelRerouted(n) => n.turn_id.as_str(),
392 Self::ModelVerification(n) => n.turn_id.as_str(),
393 Self::ModelSafetyBufferingUpdated(n) => n.turn_id.as_str(),
394 Self::TerminalInteraction(n) => n.turn_id.as_str(),
395 Self::ThreadTokenUsageUpdated(n) => n.turn_id.as_str(),
396 Self::ItemGuardianApprovalReviewStarted(n) => n.turn_id.as_str(),
397 Self::ItemGuardianApprovalReviewCompleted(n) => n.turn_id.as_str(),
398 _ => return None,
399 };
400 (!id.is_empty()).then_some(id)
401 }
402
403 pub fn thread_item(&self) -> Option<&crate::protocol::ThreadItem> {
409 match self {
410 Self::ItemStarted(n) => Some(&n.item),
411 Self::ItemCompleted(n) => Some(&n.item),
412 _ => None,
413 }
414 }
415
416 pub fn from_envelope(method: &str, params: Option<Value>) -> Result<Self, serde_json::Error> {
422 let params_value = params.clone().unwrap_or(Value::Null);
423 match method {
424 methods::THREAD_STARTED => {
425 serde_json::from_value(params_value).map(Self::ThreadStarted)
426 }
427 methods::STRICT_REVIEW_REQUIRED => {
428 serde_json::from_value(params_value).map(Self::StrictReviewRequired)
429 }
430 methods::PROJECT_CHANGED => {
431 serde_json::from_value(params_value).map(Self::ProjectChanged)
432 }
433 methods::THREAD_PROJECT_UPDATED => {
434 serde_json::from_value(params_value).map(Self::ThreadProjectUpdated)
435 }
436 methods::THREAD_QUEUE_CHANGED => {
437 serde_json::from_value(params_value).map(Self::ThreadQueueChanged)
438 }
439 methods::THREAD_REVERTED => {
440 serde_json::from_value(params_value).map(Self::ThreadReverted)
441 }
442 methods::MCP_SERVER_EVENT_STREAM => {
443 serde_json::from_value(params_value).map(Self::McpServerEventStream)
444 }
445 methods::THREAD_REALTIME_ITEM_STARTED => {
446 serde_json::from_value(params_value).map(Self::ThreadRealtimeItemStarted)
447 }
448 methods::THREAD_REALTIME_ITEM_COMPLETED => {
449 serde_json::from_value(params_value).map(Self::ThreadRealtimeItemCompleted)
450 }
451 methods::THREAD_REALTIME_ITEM_TRANSCRIPT_DELTA => {
452 serde_json::from_value(params_value).map(Self::ThreadRealtimeItemTranscriptDelta)
453 }
454 methods::MODEL_PROVIDER_AUTH_RECOVERY_STARTED => {
455 serde_json::from_value(params_value).map(Self::ModelProviderAuthRecoveryStarted)
456 }
457 methods::MODEL_PROVIDER_AUTH_RECOVERY_COMPLETED => {
458 serde_json::from_value(params_value).map(Self::ModelProviderAuthRecoveryCompleted)
459 }
460 methods::THREAD_STATUS_CHANGED => {
461 serde_json::from_value(params_value).map(Self::ThreadStatusChanged)
462 }
463 methods::THREAD_TOKEN_USAGE_UPDATED => {
464 serde_json::from_value(params_value).map(Self::ThreadTokenUsageUpdated)
465 }
466 methods::TURN_STARTED => serde_json::from_value(params_value).map(Self::TurnStarted),
467 methods::TURN_COMPLETED => {
468 serde_json::from_value(params_value).map(Self::TurnCompleted)
469 }
470 methods::ITEM_STARTED => serde_json::from_value(params_value).map(Self::ItemStarted),
471 methods::ITEM_COMPLETED => {
472 serde_json::from_value(params_value).map(Self::ItemCompleted)
473 }
474 methods::AGENT_MESSAGE_DELTA => {
475 serde_json::from_value(params_value).map(Self::AgentMessageDelta)
476 }
477 methods::CMD_OUTPUT_DELTA => {
478 serde_json::from_value(params_value).map(Self::CmdOutputDelta)
479 }
480 methods::FILE_CHANGE_OUTPUT_DELTA => {
481 serde_json::from_value(params_value).map(Self::FileChangeOutputDelta)
482 }
483 methods::REASONING_DELTA => {
484 serde_json::from_value(params_value).map(Self::ReasoningDelta)
485 }
486 methods::ERROR => serde_json::from_value(params_value).map(Self::Error),
487 methods::ACCOUNT_RATE_LIMITS_UPDATED => {
488 serde_json::from_value(params_value).map(Self::AccountRateLimitsUpdated)
489 }
490 methods::MCP_SERVER_STARTUP_STATUS_UPDATED => {
491 serde_json::from_value(params_value).map(Self::McpServerStartupStatusUpdated)
492 }
493 methods::REMOTE_CONTROL_STATUS_CHANGED => {
494 serde_json::from_value(params_value).map(Self::RemoteControlStatusChanged)
495 }
496 methods::MCP_SERVER_OAUTH_LOGIN_COMPLETED => {
497 serde_json::from_value(params_value).map(Self::McpServerOauthLoginCompleted)
498 }
499 methods::FILE_CHANGE_PATCH_UPDATED => {
500 serde_json::from_value(params_value).map(Self::FileChangePatchUpdated)
501 }
502 methods::PLAN_DELTA => serde_json::from_value(params_value).map(Self::PlanDelta),
503 methods::TURN_PLAN_UPDATED => {
504 serde_json::from_value(params_value).map(Self::TurnPlanUpdated)
505 }
506 methods::TURN_DIFF_UPDATED => {
507 serde_json::from_value(params_value).map(Self::TurnDiffUpdated)
508 }
509 methods::REASONING_SUMMARY_PART_ADDED => {
510 serde_json::from_value(params_value).map(Self::ReasoningSummaryPartAdded)
511 }
512 methods::REASONING_TEXT_DELTA => {
513 serde_json::from_value(params_value).map(Self::ReasoningTextDelta)
514 }
515 methods::ACCOUNT_LOGIN_COMPLETED => {
516 serde_json::from_value(params_value).map(Self::AccountLoginCompleted)
517 }
518 methods::DEPRECATION_NOTICE => {
519 serde_json::from_value(params_value).map(Self::DeprecationNotice)
520 }
521 methods::GUARDIAN_WARNING => {
522 serde_json::from_value(params_value).map(Self::GuardianWarning)
523 }
524 methods::WARNING => serde_json::from_value(params_value).map(Self::Warning),
525 methods::THREAD_ARCHIVED => {
526 serde_json::from_value(params_value).map(Self::ThreadArchived)
527 }
528 methods::THREAD_CLOSED => serde_json::from_value(params_value).map(Self::ThreadClosed),
529 methods::THREAD_DELETED => {
530 serde_json::from_value(params_value).map(Self::ThreadDeleted)
531 }
532 methods::THREAD_UNARCHIVED => {
533 serde_json::from_value(params_value).map(Self::ThreadUnarchived)
534 }
535 methods::THREAD_GOAL_CLEARED => {
536 serde_json::from_value(params_value).map(Self::ThreadGoalCleared)
537 }
538 methods::THREAD_NAME_UPDATED => {
539 serde_json::from_value(params_value).map(Self::ThreadNameUpdated)
540 }
541 methods::THREAD_ATTACHMENT_UPDATED => {
542 serde_json::from_value(params_value).map(Self::ThreadAttachmentUpdated)
543 }
544 methods::SKILLS_CHANGED => {
545 serde_json::from_value(params_value).map(Self::SkillsChanged)
546 }
547 methods::FS_CHANGED => serde_json::from_value(params_value).map(Self::FsChanged),
548 methods::CONFIG_WARNING => {
549 serde_json::from_value(params_value).map(Self::ConfigWarning)
550 }
551 methods::ACCOUNT_UPDATED => {
552 serde_json::from_value(params_value).map(Self::AccountUpdated)
553 }
554 methods::APP_LIST_UPDATED => {
555 serde_json::from_value(params_value).map(Self::AppListUpdated)
556 }
557 methods::COMMAND_EXEC_OUTPUT_DELTA => {
558 serde_json::from_value(params_value).map(Self::CommandExecOutputDelta)
559 }
560 methods::EXTERNAL_AGENT_CONFIG_IMPORT_COMPLETED => {
561 serde_json::from_value(params_value).map(Self::ExternalAgentConfigImportCompleted)
562 }
563 methods::FUZZY_FILE_SEARCH_SESSION_COMPLETED => {
564 serde_json::from_value(params_value).map(Self::FuzzyFileSearchSessionCompleted)
565 }
566 methods::FUZZY_FILE_SEARCH_SESSION_UPDATED => {
567 serde_json::from_value(params_value).map(Self::FuzzyFileSearchSessionUpdated)
568 }
569 methods::HOOK_COMPLETED => {
570 serde_json::from_value(params_value).map(Self::HookCompleted)
571 }
572 methods::HOOK_STARTED => serde_json::from_value(params_value).map(Self::HookStarted),
573 methods::ITEM_AUTO_APPROVAL_REVIEW_COMPLETED => {
574 serde_json::from_value(params_value).map(Self::ItemGuardianApprovalReviewCompleted)
575 }
576 methods::ITEM_AUTO_APPROVAL_REVIEW_STARTED => {
577 serde_json::from_value(params_value).map(Self::ItemGuardianApprovalReviewStarted)
578 }
579 methods::ITEM_COMMAND_EXEC_TERMINAL_INTERACTION => {
580 serde_json::from_value(params_value).map(Self::TerminalInteraction)
581 }
582 methods::ITEM_MCP_TOOL_CALL_PROGRESS => {
583 serde_json::from_value(params_value).map(Self::McpToolCallProgress)
584 }
585 methods::MODEL_REROUTED => {
586 serde_json::from_value(params_value).map(Self::ModelRerouted)
587 }
588 methods::MODEL_VERIFICATION => {
589 serde_json::from_value(params_value).map(Self::ModelVerification)
590 }
591 methods::PROCESS_EXITED => {
592 serde_json::from_value(params_value).map(Self::ProcessExited)
593 }
594 methods::PROCESS_OUTPUT_DELTA => {
595 serde_json::from_value(params_value).map(Self::ProcessOutputDelta)
596 }
597 methods::SERVER_REQUEST_RESOLVED => {
598 serde_json::from_value(params_value).map(Self::ServerRequestResolved)
599 }
600 methods::THREAD_COMPACTED => {
601 serde_json::from_value(params_value).map(Self::ContextCompacted)
602 }
603 methods::THREAD_GOAL_UPDATED => {
604 serde_json::from_value(params_value).map(Self::ThreadGoalUpdated)
605 }
606 methods::THREAD_REALTIME_CLOSED => {
607 serde_json::from_value(params_value).map(Self::ThreadRealtimeClosed)
608 }
609 methods::THREAD_REALTIME_ERROR => {
610 serde_json::from_value(params_value).map(Self::ThreadRealtimeError)
611 }
612 methods::THREAD_REALTIME_ITEM_ADDED => {
613 serde_json::from_value(params_value).map(Self::ThreadRealtimeItemAdded)
614 }
615 methods::THREAD_REALTIME_OUTPUT_AUDIO_DELTA => {
616 serde_json::from_value(params_value).map(Self::ThreadRealtimeOutputAudioDelta)
617 }
618 methods::THREAD_REALTIME_SDP => {
619 serde_json::from_value(params_value).map(Self::ThreadRealtimeSdp)
620 }
621 methods::THREAD_REALTIME_STARTED => {
622 serde_json::from_value(params_value).map(Self::ThreadRealtimeStarted)
623 }
624 methods::THREAD_REALTIME_TRANSCRIPT_DELTA => {
625 serde_json::from_value(params_value).map(Self::ThreadRealtimeTranscriptDelta)
626 }
627 methods::THREAD_REALTIME_TRANSCRIPT_DONE => {
628 serde_json::from_value(params_value).map(Self::ThreadRealtimeTranscriptDone)
629 }
630 methods::WINDOWS_WORLD_WRITABLE_WARNING => {
631 serde_json::from_value(params_value).map(Self::WindowsWorldWritableWarning)
632 }
633 methods::WINDOWS_SANDBOX_SETUP_COMPLETED => {
634 serde_json::from_value(params_value).map(Self::WindowsSandboxSetupCompleted)
635 }
636 methods::THREAD_SETTINGS_UPDATED => {
637 serde_json::from_value(params_value).map(Self::ThreadSettingsUpdated)
638 }
639 methods::TURN_MODERATION_METADATA => {
640 serde_json::from_value(params_value).map(Self::TurnModerationMetadata)
641 }
642 methods::EXTERNAL_AGENT_CONFIG_IMPORT_PROGRESS => {
643 serde_json::from_value(params_value).map(Self::ExternalAgentConfigImportProgress)
644 }
645 methods::MODEL_SAFETY_BUFFERING_UPDATED => {
646 serde_json::from_value(params_value).map(Self::ModelSafetyBufferingUpdated)
647 }
648 methods::THREAD_ENVIRONMENT_CONNECTED => {
649 serde_json::from_value(params_value).map(Self::ThreadEnvironmentConnected)
650 }
651 methods::THREAD_ENVIRONMENT_DISCONNECTED => {
652 serde_json::from_value(params_value).map(Self::ThreadEnvironmentDisconnected)
653 }
654 _ => Ok(Self::Unknown {
655 method: method.to_string(),
656 params,
657 }),
658 }
659 }
660
661 pub fn into_envelope(self) -> Result<(String, Option<Value>), serde_json::Error> {
663 fn pack<T: Serialize>(
664 method: &str,
665 v: &T,
666 ) -> Result<(String, Option<Value>), serde_json::Error> {
667 Ok((method.to_string(), Some(serde_json::to_value(v)?)))
668 }
669 match &self {
670 Self::ThreadStarted(v) => pack(methods::THREAD_STARTED, v),
671 Self::StrictReviewRequired(v) => pack(methods::STRICT_REVIEW_REQUIRED, v),
672 Self::ProjectChanged(v) => pack(methods::PROJECT_CHANGED, v),
673 Self::ThreadProjectUpdated(v) => pack(methods::THREAD_PROJECT_UPDATED, v),
674 Self::ThreadQueueChanged(v) => pack(methods::THREAD_QUEUE_CHANGED, v),
675 Self::ThreadReverted(v) => pack(methods::THREAD_REVERTED, v),
676 Self::McpServerEventStream(v) => pack(methods::MCP_SERVER_EVENT_STREAM, v),
677 Self::ThreadRealtimeItemStarted(v) => pack(methods::THREAD_REALTIME_ITEM_STARTED, v),
678 Self::ThreadRealtimeItemCompleted(v) => {
679 pack(methods::THREAD_REALTIME_ITEM_COMPLETED, v)
680 }
681 Self::ThreadRealtimeItemTranscriptDelta(v) => {
682 pack(methods::THREAD_REALTIME_ITEM_TRANSCRIPT_DELTA, v)
683 }
684 Self::ModelProviderAuthRecoveryStarted(v) => {
685 pack(methods::MODEL_PROVIDER_AUTH_RECOVERY_STARTED, v)
686 }
687 Self::ModelProviderAuthRecoveryCompleted(v) => {
688 pack(methods::MODEL_PROVIDER_AUTH_RECOVERY_COMPLETED, v)
689 }
690 Self::ThreadStatusChanged(v) => pack(methods::THREAD_STATUS_CHANGED, v),
691 Self::ThreadTokenUsageUpdated(v) => pack(methods::THREAD_TOKEN_USAGE_UPDATED, v),
692 Self::TurnStarted(v) => pack(methods::TURN_STARTED, v),
693 Self::TurnCompleted(v) => pack(methods::TURN_COMPLETED, v),
694 Self::ItemStarted(v) => pack(methods::ITEM_STARTED, v),
695 Self::ItemCompleted(v) => pack(methods::ITEM_COMPLETED, v),
696 Self::AgentMessageDelta(v) => pack(methods::AGENT_MESSAGE_DELTA, v),
697 Self::CmdOutputDelta(v) => pack(methods::CMD_OUTPUT_DELTA, v),
698 Self::FileChangeOutputDelta(v) => pack(methods::FILE_CHANGE_OUTPUT_DELTA, v),
699 Self::ReasoningDelta(v) => pack(methods::REASONING_DELTA, v),
700 Self::Error(v) => pack(methods::ERROR, v),
701 Self::AccountRateLimitsUpdated(v) => pack(methods::ACCOUNT_RATE_LIMITS_UPDATED, v),
702 Self::McpServerStartupStatusUpdated(v) => {
703 pack(methods::MCP_SERVER_STARTUP_STATUS_UPDATED, v)
704 }
705 Self::RemoteControlStatusChanged(v) => pack(methods::REMOTE_CONTROL_STATUS_CHANGED, v),
706 Self::McpServerOauthLoginCompleted(v) => {
707 pack(methods::MCP_SERVER_OAUTH_LOGIN_COMPLETED, v)
708 }
709 Self::FileChangePatchUpdated(v) => pack(methods::FILE_CHANGE_PATCH_UPDATED, v),
710 Self::PlanDelta(v) => pack(methods::PLAN_DELTA, v),
711 Self::TurnPlanUpdated(v) => pack(methods::TURN_PLAN_UPDATED, v),
712 Self::TurnDiffUpdated(v) => pack(methods::TURN_DIFF_UPDATED, v),
713 Self::ReasoningSummaryPartAdded(v) => pack(methods::REASONING_SUMMARY_PART_ADDED, v),
714 Self::ReasoningTextDelta(v) => pack(methods::REASONING_TEXT_DELTA, v),
715 Self::AccountLoginCompleted(v) => pack(methods::ACCOUNT_LOGIN_COMPLETED, v),
716 Self::DeprecationNotice(v) => pack(methods::DEPRECATION_NOTICE, v),
717 Self::GuardianWarning(v) => pack(methods::GUARDIAN_WARNING, v),
718 Self::Warning(v) => pack(methods::WARNING, v),
719 Self::ThreadArchived(v) => pack(methods::THREAD_ARCHIVED, v),
720 Self::ThreadClosed(v) => pack(methods::THREAD_CLOSED, v),
721 Self::ThreadDeleted(v) => pack(methods::THREAD_DELETED, v),
722 Self::ThreadUnarchived(v) => pack(methods::THREAD_UNARCHIVED, v),
723 Self::ThreadGoalCleared(v) => pack(methods::THREAD_GOAL_CLEARED, v),
724 Self::ThreadNameUpdated(v) => pack(methods::THREAD_NAME_UPDATED, v),
725 Self::ThreadAttachmentUpdated(v) => pack(methods::THREAD_ATTACHMENT_UPDATED, v),
726 Self::SkillsChanged(v) => pack(methods::SKILLS_CHANGED, v),
727 Self::FsChanged(v) => pack(methods::FS_CHANGED, v),
728 Self::ConfigWarning(v) => pack(methods::CONFIG_WARNING, v),
729 Self::AccountUpdated(v) => pack(methods::ACCOUNT_UPDATED, v),
730 Self::AppListUpdated(v) => pack(methods::APP_LIST_UPDATED, v),
731 Self::CommandExecOutputDelta(v) => pack(methods::COMMAND_EXEC_OUTPUT_DELTA, v),
732 Self::ExternalAgentConfigImportCompleted(v) => {
733 pack(methods::EXTERNAL_AGENT_CONFIG_IMPORT_COMPLETED, v)
734 }
735 Self::FuzzyFileSearchSessionCompleted(v) => {
736 pack(methods::FUZZY_FILE_SEARCH_SESSION_COMPLETED, v)
737 }
738 Self::FuzzyFileSearchSessionUpdated(v) => {
739 pack(methods::FUZZY_FILE_SEARCH_SESSION_UPDATED, v)
740 }
741 Self::HookCompleted(v) => pack(methods::HOOK_COMPLETED, v),
742 Self::HookStarted(v) => pack(methods::HOOK_STARTED, v),
743 Self::ItemGuardianApprovalReviewCompleted(v) => {
744 pack(methods::ITEM_AUTO_APPROVAL_REVIEW_COMPLETED, v)
745 }
746 Self::ItemGuardianApprovalReviewStarted(v) => {
747 pack(methods::ITEM_AUTO_APPROVAL_REVIEW_STARTED, v)
748 }
749 Self::TerminalInteraction(v) => {
750 pack(methods::ITEM_COMMAND_EXEC_TERMINAL_INTERACTION, v)
751 }
752 Self::McpToolCallProgress(v) => pack(methods::ITEM_MCP_TOOL_CALL_PROGRESS, v),
753 Self::ModelRerouted(v) => pack(methods::MODEL_REROUTED, v),
754 Self::ModelVerification(v) => pack(methods::MODEL_VERIFICATION, v),
755 Self::ProcessExited(v) => pack(methods::PROCESS_EXITED, v),
756 Self::ProcessOutputDelta(v) => pack(methods::PROCESS_OUTPUT_DELTA, v),
757 Self::ServerRequestResolved(v) => pack(methods::SERVER_REQUEST_RESOLVED, v),
758 Self::ContextCompacted(v) => pack(methods::THREAD_COMPACTED, v),
759 Self::ThreadGoalUpdated(v) => pack(methods::THREAD_GOAL_UPDATED, v),
760 Self::ThreadRealtimeClosed(v) => pack(methods::THREAD_REALTIME_CLOSED, v),
761 Self::ThreadRealtimeError(v) => pack(methods::THREAD_REALTIME_ERROR, v),
762 Self::ThreadRealtimeItemAdded(v) => pack(methods::THREAD_REALTIME_ITEM_ADDED, v),
763 Self::ThreadRealtimeOutputAudioDelta(v) => {
764 pack(methods::THREAD_REALTIME_OUTPUT_AUDIO_DELTA, v)
765 }
766 Self::ThreadRealtimeSdp(v) => pack(methods::THREAD_REALTIME_SDP, v),
767 Self::ThreadRealtimeStarted(v) => pack(methods::THREAD_REALTIME_STARTED, v),
768 Self::ThreadRealtimeTranscriptDelta(v) => {
769 pack(methods::THREAD_REALTIME_TRANSCRIPT_DELTA, v)
770 }
771 Self::ThreadRealtimeTranscriptDone(v) => {
772 pack(methods::THREAD_REALTIME_TRANSCRIPT_DONE, v)
773 }
774 Self::WindowsWorldWritableWarning(v) => {
775 pack(methods::WINDOWS_WORLD_WRITABLE_WARNING, v)
776 }
777 Self::WindowsSandboxSetupCompleted(v) => {
778 pack(methods::WINDOWS_SANDBOX_SETUP_COMPLETED, v)
779 }
780 Self::ThreadSettingsUpdated(v) => pack(methods::THREAD_SETTINGS_UPDATED, v),
781 Self::TurnModerationMetadata(v) => pack(methods::TURN_MODERATION_METADATA, v),
782 Self::ExternalAgentConfigImportProgress(v) => {
783 pack(methods::EXTERNAL_AGENT_CONFIG_IMPORT_PROGRESS, v)
784 }
785 Self::ModelSafetyBufferingUpdated(v) => {
786 pack(methods::MODEL_SAFETY_BUFFERING_UPDATED, v)
787 }
788 Self::ThreadEnvironmentConnected(v) => pack(methods::THREAD_ENVIRONMENT_CONNECTED, v),
789 Self::ThreadEnvironmentDisconnected(v) => {
790 pack(methods::THREAD_ENVIRONMENT_DISCONNECTED, v)
791 }
792 Self::Unknown { method, params } => Ok((method.clone(), params.clone())),
793 }
794 }
795}
796
797impl Serialize for Notification {
798 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
799 let (method, params) = self
800 .clone()
801 .into_envelope()
802 .map_err(serde::ser::Error::custom)?;
803 let mut env = serde_json::Map::new();
804 env.insert("method".to_string(), Value::String(method));
805 if let Some(p) = params {
806 env.insert("params".to_string(), p);
807 }
808 Value::Object(env).serialize(serializer)
809 }
810}
811
812impl<'de> Deserialize<'de> for Notification {
813 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
814 let value = Value::deserialize(deserializer)?;
815 let method = value
816 .get("method")
817 .and_then(|v| v.as_str())
818 .ok_or_else(|| serde::de::Error::missing_field("method"))?
819 .to_string();
820 let params = value.get("params").cloned();
821 Self::from_envelope(&method, params).map_err(serde::de::Error::custom)
822 }
823}
824
825#[derive(Debug, Clone)]
832pub enum ServerRequest {
833 CmdExecApproval(CommandExecutionRequestApprovalParams),
835 FileChangeApproval(FileChangeRequestApprovalParams),
837 ToolRequestUserInput(crate::protocol::ToolRequestUserInputParams),
839 McpServerElicitationRequest(crate::protocol::McpServerElicitationRequestParams),
841 PermissionsRequestApproval(crate::protocol::PermissionsRequestApprovalParams),
843 ItemToolCall(crate::protocol::DynamicToolCallParams),
845 ChatgptAuthTokensRefresh(crate::protocol::ChatgptAuthTokensRefreshParams),
847 AttestationGenerate(crate::protocol::AttestationGenerateParams),
849 ApplyPatchApproval(crate::protocol::ApplyPatchApprovalParams),
851 ExecCommandApproval(crate::protocol::ExecCommandApprovalParams),
853 Unknown {
855 method: String,
856 params: Option<Value>,
857 },
858}
859
860impl ServerRequest {
861 pub fn method(&self) -> &str {
863 match self {
864 Self::CmdExecApproval(_) => methods::CMD_EXEC_APPROVAL,
865 Self::FileChangeApproval(_) => methods::FILE_CHANGE_APPROVAL,
866 Self::ToolRequestUserInput(_) => methods::TOOL_REQUEST_USER_INPUT,
867 Self::McpServerElicitationRequest(_) => methods::MCP_SERVER_ELICITATION_REQUEST,
868 Self::PermissionsRequestApproval(_) => methods::PERMISSIONS_REQUEST_APPROVAL,
869 Self::ItemToolCall(_) => methods::ITEM_TOOL_CALL,
870 Self::ChatgptAuthTokensRefresh(_) => methods::CHATGPT_AUTH_TOKENS_REFRESH,
871 Self::AttestationGenerate(_) => methods::ATTESTATION_GENERATE,
872 Self::ApplyPatchApproval(_) => methods::APPLY_PATCH_APPROVAL,
873 Self::ExecCommandApproval(_) => methods::EXEC_COMMAND_APPROVAL,
874 Self::Unknown { method, .. } => method,
875 }
876 }
877
878 pub fn is_unknown(&self) -> bool {
880 matches!(self, Self::Unknown { .. })
881 }
882
883 pub fn from_envelope(method: &str, params: Option<Value>) -> Result<Self, serde_json::Error> {
885 let params_value = params.clone().unwrap_or(Value::Null);
886 match method {
887 methods::CMD_EXEC_APPROVAL => {
888 serde_json::from_value(params_value).map(Self::CmdExecApproval)
889 }
890 methods::FILE_CHANGE_APPROVAL => {
891 serde_json::from_value(params_value).map(Self::FileChangeApproval)
892 }
893 methods::TOOL_REQUEST_USER_INPUT => {
894 serde_json::from_value(params_value).map(Self::ToolRequestUserInput)
895 }
896 methods::MCP_SERVER_ELICITATION_REQUEST => {
897 serde_json::from_value(params_value).map(Self::McpServerElicitationRequest)
898 }
899 methods::PERMISSIONS_REQUEST_APPROVAL => {
900 serde_json::from_value(params_value).map(Self::PermissionsRequestApproval)
901 }
902 methods::ITEM_TOOL_CALL => serde_json::from_value(params_value).map(Self::ItemToolCall),
903 methods::CHATGPT_AUTH_TOKENS_REFRESH => {
904 serde_json::from_value(params_value).map(Self::ChatgptAuthTokensRefresh)
905 }
906 methods::ATTESTATION_GENERATE => {
907 serde_json::from_value(params_value).map(Self::AttestationGenerate)
908 }
909 methods::APPLY_PATCH_APPROVAL => {
910 serde_json::from_value(params_value).map(Self::ApplyPatchApproval)
911 }
912 methods::EXEC_COMMAND_APPROVAL => {
913 serde_json::from_value(params_value).map(Self::ExecCommandApproval)
914 }
915 _ => Ok(Self::Unknown {
916 method: method.to_string(),
917 params,
918 }),
919 }
920 }
921}
922
923#[allow(clippy::large_enum_variant)]
932#[derive(Debug, Clone)]
933pub enum ServerMessage {
934 Notification(Notification),
936 Request {
938 id: RequestId,
939 request: ServerRequest,
940 },
941}
942
943impl ServerMessage {
944 pub fn is_unknown(&self) -> bool {
946 match self {
947 Self::Notification(n) => n.is_unknown(),
948 Self::Request { request, .. } => request.is_unknown(),
949 }
950 }
951
952 pub fn from_json_str(s: &str) -> Result<Self, Error> {
967 let msg: JsonRpcMessage = serde_json::from_str(s)
968 .map_err(|e| Error::Deserialization(ParseError::from_line(s, e)))?;
969 Self::from_jsonrpc(msg)
970 }
971
972 pub fn from_value(value: Value) -> Result<Self, Error> {
976 let msg: JsonRpcMessage = serde_json::from_value(value.clone())
977 .map_err(|e| Error::Deserialization(ParseError::from_line(value.to_string(), e)))?;
978 Self::from_jsonrpc(msg)
979 }
980
981 fn from_jsonrpc(msg: JsonRpcMessage) -> Result<Self, Error> {
982 match msg {
983 JsonRpcMessage::Notification(JsonRpcNotification { method, params }) => {
984 Notification::from_envelope(&method, params.clone())
985 .map(ServerMessage::Notification)
986 .map_err(|e| Error::Deserialization(ParseError::from_envelope(method, params, e)))
987 }
988 JsonRpcMessage::Request(JsonRpcRequest { id, method, params }) => {
989 ServerRequest::from_envelope(&method, params.clone())
990 .map(|request| ServerMessage::Request { id, request })
991 .map_err(|e| Error::Deserialization(ParseError::from_envelope(method, params, e)))
992 }
993 JsonRpcMessage::Response(resp) => Err(Error::Protocol(format!(
994 "frame is a JSON-RPC response (id={}), not a server-initiated message",
995 resp.id
996 ))),
997 JsonRpcMessage::Error(err) => Err(Error::Protocol(format!(
998 "frame is a JSON-RPC error response (id={}, code={}), not a server-initiated message",
999 err.id, err.error.code
1000 ))),
1001 }
1002 }
1003}
1004
1005#[cfg(test)]
1006mod tests {
1007 use super::*;
1008
1009 #[test]
1010 fn test_notification_unknown_method_routes_to_unknown_variant() {
1011 let n = Notification::from_envelope("foo/bar", Some(serde_json::json!({"x": 1})))
1012 .expect("unknown methods do not error");
1013 match n {
1014 Notification::Unknown { method, params } => {
1015 assert_eq!(method, "foo/bar");
1016 assert_eq!(params, Some(serde_json::json!({"x": 1})));
1017 }
1018 other => panic!("expected Unknown, got {:?}", other),
1019 }
1020 }
1021
1022 #[test]
1023 fn test_notification_known_method_with_bad_params_errors() {
1024 let err = Notification::from_envelope("thread/started", Some(serde_json::json!({})));
1026 assert!(err.is_err());
1027 }
1028
1029 #[test]
1030 fn test_notification_round_trip_envelope() {
1031 let wire = serde_json::json!({
1032 "method": "item/agentMessage/delta",
1033 "params": {"threadId": "t1", "turnId": "u1", "itemId": "i1", "delta": "hi"},
1034 });
1035 let n: Notification = serde_json::from_value(wire.clone()).unwrap();
1036 assert!(matches!(n, Notification::AgentMessageDelta(_)));
1037 let back = serde_json::to_value(&n).unwrap();
1038 assert_eq!(back, wire);
1039 }
1040
1041 #[test]
1042 fn test_turn_id_from_turn_started() {
1043 let wire = serde_json::json!({
1045 "method": "turn/started",
1046 "params": {"threadId": "t1", "turn": {"id": "turn_42", "status": "inProgress"}},
1047 });
1048 let n: Notification = serde_json::from_value(wire).unwrap();
1049 assert!(matches!(n, Notification::TurnStarted(_)));
1050 assert_eq!(n.turn_id(), Some("turn_42"));
1051 }
1052
1053 #[test]
1054 fn test_turn_id_from_delta_field() {
1055 let wire = serde_json::json!({
1057 "method": "item/agentMessage/delta",
1058 "params": {"threadId": "t1", "turnId": "turn_7", "itemId": "i1", "delta": "hi"},
1059 });
1060 let n: Notification = serde_json::from_value(wire).unwrap();
1061 assert_eq!(n.turn_id(), Some("turn_7"));
1062 }
1063
1064 #[test]
1065 fn test_turn_id_none_for_non_turn_notification() {
1066 let n = Notification::from_envelope(
1067 "thread/deleted",
1068 Some(serde_json::json!({"threadId": "t1"})),
1069 )
1070 .unwrap();
1071 assert_eq!(n.turn_id(), None);
1072 }
1073
1074 #[test]
1075 fn test_thread_item_accessor() {
1076 let wire = serde_json::json!({
1077 "method": "item/started",
1078 "params": {
1079 "threadId": "t1",
1080 "turnId": "turn_1",
1081 "startedAtMs": 0,
1082 "item": {"type": "agentMessage", "id": "i1", "text": "hi"},
1083 },
1084 });
1085 let n: Notification = serde_json::from_value(wire).unwrap();
1086 assert!(n.thread_item().is_some());
1087 let other = Notification::from_envelope(
1089 "thread/deleted",
1090 Some(serde_json::json!({"threadId": "t1"})),
1091 )
1092 .unwrap();
1093 assert!(other.thread_item().is_none());
1094 }
1095
1096 #[test]
1097 fn test_server_message_from_json_str_notification() {
1098 let line = r#"{"method":"turn/started","params":{"threadId":"t1","turn":{"id":"turn_9","status":"inProgress"}}}"#;
1099 let msg = ServerMessage::from_json_str(line).expect("parses a notification frame");
1100 match msg {
1101 ServerMessage::Notification(n) => assert_eq!(n.turn_id(), Some("turn_9")),
1102 other => panic!("expected Notification, got {other:?}"),
1103 }
1104 }
1105
1106 #[test]
1107 fn test_server_message_from_value_request() {
1108 let frame = serde_json::json!({
1109 "id": 7,
1110 "method": "execCommandApproval",
1111 "params": {
1112 "callId": "c1",
1113 "command": ["ls"],
1114 "conversationId": "th_1",
1115 "cwd": "/tmp",
1116 "parsedCmd": [],
1117 },
1118 });
1119 let msg = ServerMessage::from_value(frame).expect("parses a request frame");
1120 match msg {
1121 ServerMessage::Request { id, request } => {
1122 assert_eq!(id, RequestId::Integer(7));
1123 assert!(matches!(request, ServerRequest::ExecCommandApproval(_)));
1124 }
1125 other => panic!("expected Request, got {other:?}"),
1126 }
1127 }
1128
1129 #[test]
1130 fn test_server_message_from_json_str_rejects_response() {
1131 let line = r#"{"id":1,"result":{"threadId":"th_abc"}}"#;
1133 let err = ServerMessage::from_json_str(line).unwrap_err();
1134 assert!(matches!(err, Error::Protocol(_)), "got: {err:?}");
1135 }
1136
1137 #[test]
1138 fn test_server_message_from_json_str_unknown_method_ok() {
1139 let line = r#"{"method":"some/future/notification","params":{"x":1}}"#;
1140 let msg = ServerMessage::from_json_str(line).unwrap();
1141 assert!(msg.is_unknown());
1142 }
1143}