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 GatewayOAuthChangedNotification, GuardianWarningNotification, HookCompletedNotification,
40 HookStartedNotification, ItemCompletedNotification,
41 ItemGuardianApprovalReviewCompletedNotification, ItemGuardianApprovalReviewStartedNotification,
42 ItemStartedNotification, McpServerEventStreamNotification,
43 McpServerOauthLoginCompletedNotification, McpServerStatusUpdatedNotification,
44 McpToolCallProgressNotification, ModelReroutedNotification,
45 ModelSafetyBufferingUpdatedNotification, ModelVerificationNotification, PlanDeltaNotification,
46 ProcessExitedNotification, ProcessOutputDeltaNotification, ProjectChangedNotification,
47 ReasoningSummaryPartAddedNotification, ReasoningSummaryTextDeltaNotification,
48 ReasoningTextDeltaNotification, RemoteControlStatusChangedNotification,
49 ServerRequestResolvedNotification, SkillsChangedNotification, StrictReviewRequiredNotification,
50 TerminalInteractionNotification, ThreadArchivedNotification,
51 ThreadAttachmentUpdatedNotification, ThreadClosedNotification, ThreadDeletedNotification,
52 ThreadGoalClearedNotification, ThreadGoalUpdatedNotification, ThreadNameUpdatedNotification,
53 ThreadPredictionUpdatedNotification, ThreadProjectUpdatedNotification,
54 ThreadQueueChangedNotification, ThreadReadStateChangedNotification,
55 ThreadRealtimeClosedNotification, ThreadRealtimeErrorNotification,
56 ThreadRealtimeItemAddedNotification, ThreadRealtimeItemCompletedNotification,
57 ThreadRealtimeItemStartedNotification, ThreadRealtimeItemTranscriptDeltaNotification,
58 ThreadRealtimeOutputAudioDeltaNotification, ThreadRealtimeSdpNotification,
59 ThreadRealtimeStartedNotification, ThreadRealtimeTranscriptDeltaNotification,
60 ThreadRealtimeTranscriptDoneNotification, ThreadRevertedNotification,
61 ThreadSectionChangedNotification, ThreadSettingsUpdatedNotification, ThreadStartedNotification,
62 ThreadStatusChangedNotification, ThreadTokenUsageUpdatedNotification,
63 ThreadUnarchivedNotification, TurnCompletedNotification, TurnDiffUpdatedNotification,
64 TurnModerationMetadataNotification, TurnPlanUpdatedNotification, TurnStartedNotification,
65 WarningNotification, WindowsSandboxSetupCompletedNotification,
66 WindowsWorldWritableWarningNotification,
67};
68use serde::{Deserialize, Deserializer, Serialize, Serializer};
69use serde_json::Value;
70
71#[derive(Debug, Clone)]
77#[allow(clippy::large_enum_variant)] pub enum Notification {
79 ThreadStarted(ThreadStartedNotification),
81 ThreadStatusChanged(ThreadStatusChangedNotification),
83 ThreadReadStateChanged(ThreadReadStateChangedNotification),
86 ThreadSectionChanged(ThreadSectionChangedNotification),
89 ThreadTokenUsageUpdated(ThreadTokenUsageUpdatedNotification),
91 TurnStarted(TurnStartedNotification),
93 TurnCompleted(TurnCompletedNotification),
95 ItemStarted(ItemStartedNotification),
97 ItemCompleted(ItemCompletedNotification),
99 AgentMessageDelta(AgentMessageDeltaNotification),
101 CmdOutputDelta(CommandExecutionOutputDeltaNotification),
103 FileChangeOutputDelta(FileChangeOutputDeltaNotification),
105 ReasoningDelta(ReasoningSummaryTextDeltaNotification),
107 Error(ErrorNotification),
109 AccountRateLimitsUpdated(AccountRateLimitsUpdatedNotification),
111 McpServerStartupStatusUpdated(McpServerStatusUpdatedNotification),
113 RemoteControlStatusChanged(RemoteControlStatusChangedNotification),
115 McpServerOauthLoginCompleted(McpServerOauthLoginCompletedNotification),
117 FileChangePatchUpdated(FileChangePatchUpdatedNotification),
119 PlanDelta(PlanDeltaNotification),
121 TurnPlanUpdated(TurnPlanUpdatedNotification),
123 TurnDiffUpdated(TurnDiffUpdatedNotification),
125 ReasoningSummaryPartAdded(ReasoningSummaryPartAddedNotification),
127 ReasoningTextDelta(ReasoningTextDeltaNotification),
129 AccountLoginCompleted(AccountLoginCompletedNotification),
131 DeprecationNotice(DeprecationNoticeNotification),
133 GuardianWarning(GuardianWarningNotification),
135 Warning(WarningNotification),
137 ThreadArchived(ThreadArchivedNotification),
139 ThreadClosed(ThreadClosedNotification),
141 ThreadDeleted(ThreadDeletedNotification),
143 ThreadUnarchived(ThreadUnarchivedNotification),
145 ThreadGoalCleared(ThreadGoalClearedNotification),
147 ThreadNameUpdated(ThreadNameUpdatedNotification),
149 ThreadAttachmentUpdated(ThreadAttachmentUpdatedNotification),
151 SkillsChanged(SkillsChangedNotification),
153 FsChanged(FsChangedNotification),
155 ConfigWarning(ConfigWarningNotification),
157 AccountUpdated(AccountUpdatedNotification),
159 GatewayOAuthChanged(GatewayOAuthChangedNotification),
161 AppListUpdated(AppListUpdatedNotification),
163 CommandExecOutputDelta(CommandExecOutputDeltaNotification),
165 ExternalAgentConfigImportCompleted(ExternalAgentConfigImportCompletedNotification),
167 FuzzyFileSearchSessionCompleted(FuzzyFileSearchSessionCompletedNotification),
169 FuzzyFileSearchSessionUpdated(FuzzyFileSearchSessionUpdatedNotification),
171 HookCompleted(HookCompletedNotification),
173 HookStarted(HookStartedNotification),
175 ItemGuardianApprovalReviewCompleted(ItemGuardianApprovalReviewCompletedNotification),
177 ItemGuardianApprovalReviewStarted(ItemGuardianApprovalReviewStartedNotification),
179 TerminalInteraction(TerminalInteractionNotification),
181 McpToolCallProgress(McpToolCallProgressNotification),
183 ModelRerouted(ModelReroutedNotification),
185 ModelVerification(ModelVerificationNotification),
187 ProcessExited(ProcessExitedNotification),
189 ProcessOutputDelta(ProcessOutputDeltaNotification),
191 ServerRequestResolved(ServerRequestResolvedNotification),
193 ContextCompacted(ContextCompactedNotification),
195 ThreadGoalUpdated(ThreadGoalUpdatedNotification),
197 ThreadRealtimeClosed(ThreadRealtimeClosedNotification),
199 ThreadRealtimeError(ThreadRealtimeErrorNotification),
201 ThreadRealtimeItemAdded(ThreadRealtimeItemAddedNotification),
203 ThreadRealtimeOutputAudioDelta(ThreadRealtimeOutputAudioDeltaNotification),
205 ThreadRealtimeSdp(ThreadRealtimeSdpNotification),
207 ThreadRealtimeStarted(ThreadRealtimeStartedNotification),
209 ThreadRealtimeTranscriptDelta(ThreadRealtimeTranscriptDeltaNotification),
211 ThreadRealtimeTranscriptDone(ThreadRealtimeTranscriptDoneNotification),
213 WindowsWorldWritableWarning(WindowsWorldWritableWarningNotification),
215 WindowsSandboxSetupCompleted(WindowsSandboxSetupCompletedNotification),
217 ThreadSettingsUpdated(ThreadSettingsUpdatedNotification),
219 TurnModerationMetadata(TurnModerationMetadataNotification),
221 ExternalAgentConfigImportProgress(ExternalAgentConfigImportProgressNotification),
223 ModelSafetyBufferingUpdated(ModelSafetyBufferingUpdatedNotification),
225 ThreadEnvironmentConnected(EnvironmentConnectionNotification),
227 ThreadEnvironmentDisconnected(EnvironmentConnectionNotification),
229 StrictReviewRequired(StrictReviewRequiredNotification),
231 ProjectChanged(ProjectChangedNotification),
233 ThreadProjectUpdated(ThreadProjectUpdatedNotification),
235 ThreadQueueChanged(ThreadQueueChangedNotification),
237 ThreadReverted(ThreadRevertedNotification),
239 McpServerEventStream(McpServerEventStreamNotification),
241 ThreadRealtimeItemStarted(ThreadRealtimeItemStartedNotification),
243 ThreadRealtimeItemCompleted(ThreadRealtimeItemCompletedNotification),
245 ThreadRealtimeItemTranscriptDelta(ThreadRealtimeItemTranscriptDeltaNotification),
247 ModelProviderAuthRecoveryStarted(AuthRecoveryNotification),
249 ModelProviderAuthRecoveryCompleted(AuthRecoveryNotification),
251 ThreadPredictionUpdated(ThreadPredictionUpdatedNotification),
253 Unknown {
257 method: String,
258 params: Option<Value>,
259 },
260}
261
262impl Notification {
263 pub fn method(&self) -> &str {
265 match self {
266 Self::ThreadStarted(_) => methods::THREAD_STARTED,
267 Self::StrictReviewRequired(_) => methods::STRICT_REVIEW_REQUIRED,
268 Self::ProjectChanged(_) => methods::PROJECT_CHANGED,
269 Self::ThreadProjectUpdated(_) => methods::THREAD_PROJECT_UPDATED,
270 Self::ThreadQueueChanged(_) => methods::THREAD_QUEUE_CHANGED,
271 Self::ThreadReverted(_) => methods::THREAD_REVERTED,
272 Self::ThreadPredictionUpdated(_) => methods::THREAD_PREDICTION_UPDATED,
273 Self::McpServerEventStream(_) => methods::MCP_SERVER_EVENT_STREAM,
274 Self::ThreadRealtimeItemStarted(_) => methods::THREAD_REALTIME_ITEM_STARTED,
275 Self::ThreadRealtimeItemCompleted(_) => methods::THREAD_REALTIME_ITEM_COMPLETED,
276 Self::ThreadRealtimeItemTranscriptDelta(_) => {
277 methods::THREAD_REALTIME_ITEM_TRANSCRIPT_DELTA
278 }
279 Self::ModelProviderAuthRecoveryStarted(_) => {
280 methods::MODEL_PROVIDER_AUTH_RECOVERY_STARTED
281 }
282 Self::ModelProviderAuthRecoveryCompleted(_) => {
283 methods::MODEL_PROVIDER_AUTH_RECOVERY_COMPLETED
284 }
285 Self::ThreadStatusChanged(_) => methods::THREAD_STATUS_CHANGED,
286 Self::ThreadReadStateChanged(_) => methods::THREAD_READ_STATE_CHANGED,
287 Self::ThreadSectionChanged(_) => methods::THREAD_SECTION_CHANGED,
288 Self::ThreadTokenUsageUpdated(_) => methods::THREAD_TOKEN_USAGE_UPDATED,
289 Self::TurnStarted(_) => methods::TURN_STARTED,
290 Self::TurnCompleted(_) => methods::TURN_COMPLETED,
291 Self::ItemStarted(_) => methods::ITEM_STARTED,
292 Self::ItemCompleted(_) => methods::ITEM_COMPLETED,
293 Self::AgentMessageDelta(_) => methods::AGENT_MESSAGE_DELTA,
294 Self::CmdOutputDelta(_) => methods::CMD_OUTPUT_DELTA,
295 Self::FileChangeOutputDelta(_) => methods::FILE_CHANGE_OUTPUT_DELTA,
296 Self::ReasoningDelta(_) => methods::REASONING_DELTA,
297 Self::Error(_) => methods::ERROR,
298 Self::AccountRateLimitsUpdated(_) => methods::ACCOUNT_RATE_LIMITS_UPDATED,
299 Self::McpServerStartupStatusUpdated(_) => methods::MCP_SERVER_STARTUP_STATUS_UPDATED,
300 Self::RemoteControlStatusChanged(_) => methods::REMOTE_CONTROL_STATUS_CHANGED,
301 Self::McpServerOauthLoginCompleted(_) => methods::MCP_SERVER_OAUTH_LOGIN_COMPLETED,
302 Self::FileChangePatchUpdated(_) => methods::FILE_CHANGE_PATCH_UPDATED,
303 Self::PlanDelta(_) => methods::PLAN_DELTA,
304 Self::TurnPlanUpdated(_) => methods::TURN_PLAN_UPDATED,
305 Self::TurnDiffUpdated(_) => methods::TURN_DIFF_UPDATED,
306 Self::ReasoningSummaryPartAdded(_) => methods::REASONING_SUMMARY_PART_ADDED,
307 Self::ReasoningTextDelta(_) => methods::REASONING_TEXT_DELTA,
308 Self::AccountLoginCompleted(_) => methods::ACCOUNT_LOGIN_COMPLETED,
309 Self::DeprecationNotice(_) => methods::DEPRECATION_NOTICE,
310 Self::GuardianWarning(_) => methods::GUARDIAN_WARNING,
311 Self::Warning(_) => methods::WARNING,
312 Self::ThreadArchived(_) => methods::THREAD_ARCHIVED,
313 Self::ThreadClosed(_) => methods::THREAD_CLOSED,
314 Self::ThreadDeleted(_) => methods::THREAD_DELETED,
315 Self::ThreadUnarchived(_) => methods::THREAD_UNARCHIVED,
316 Self::ThreadGoalCleared(_) => methods::THREAD_GOAL_CLEARED,
317 Self::ThreadNameUpdated(_) => methods::THREAD_NAME_UPDATED,
318 Self::ThreadAttachmentUpdated(_) => methods::THREAD_ATTACHMENT_UPDATED,
319 Self::SkillsChanged(_) => methods::SKILLS_CHANGED,
320 Self::FsChanged(_) => methods::FS_CHANGED,
321 Self::ConfigWarning(_) => methods::CONFIG_WARNING,
322 Self::AccountUpdated(_) => methods::ACCOUNT_UPDATED,
323 Self::GatewayOAuthChanged(_) => methods::ACCOUNT_GATEWAY_OAUTH_CHANGED,
324 Self::AppListUpdated(_) => methods::APP_LIST_UPDATED,
325 Self::CommandExecOutputDelta(_) => methods::COMMAND_EXEC_OUTPUT_DELTA,
326 Self::ExternalAgentConfigImportCompleted(_) => {
327 methods::EXTERNAL_AGENT_CONFIG_IMPORT_COMPLETED
328 }
329 Self::FuzzyFileSearchSessionCompleted(_) => {
330 methods::FUZZY_FILE_SEARCH_SESSION_COMPLETED
331 }
332 Self::FuzzyFileSearchSessionUpdated(_) => methods::FUZZY_FILE_SEARCH_SESSION_UPDATED,
333 Self::HookCompleted(_) => methods::HOOK_COMPLETED,
334 Self::HookStarted(_) => methods::HOOK_STARTED,
335 Self::ItemGuardianApprovalReviewCompleted(_) => {
336 methods::ITEM_AUTO_APPROVAL_REVIEW_COMPLETED
337 }
338 Self::ItemGuardianApprovalReviewStarted(_) => {
339 methods::ITEM_AUTO_APPROVAL_REVIEW_STARTED
340 }
341 Self::TerminalInteraction(_) => methods::ITEM_COMMAND_EXEC_TERMINAL_INTERACTION,
342 Self::McpToolCallProgress(_) => methods::ITEM_MCP_TOOL_CALL_PROGRESS,
343 Self::ModelRerouted(_) => methods::MODEL_REROUTED,
344 Self::ModelVerification(_) => methods::MODEL_VERIFICATION,
345 Self::ProcessExited(_) => methods::PROCESS_EXITED,
346 Self::ProcessOutputDelta(_) => methods::PROCESS_OUTPUT_DELTA,
347 Self::ServerRequestResolved(_) => methods::SERVER_REQUEST_RESOLVED,
348 Self::ContextCompacted(_) => methods::THREAD_COMPACTED,
349 Self::ThreadGoalUpdated(_) => methods::THREAD_GOAL_UPDATED,
350 Self::ThreadRealtimeClosed(_) => methods::THREAD_REALTIME_CLOSED,
351 Self::ThreadRealtimeError(_) => methods::THREAD_REALTIME_ERROR,
352 Self::ThreadRealtimeItemAdded(_) => methods::THREAD_REALTIME_ITEM_ADDED,
353 Self::ThreadRealtimeOutputAudioDelta(_) => methods::THREAD_REALTIME_OUTPUT_AUDIO_DELTA,
354 Self::ThreadRealtimeSdp(_) => methods::THREAD_REALTIME_SDP,
355 Self::ThreadRealtimeStarted(_) => methods::THREAD_REALTIME_STARTED,
356 Self::ThreadRealtimeTranscriptDelta(_) => methods::THREAD_REALTIME_TRANSCRIPT_DELTA,
357 Self::ThreadRealtimeTranscriptDone(_) => methods::THREAD_REALTIME_TRANSCRIPT_DONE,
358 Self::WindowsWorldWritableWarning(_) => methods::WINDOWS_WORLD_WRITABLE_WARNING,
359 Self::WindowsSandboxSetupCompleted(_) => methods::WINDOWS_SANDBOX_SETUP_COMPLETED,
360 Self::ThreadSettingsUpdated(_) => methods::THREAD_SETTINGS_UPDATED,
361 Self::TurnModerationMetadata(_) => methods::TURN_MODERATION_METADATA,
362 Self::ExternalAgentConfigImportProgress(_) => {
363 methods::EXTERNAL_AGENT_CONFIG_IMPORT_PROGRESS
364 }
365 Self::ModelSafetyBufferingUpdated(_) => methods::MODEL_SAFETY_BUFFERING_UPDATED,
366 Self::ThreadEnvironmentConnected(_) => methods::THREAD_ENVIRONMENT_CONNECTED,
367 Self::ThreadEnvironmentDisconnected(_) => methods::THREAD_ENVIRONMENT_DISCONNECTED,
368 Self::Unknown { method, .. } => method,
369 }
370 }
371
372 pub fn is_unknown(&self) -> bool {
374 matches!(self, Self::Unknown { .. })
375 }
376
377 pub fn turn_id(&self) -> Option<&str> {
389 let id = match self {
390 Self::TurnStarted(n) => n.turn.id.as_str(),
391 Self::TurnCompleted(n) => n.turn.id.as_str(),
392 Self::AgentMessageDelta(n) => n.turn_id.as_str(),
393 Self::CmdOutputDelta(n) => n.turn_id.as_str(),
394 Self::FileChangeOutputDelta(n) => n.turn_id.as_str(),
395 Self::ReasoningDelta(n) => n.turn_id.as_str(),
396 Self::Error(n) => n.turn_id.as_str(),
397 Self::FileChangePatchUpdated(n) => n.turn_id.as_str(),
398 Self::PlanDelta(n) => n.turn_id.as_str(),
399 Self::TurnPlanUpdated(n) => n.turn_id.as_str(),
400 Self::TurnDiffUpdated(n) => n.turn_id.as_str(),
401 Self::TurnModerationMetadata(n) => n.turn_id.as_str(),
402 Self::ReasoningSummaryPartAdded(n) => n.turn_id.as_str(),
403 Self::ReasoningTextDelta(n) => n.turn_id.as_str(),
404 Self::ItemStarted(n) => n.turn_id.as_str(),
405 Self::ItemCompleted(n) => n.turn_id.as_str(),
406 Self::ContextCompacted(n) => n.turn_id.as_str(),
407 Self::McpToolCallProgress(n) => n.turn_id.as_str(),
408 Self::ModelRerouted(n) => n.turn_id.as_str(),
409 Self::ModelVerification(n) => n.turn_id.as_str(),
410 Self::ModelSafetyBufferingUpdated(n) => n.turn_id.as_str(),
411 Self::TerminalInteraction(n) => n.turn_id.as_str(),
412 Self::ThreadTokenUsageUpdated(n) => n.turn_id.as_str(),
413 Self::ItemGuardianApprovalReviewStarted(n) => n.turn_id.as_str(),
414 Self::ItemGuardianApprovalReviewCompleted(n) => n.turn_id.as_str(),
415 _ => return None,
416 };
417 (!id.is_empty()).then_some(id)
418 }
419
420 pub fn thread_item(&self) -> Option<&crate::protocol::ThreadItem> {
426 match self {
427 Self::ItemStarted(n) => Some(&n.item),
428 Self::ItemCompleted(n) => Some(&n.item),
429 _ => None,
430 }
431 }
432
433 pub fn from_envelope(method: &str, params: Option<Value>) -> Result<Self, serde_json::Error> {
439 let params_value = params.clone().unwrap_or(Value::Null);
440 match method {
441 methods::THREAD_STARTED => {
442 serde_json::from_value(params_value).map(Self::ThreadStarted)
443 }
444 methods::STRICT_REVIEW_REQUIRED => {
445 serde_json::from_value(params_value).map(Self::StrictReviewRequired)
446 }
447 methods::PROJECT_CHANGED => {
448 serde_json::from_value(params_value).map(Self::ProjectChanged)
449 }
450 methods::THREAD_PROJECT_UPDATED => {
451 serde_json::from_value(params_value).map(Self::ThreadProjectUpdated)
452 }
453 methods::THREAD_QUEUE_CHANGED => {
454 serde_json::from_value(params_value).map(Self::ThreadQueueChanged)
455 }
456 methods::THREAD_REVERTED => {
457 serde_json::from_value(params_value).map(Self::ThreadReverted)
458 }
459 methods::THREAD_PREDICTION_UPDATED => {
460 serde_json::from_value(params_value).map(Self::ThreadPredictionUpdated)
461 }
462 methods::MCP_SERVER_EVENT_STREAM => {
463 serde_json::from_value(params_value).map(Self::McpServerEventStream)
464 }
465 methods::THREAD_REALTIME_ITEM_STARTED => {
466 serde_json::from_value(params_value).map(Self::ThreadRealtimeItemStarted)
467 }
468 methods::THREAD_REALTIME_ITEM_COMPLETED => {
469 serde_json::from_value(params_value).map(Self::ThreadRealtimeItemCompleted)
470 }
471 methods::THREAD_REALTIME_ITEM_TRANSCRIPT_DELTA => {
472 serde_json::from_value(params_value).map(Self::ThreadRealtimeItemTranscriptDelta)
473 }
474 methods::MODEL_PROVIDER_AUTH_RECOVERY_STARTED => {
475 serde_json::from_value(params_value).map(Self::ModelProviderAuthRecoveryStarted)
476 }
477 methods::MODEL_PROVIDER_AUTH_RECOVERY_COMPLETED => {
478 serde_json::from_value(params_value).map(Self::ModelProviderAuthRecoveryCompleted)
479 }
480 methods::THREAD_STATUS_CHANGED => {
481 serde_json::from_value(params_value).map(Self::ThreadStatusChanged)
482 }
483 methods::THREAD_READ_STATE_CHANGED => {
484 serde_json::from_value(params_value).map(Self::ThreadReadStateChanged)
485 }
486 methods::THREAD_SECTION_CHANGED => {
487 serde_json::from_value(params_value).map(Self::ThreadSectionChanged)
488 }
489 methods::THREAD_TOKEN_USAGE_UPDATED => {
490 serde_json::from_value(params_value).map(Self::ThreadTokenUsageUpdated)
491 }
492 methods::TURN_STARTED => serde_json::from_value(params_value).map(Self::TurnStarted),
493 methods::TURN_COMPLETED => {
494 serde_json::from_value(params_value).map(Self::TurnCompleted)
495 }
496 methods::ITEM_STARTED => serde_json::from_value(params_value).map(Self::ItemStarted),
497 methods::ITEM_COMPLETED => {
498 serde_json::from_value(params_value).map(Self::ItemCompleted)
499 }
500 methods::AGENT_MESSAGE_DELTA => {
501 serde_json::from_value(params_value).map(Self::AgentMessageDelta)
502 }
503 methods::CMD_OUTPUT_DELTA => {
504 serde_json::from_value(params_value).map(Self::CmdOutputDelta)
505 }
506 methods::FILE_CHANGE_OUTPUT_DELTA => {
507 serde_json::from_value(params_value).map(Self::FileChangeOutputDelta)
508 }
509 methods::REASONING_DELTA => {
510 serde_json::from_value(params_value).map(Self::ReasoningDelta)
511 }
512 methods::ERROR => serde_json::from_value(params_value).map(Self::Error),
513 methods::ACCOUNT_RATE_LIMITS_UPDATED => {
514 serde_json::from_value(params_value).map(Self::AccountRateLimitsUpdated)
515 }
516 methods::MCP_SERVER_STARTUP_STATUS_UPDATED => {
517 serde_json::from_value(params_value).map(Self::McpServerStartupStatusUpdated)
518 }
519 methods::REMOTE_CONTROL_STATUS_CHANGED => {
520 serde_json::from_value(params_value).map(Self::RemoteControlStatusChanged)
521 }
522 methods::MCP_SERVER_OAUTH_LOGIN_COMPLETED => {
523 serde_json::from_value(params_value).map(Self::McpServerOauthLoginCompleted)
524 }
525 methods::FILE_CHANGE_PATCH_UPDATED => {
526 serde_json::from_value(params_value).map(Self::FileChangePatchUpdated)
527 }
528 methods::PLAN_DELTA => serde_json::from_value(params_value).map(Self::PlanDelta),
529 methods::TURN_PLAN_UPDATED => {
530 serde_json::from_value(params_value).map(Self::TurnPlanUpdated)
531 }
532 methods::TURN_DIFF_UPDATED => {
533 serde_json::from_value(params_value).map(Self::TurnDiffUpdated)
534 }
535 methods::REASONING_SUMMARY_PART_ADDED => {
536 serde_json::from_value(params_value).map(Self::ReasoningSummaryPartAdded)
537 }
538 methods::REASONING_TEXT_DELTA => {
539 serde_json::from_value(params_value).map(Self::ReasoningTextDelta)
540 }
541 methods::ACCOUNT_LOGIN_COMPLETED => {
542 serde_json::from_value(params_value).map(Self::AccountLoginCompleted)
543 }
544 methods::DEPRECATION_NOTICE => {
545 serde_json::from_value(params_value).map(Self::DeprecationNotice)
546 }
547 methods::GUARDIAN_WARNING => {
548 serde_json::from_value(params_value).map(Self::GuardianWarning)
549 }
550 methods::WARNING => serde_json::from_value(params_value).map(Self::Warning),
551 methods::THREAD_ARCHIVED => {
552 serde_json::from_value(params_value).map(Self::ThreadArchived)
553 }
554 methods::THREAD_CLOSED => serde_json::from_value(params_value).map(Self::ThreadClosed),
555 methods::THREAD_DELETED => {
556 serde_json::from_value(params_value).map(Self::ThreadDeleted)
557 }
558 methods::THREAD_UNARCHIVED => {
559 serde_json::from_value(params_value).map(Self::ThreadUnarchived)
560 }
561 methods::THREAD_GOAL_CLEARED => {
562 serde_json::from_value(params_value).map(Self::ThreadGoalCleared)
563 }
564 methods::THREAD_NAME_UPDATED => {
565 serde_json::from_value(params_value).map(Self::ThreadNameUpdated)
566 }
567 methods::THREAD_ATTACHMENT_UPDATED => {
568 serde_json::from_value(params_value).map(Self::ThreadAttachmentUpdated)
569 }
570 methods::SKILLS_CHANGED => {
571 serde_json::from_value(params_value).map(Self::SkillsChanged)
572 }
573 methods::FS_CHANGED => serde_json::from_value(params_value).map(Self::FsChanged),
574 methods::CONFIG_WARNING => {
575 serde_json::from_value(params_value).map(Self::ConfigWarning)
576 }
577 methods::ACCOUNT_UPDATED => {
578 serde_json::from_value(params_value).map(Self::AccountUpdated)
579 }
580 methods::ACCOUNT_GATEWAY_OAUTH_CHANGED => {
581 serde_json::from_value(params_value).map(Self::GatewayOAuthChanged)
582 }
583 methods::APP_LIST_UPDATED => {
584 serde_json::from_value(params_value).map(Self::AppListUpdated)
585 }
586 methods::COMMAND_EXEC_OUTPUT_DELTA => {
587 serde_json::from_value(params_value).map(Self::CommandExecOutputDelta)
588 }
589 methods::EXTERNAL_AGENT_CONFIG_IMPORT_COMPLETED => {
590 serde_json::from_value(params_value).map(Self::ExternalAgentConfigImportCompleted)
591 }
592 methods::FUZZY_FILE_SEARCH_SESSION_COMPLETED => {
593 serde_json::from_value(params_value).map(Self::FuzzyFileSearchSessionCompleted)
594 }
595 methods::FUZZY_FILE_SEARCH_SESSION_UPDATED => {
596 serde_json::from_value(params_value).map(Self::FuzzyFileSearchSessionUpdated)
597 }
598 methods::HOOK_COMPLETED => {
599 serde_json::from_value(params_value).map(Self::HookCompleted)
600 }
601 methods::HOOK_STARTED => serde_json::from_value(params_value).map(Self::HookStarted),
602 methods::ITEM_AUTO_APPROVAL_REVIEW_COMPLETED => {
603 serde_json::from_value(params_value).map(Self::ItemGuardianApprovalReviewCompleted)
604 }
605 methods::ITEM_AUTO_APPROVAL_REVIEW_STARTED => {
606 serde_json::from_value(params_value).map(Self::ItemGuardianApprovalReviewStarted)
607 }
608 methods::ITEM_COMMAND_EXEC_TERMINAL_INTERACTION => {
609 serde_json::from_value(params_value).map(Self::TerminalInteraction)
610 }
611 methods::ITEM_MCP_TOOL_CALL_PROGRESS => {
612 serde_json::from_value(params_value).map(Self::McpToolCallProgress)
613 }
614 methods::MODEL_REROUTED => {
615 serde_json::from_value(params_value).map(Self::ModelRerouted)
616 }
617 methods::MODEL_VERIFICATION => {
618 serde_json::from_value(params_value).map(Self::ModelVerification)
619 }
620 methods::PROCESS_EXITED => {
621 serde_json::from_value(params_value).map(Self::ProcessExited)
622 }
623 methods::PROCESS_OUTPUT_DELTA => {
624 serde_json::from_value(params_value).map(Self::ProcessOutputDelta)
625 }
626 methods::SERVER_REQUEST_RESOLVED => {
627 serde_json::from_value(params_value).map(Self::ServerRequestResolved)
628 }
629 methods::THREAD_COMPACTED => {
630 serde_json::from_value(params_value).map(Self::ContextCompacted)
631 }
632 methods::THREAD_GOAL_UPDATED => {
633 serde_json::from_value(params_value).map(Self::ThreadGoalUpdated)
634 }
635 methods::THREAD_REALTIME_CLOSED => {
636 serde_json::from_value(params_value).map(Self::ThreadRealtimeClosed)
637 }
638 methods::THREAD_REALTIME_ERROR => {
639 serde_json::from_value(params_value).map(Self::ThreadRealtimeError)
640 }
641 methods::THREAD_REALTIME_ITEM_ADDED => {
642 serde_json::from_value(params_value).map(Self::ThreadRealtimeItemAdded)
643 }
644 methods::THREAD_REALTIME_OUTPUT_AUDIO_DELTA => {
645 serde_json::from_value(params_value).map(Self::ThreadRealtimeOutputAudioDelta)
646 }
647 methods::THREAD_REALTIME_SDP => {
648 serde_json::from_value(params_value).map(Self::ThreadRealtimeSdp)
649 }
650 methods::THREAD_REALTIME_STARTED => {
651 serde_json::from_value(params_value).map(Self::ThreadRealtimeStarted)
652 }
653 methods::THREAD_REALTIME_TRANSCRIPT_DELTA => {
654 serde_json::from_value(params_value).map(Self::ThreadRealtimeTranscriptDelta)
655 }
656 methods::THREAD_REALTIME_TRANSCRIPT_DONE => {
657 serde_json::from_value(params_value).map(Self::ThreadRealtimeTranscriptDone)
658 }
659 methods::WINDOWS_WORLD_WRITABLE_WARNING => {
660 serde_json::from_value(params_value).map(Self::WindowsWorldWritableWarning)
661 }
662 methods::WINDOWS_SANDBOX_SETUP_COMPLETED => {
663 serde_json::from_value(params_value).map(Self::WindowsSandboxSetupCompleted)
664 }
665 methods::THREAD_SETTINGS_UPDATED => {
666 serde_json::from_value(params_value).map(Self::ThreadSettingsUpdated)
667 }
668 methods::TURN_MODERATION_METADATA => {
669 serde_json::from_value(params_value).map(Self::TurnModerationMetadata)
670 }
671 methods::EXTERNAL_AGENT_CONFIG_IMPORT_PROGRESS => {
672 serde_json::from_value(params_value).map(Self::ExternalAgentConfigImportProgress)
673 }
674 methods::MODEL_SAFETY_BUFFERING_UPDATED => {
675 serde_json::from_value(params_value).map(Self::ModelSafetyBufferingUpdated)
676 }
677 methods::THREAD_ENVIRONMENT_CONNECTED => {
678 serde_json::from_value(params_value).map(Self::ThreadEnvironmentConnected)
679 }
680 methods::THREAD_ENVIRONMENT_DISCONNECTED => {
681 serde_json::from_value(params_value).map(Self::ThreadEnvironmentDisconnected)
682 }
683 _ => Ok(Self::Unknown {
684 method: method.to_string(),
685 params,
686 }),
687 }
688 }
689
690 pub fn into_envelope(self) -> Result<(String, Option<Value>), serde_json::Error> {
692 fn pack<T: Serialize>(
693 method: &str,
694 v: &T,
695 ) -> Result<(String, Option<Value>), serde_json::Error> {
696 Ok((method.to_string(), Some(serde_json::to_value(v)?)))
697 }
698 match &self {
699 Self::ThreadStarted(v) => pack(methods::THREAD_STARTED, v),
700 Self::StrictReviewRequired(v) => pack(methods::STRICT_REVIEW_REQUIRED, v),
701 Self::ProjectChanged(v) => pack(methods::PROJECT_CHANGED, v),
702 Self::ThreadProjectUpdated(v) => pack(methods::THREAD_PROJECT_UPDATED, v),
703 Self::ThreadQueueChanged(v) => pack(methods::THREAD_QUEUE_CHANGED, v),
704 Self::ThreadReverted(v) => pack(methods::THREAD_REVERTED, v),
705 Self::ThreadPredictionUpdated(v) => pack(methods::THREAD_PREDICTION_UPDATED, v),
706 Self::McpServerEventStream(v) => pack(methods::MCP_SERVER_EVENT_STREAM, v),
707 Self::ThreadRealtimeItemStarted(v) => pack(methods::THREAD_REALTIME_ITEM_STARTED, v),
708 Self::ThreadRealtimeItemCompleted(v) => {
709 pack(methods::THREAD_REALTIME_ITEM_COMPLETED, v)
710 }
711 Self::ThreadRealtimeItemTranscriptDelta(v) => {
712 pack(methods::THREAD_REALTIME_ITEM_TRANSCRIPT_DELTA, v)
713 }
714 Self::ModelProviderAuthRecoveryStarted(v) => {
715 pack(methods::MODEL_PROVIDER_AUTH_RECOVERY_STARTED, v)
716 }
717 Self::ModelProviderAuthRecoveryCompleted(v) => {
718 pack(methods::MODEL_PROVIDER_AUTH_RECOVERY_COMPLETED, v)
719 }
720 Self::ThreadStatusChanged(v) => pack(methods::THREAD_STATUS_CHANGED, v),
721 Self::ThreadReadStateChanged(v) => pack(methods::THREAD_READ_STATE_CHANGED, v),
722 Self::ThreadSectionChanged(v) => pack(methods::THREAD_SECTION_CHANGED, v),
723 Self::ThreadTokenUsageUpdated(v) => pack(methods::THREAD_TOKEN_USAGE_UPDATED, v),
724 Self::TurnStarted(v) => pack(methods::TURN_STARTED, v),
725 Self::TurnCompleted(v) => pack(methods::TURN_COMPLETED, v),
726 Self::ItemStarted(v) => pack(methods::ITEM_STARTED, v),
727 Self::ItemCompleted(v) => pack(methods::ITEM_COMPLETED, v),
728 Self::AgentMessageDelta(v) => pack(methods::AGENT_MESSAGE_DELTA, v),
729 Self::CmdOutputDelta(v) => pack(methods::CMD_OUTPUT_DELTA, v),
730 Self::FileChangeOutputDelta(v) => pack(methods::FILE_CHANGE_OUTPUT_DELTA, v),
731 Self::ReasoningDelta(v) => pack(methods::REASONING_DELTA, v),
732 Self::Error(v) => pack(methods::ERROR, v),
733 Self::AccountRateLimitsUpdated(v) => pack(methods::ACCOUNT_RATE_LIMITS_UPDATED, v),
734 Self::McpServerStartupStatusUpdated(v) => {
735 pack(methods::MCP_SERVER_STARTUP_STATUS_UPDATED, v)
736 }
737 Self::RemoteControlStatusChanged(v) => pack(methods::REMOTE_CONTROL_STATUS_CHANGED, v),
738 Self::McpServerOauthLoginCompleted(v) => {
739 pack(methods::MCP_SERVER_OAUTH_LOGIN_COMPLETED, v)
740 }
741 Self::FileChangePatchUpdated(v) => pack(methods::FILE_CHANGE_PATCH_UPDATED, v),
742 Self::PlanDelta(v) => pack(methods::PLAN_DELTA, v),
743 Self::TurnPlanUpdated(v) => pack(methods::TURN_PLAN_UPDATED, v),
744 Self::TurnDiffUpdated(v) => pack(methods::TURN_DIFF_UPDATED, v),
745 Self::ReasoningSummaryPartAdded(v) => pack(methods::REASONING_SUMMARY_PART_ADDED, v),
746 Self::ReasoningTextDelta(v) => pack(methods::REASONING_TEXT_DELTA, v),
747 Self::AccountLoginCompleted(v) => pack(methods::ACCOUNT_LOGIN_COMPLETED, v),
748 Self::DeprecationNotice(v) => pack(methods::DEPRECATION_NOTICE, v),
749 Self::GuardianWarning(v) => pack(methods::GUARDIAN_WARNING, v),
750 Self::Warning(v) => pack(methods::WARNING, v),
751 Self::ThreadArchived(v) => pack(methods::THREAD_ARCHIVED, v),
752 Self::ThreadClosed(v) => pack(methods::THREAD_CLOSED, v),
753 Self::ThreadDeleted(v) => pack(methods::THREAD_DELETED, v),
754 Self::ThreadUnarchived(v) => pack(methods::THREAD_UNARCHIVED, v),
755 Self::ThreadGoalCleared(v) => pack(methods::THREAD_GOAL_CLEARED, v),
756 Self::ThreadNameUpdated(v) => pack(methods::THREAD_NAME_UPDATED, v),
757 Self::ThreadAttachmentUpdated(v) => pack(methods::THREAD_ATTACHMENT_UPDATED, v),
758 Self::SkillsChanged(v) => pack(methods::SKILLS_CHANGED, v),
759 Self::FsChanged(v) => pack(methods::FS_CHANGED, v),
760 Self::ConfigWarning(v) => pack(methods::CONFIG_WARNING, v),
761 Self::AccountUpdated(v) => pack(methods::ACCOUNT_UPDATED, v),
762 Self::GatewayOAuthChanged(v) => pack(methods::ACCOUNT_GATEWAY_OAUTH_CHANGED, v),
763 Self::AppListUpdated(v) => pack(methods::APP_LIST_UPDATED, v),
764 Self::CommandExecOutputDelta(v) => pack(methods::COMMAND_EXEC_OUTPUT_DELTA, v),
765 Self::ExternalAgentConfigImportCompleted(v) => {
766 pack(methods::EXTERNAL_AGENT_CONFIG_IMPORT_COMPLETED, v)
767 }
768 Self::FuzzyFileSearchSessionCompleted(v) => {
769 pack(methods::FUZZY_FILE_SEARCH_SESSION_COMPLETED, v)
770 }
771 Self::FuzzyFileSearchSessionUpdated(v) => {
772 pack(methods::FUZZY_FILE_SEARCH_SESSION_UPDATED, v)
773 }
774 Self::HookCompleted(v) => pack(methods::HOOK_COMPLETED, v),
775 Self::HookStarted(v) => pack(methods::HOOK_STARTED, v),
776 Self::ItemGuardianApprovalReviewCompleted(v) => {
777 pack(methods::ITEM_AUTO_APPROVAL_REVIEW_COMPLETED, v)
778 }
779 Self::ItemGuardianApprovalReviewStarted(v) => {
780 pack(methods::ITEM_AUTO_APPROVAL_REVIEW_STARTED, v)
781 }
782 Self::TerminalInteraction(v) => {
783 pack(methods::ITEM_COMMAND_EXEC_TERMINAL_INTERACTION, v)
784 }
785 Self::McpToolCallProgress(v) => pack(methods::ITEM_MCP_TOOL_CALL_PROGRESS, v),
786 Self::ModelRerouted(v) => pack(methods::MODEL_REROUTED, v),
787 Self::ModelVerification(v) => pack(methods::MODEL_VERIFICATION, v),
788 Self::ProcessExited(v) => pack(methods::PROCESS_EXITED, v),
789 Self::ProcessOutputDelta(v) => pack(methods::PROCESS_OUTPUT_DELTA, v),
790 Self::ServerRequestResolved(v) => pack(methods::SERVER_REQUEST_RESOLVED, v),
791 Self::ContextCompacted(v) => pack(methods::THREAD_COMPACTED, v),
792 Self::ThreadGoalUpdated(v) => pack(methods::THREAD_GOAL_UPDATED, v),
793 Self::ThreadRealtimeClosed(v) => pack(methods::THREAD_REALTIME_CLOSED, v),
794 Self::ThreadRealtimeError(v) => pack(methods::THREAD_REALTIME_ERROR, v),
795 Self::ThreadRealtimeItemAdded(v) => pack(methods::THREAD_REALTIME_ITEM_ADDED, v),
796 Self::ThreadRealtimeOutputAudioDelta(v) => {
797 pack(methods::THREAD_REALTIME_OUTPUT_AUDIO_DELTA, v)
798 }
799 Self::ThreadRealtimeSdp(v) => pack(methods::THREAD_REALTIME_SDP, v),
800 Self::ThreadRealtimeStarted(v) => pack(methods::THREAD_REALTIME_STARTED, v),
801 Self::ThreadRealtimeTranscriptDelta(v) => {
802 pack(methods::THREAD_REALTIME_TRANSCRIPT_DELTA, v)
803 }
804 Self::ThreadRealtimeTranscriptDone(v) => {
805 pack(methods::THREAD_REALTIME_TRANSCRIPT_DONE, v)
806 }
807 Self::WindowsWorldWritableWarning(v) => {
808 pack(methods::WINDOWS_WORLD_WRITABLE_WARNING, v)
809 }
810 Self::WindowsSandboxSetupCompleted(v) => {
811 pack(methods::WINDOWS_SANDBOX_SETUP_COMPLETED, v)
812 }
813 Self::ThreadSettingsUpdated(v) => pack(methods::THREAD_SETTINGS_UPDATED, v),
814 Self::TurnModerationMetadata(v) => pack(methods::TURN_MODERATION_METADATA, v),
815 Self::ExternalAgentConfigImportProgress(v) => {
816 pack(methods::EXTERNAL_AGENT_CONFIG_IMPORT_PROGRESS, v)
817 }
818 Self::ModelSafetyBufferingUpdated(v) => {
819 pack(methods::MODEL_SAFETY_BUFFERING_UPDATED, v)
820 }
821 Self::ThreadEnvironmentConnected(v) => pack(methods::THREAD_ENVIRONMENT_CONNECTED, v),
822 Self::ThreadEnvironmentDisconnected(v) => {
823 pack(methods::THREAD_ENVIRONMENT_DISCONNECTED, v)
824 }
825 Self::Unknown { method, params } => Ok((method.clone(), params.clone())),
826 }
827 }
828}
829
830impl Serialize for Notification {
831 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
832 let (method, params) = self
833 .clone()
834 .into_envelope()
835 .map_err(serde::ser::Error::custom)?;
836 let mut env = serde_json::Map::new();
837 env.insert("method".to_string(), Value::String(method));
838 if let Some(p) = params {
839 env.insert("params".to_string(), p);
840 }
841 Value::Object(env).serialize(serializer)
842 }
843}
844
845impl<'de> Deserialize<'de> for Notification {
846 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
847 let value = Value::deserialize(deserializer)?;
848 let method = value
849 .get("method")
850 .and_then(|v| v.as_str())
851 .ok_or_else(|| serde::de::Error::missing_field("method"))?
852 .to_string();
853 let params = value.get("params").cloned();
854 Self::from_envelope(&method, params).map_err(serde::de::Error::custom)
855 }
856}
857
858#[derive(Debug, Clone)]
865pub enum ServerRequest {
866 CmdExecApproval(CommandExecutionRequestApprovalParams),
868 FileChangeApproval(FileChangeRequestApprovalParams),
870 ToolRequestUserInput(crate::protocol::ToolRequestUserInputParams),
872 McpServerElicitationRequest(crate::protocol::McpServerElicitationRequestParams),
874 PermissionsRequestApproval(crate::protocol::PermissionsRequestApprovalParams),
876 ItemToolCall(crate::protocol::DynamicToolCallParams),
878 ChatgptAuthTokensRefresh(crate::protocol::ChatgptAuthTokensRefreshParams),
880 AttestationGenerate(crate::protocol::AttestationGenerateParams),
882 ApplyPatchApproval(crate::protocol::ApplyPatchApprovalParams),
884 ExecCommandApproval(crate::protocol::ExecCommandApprovalParams),
886 Unknown {
888 method: String,
889 params: Option<Value>,
890 },
891}
892
893impl ServerRequest {
894 pub fn method(&self) -> &str {
896 match self {
897 Self::CmdExecApproval(_) => methods::CMD_EXEC_APPROVAL,
898 Self::FileChangeApproval(_) => methods::FILE_CHANGE_APPROVAL,
899 Self::ToolRequestUserInput(_) => methods::TOOL_REQUEST_USER_INPUT,
900 Self::McpServerElicitationRequest(_) => methods::MCP_SERVER_ELICITATION_REQUEST,
901 Self::PermissionsRequestApproval(_) => methods::PERMISSIONS_REQUEST_APPROVAL,
902 Self::ItemToolCall(_) => methods::ITEM_TOOL_CALL,
903 Self::ChatgptAuthTokensRefresh(_) => methods::CHATGPT_AUTH_TOKENS_REFRESH,
904 Self::AttestationGenerate(_) => methods::ATTESTATION_GENERATE,
905 Self::ApplyPatchApproval(_) => methods::APPLY_PATCH_APPROVAL,
906 Self::ExecCommandApproval(_) => methods::EXEC_COMMAND_APPROVAL,
907 Self::Unknown { method, .. } => method,
908 }
909 }
910
911 pub fn is_unknown(&self) -> bool {
913 matches!(self, Self::Unknown { .. })
914 }
915
916 pub fn from_envelope(method: &str, params: Option<Value>) -> Result<Self, serde_json::Error> {
918 let params_value = params.clone().unwrap_or(Value::Null);
919 match method {
920 methods::CMD_EXEC_APPROVAL => {
921 serde_json::from_value(params_value).map(Self::CmdExecApproval)
922 }
923 methods::FILE_CHANGE_APPROVAL => {
924 serde_json::from_value(params_value).map(Self::FileChangeApproval)
925 }
926 methods::TOOL_REQUEST_USER_INPUT => {
927 serde_json::from_value(params_value).map(Self::ToolRequestUserInput)
928 }
929 methods::MCP_SERVER_ELICITATION_REQUEST => {
930 serde_json::from_value(params_value).map(Self::McpServerElicitationRequest)
931 }
932 methods::PERMISSIONS_REQUEST_APPROVAL => {
933 serde_json::from_value(params_value).map(Self::PermissionsRequestApproval)
934 }
935 methods::ITEM_TOOL_CALL => serde_json::from_value(params_value).map(Self::ItemToolCall),
936 methods::CHATGPT_AUTH_TOKENS_REFRESH => {
937 serde_json::from_value(params_value).map(Self::ChatgptAuthTokensRefresh)
938 }
939 methods::ATTESTATION_GENERATE => {
940 serde_json::from_value(params_value).map(Self::AttestationGenerate)
941 }
942 methods::APPLY_PATCH_APPROVAL => {
943 serde_json::from_value(params_value).map(Self::ApplyPatchApproval)
944 }
945 methods::EXEC_COMMAND_APPROVAL => {
946 serde_json::from_value(params_value).map(Self::ExecCommandApproval)
947 }
948 _ => Ok(Self::Unknown {
949 method: method.to_string(),
950 params,
951 }),
952 }
953 }
954}
955
956#[allow(clippy::large_enum_variant)]
965#[derive(Debug, Clone)]
966pub enum ServerMessage {
967 Notification(Notification),
969 Request {
971 id: RequestId,
972 request: ServerRequest,
973 },
974}
975
976impl ServerMessage {
977 pub fn is_unknown(&self) -> bool {
979 match self {
980 Self::Notification(n) => n.is_unknown(),
981 Self::Request { request, .. } => request.is_unknown(),
982 }
983 }
984
985 pub fn from_json_str(s: &str) -> Result<Self, Error> {
1000 let msg: JsonRpcMessage = serde_json::from_str(s)
1001 .map_err(|e| Error::Deserialization(ParseError::from_line(s, e)))?;
1002 Self::from_jsonrpc(msg)
1003 }
1004
1005 pub fn from_value(value: Value) -> Result<Self, Error> {
1009 let msg: JsonRpcMessage = serde_json::from_value(value.clone())
1010 .map_err(|e| Error::Deserialization(ParseError::from_line(value.to_string(), e)))?;
1011 Self::from_jsonrpc(msg)
1012 }
1013
1014 fn from_jsonrpc(msg: JsonRpcMessage) -> Result<Self, Error> {
1015 match msg {
1016 JsonRpcMessage::Notification(JsonRpcNotification { method, params }) => {
1017 Notification::from_envelope(&method, params.clone())
1018 .map(ServerMessage::Notification)
1019 .map_err(|e| Error::Deserialization(ParseError::from_envelope(method, params, e)))
1020 }
1021 JsonRpcMessage::Request(JsonRpcRequest { id, method, params }) => {
1022 ServerRequest::from_envelope(&method, params.clone())
1023 .map(|request| ServerMessage::Request { id, request })
1024 .map_err(|e| Error::Deserialization(ParseError::from_envelope(method, params, e)))
1025 }
1026 JsonRpcMessage::Response(resp) => Err(Error::Protocol(format!(
1027 "frame is a JSON-RPC response (id={}), not a server-initiated message",
1028 resp.id
1029 ))),
1030 JsonRpcMessage::Error(err) => Err(Error::Protocol(format!(
1031 "frame is a JSON-RPC error response (id={}, code={}), not a server-initiated message",
1032 err.id, err.error.code
1033 ))),
1034 }
1035 }
1036}
1037
1038#[cfg(test)]
1039mod tests {
1040 use super::*;
1041
1042 #[test]
1043 fn test_notification_unknown_method_routes_to_unknown_variant() {
1044 let n = Notification::from_envelope("foo/bar", Some(serde_json::json!({"x": 1})))
1045 .expect("unknown methods do not error");
1046 match n {
1047 Notification::Unknown { method, params } => {
1048 assert_eq!(method, "foo/bar");
1049 assert_eq!(params, Some(serde_json::json!({"x": 1})));
1050 }
1051 other => panic!("expected Unknown, got {:?}", other),
1052 }
1053 }
1054
1055 #[test]
1056 fn test_notification_known_method_with_bad_params_errors() {
1057 let err = Notification::from_envelope("thread/started", Some(serde_json::json!({})));
1059 assert!(err.is_err());
1060 }
1061
1062 #[test]
1063 fn test_notification_round_trip_envelope() {
1064 let wire = serde_json::json!({
1065 "method": "item/agentMessage/delta",
1066 "params": {"threadId": "t1", "turnId": "u1", "itemId": "i1", "delta": "hi"},
1067 });
1068 let n: Notification = serde_json::from_value(wire.clone()).unwrap();
1069 assert!(matches!(n, Notification::AgentMessageDelta(_)));
1070 let back = serde_json::to_value(&n).unwrap();
1071 assert_eq!(back, wire);
1072 }
1073
1074 #[test]
1075 fn test_model_provider_error_round_trips_from_jsonrpc_error_data() {
1076 use crate::protocol_generated::types::ModelProviderError;
1077
1078 let wire = serde_json::json!({
1079 "code": -32600,
1080 "message": "thread was created with a different model provider",
1081 "data": {
1082 "type": "modelProviderMismatch",
1083 "modelProvider": "old-provider",
1084 "currentModelProvider": "current-provider",
1085 },
1086 });
1087 let err: crate::JsonRpcErrorData = serde_json::from_value(wire.clone()).unwrap();
1088 let data: ModelProviderError = serde_json::from_value(err.data.unwrap()).unwrap();
1089 assert_eq!(
1090 data,
1091 ModelProviderError::ModelProviderMismatch {
1092 model_provider: "old-provider".to_string(),
1093 current_model_provider: "current-provider".to_string(),
1094 }
1095 );
1096 assert_eq!(serde_json::to_value(&data).unwrap(), wire["data"]);
1097
1098 for (wire, expected) in [
1099 (
1100 serde_json::json!({
1101 "type": "modelProviderNotConfigured",
1102 "modelProvider": "old-provider",
1103 }),
1104 ModelProviderError::ModelProviderNotConfigured {
1105 model_provider: "old-provider".to_string(),
1106 },
1107 ),
1108 (
1109 serde_json::json!({
1110 "type": "modelProviderPolicyConflict",
1111 "modelProvider": "old-provider",
1112 "requiredModelProvider": "required-provider",
1113 }),
1114 ModelProviderError::ModelProviderPolicyConflict {
1115 model_provider: "old-provider".to_string(),
1116 required_model_provider: "required-provider".to_string(),
1117 },
1118 ),
1119 ] {
1120 assert_eq!(
1121 serde_json::from_value::<ModelProviderError>(wire.clone()).unwrap(),
1122 expected
1123 );
1124 assert_eq!(serde_json::to_value(&expected).unwrap(), wire);
1125 }
1126 }
1127
1128 #[test]
1129 fn test_model_provider_list_response_round_trips() {
1130 use crate::protocol_generated::types::{
1131 GatewayAuth, ModelProviderListResponse, ProviderSettings,
1132 };
1133
1134 let wire = serde_json::json!({
1135 "data": [
1136 {"id": "openai", "name": "OpenAI", "canEdit": false,
1137 "baseUrl": null, "settings": {"type": "openai"}},
1138 {"id": "gw", "name": "Gateway", "canEdit": true,
1139 "baseUrl": "https://gw.example",
1140 "settings": {"type": "customGateway",
1141 "auth": {"mode": "environmentVariable", "envKey": "GW_KEY"}}},
1142 {"id": "ext", "name": "External", "canEdit": true, "baseUrl": null,
1143 "settings": {"type": "customGateway",
1144 "auth": {"mode": "externallyConfigured", "envKey": null}}},
1145 {"id": "br", "name": "Bedrock", "canEdit": true, "baseUrl": null,
1146 "settings": {"type": "bedrock", "profile": "dev", "region": null}},
1147 {"id": "vx", "name": "Vertex", "canEdit": false, "baseUrl": null,
1148 "settings": {"type": "gcp", "projectId": "p", "location": null,
1149 "credentialFile": "/creds.json"}},
1150 ],
1151 "defaultProviderId": "openai",
1152 "canChangeDefault": true,
1153 });
1154 let resp: ModelProviderListResponse = serde_json::from_value(wire.clone()).unwrap();
1155 assert_eq!(resp.default_provider_id, "openai");
1156 assert!(resp.can_change_default);
1157 assert_eq!(resp.data[0].settings, ProviderSettings::OpenAi);
1158 assert_eq!(
1159 resp.data[1].settings,
1160 ProviderSettings::CustomGateway {
1161 auth: GatewayAuth::EnvironmentVariable {
1162 env_key: "GW_KEY".to_string()
1163 }
1164 }
1165 );
1166 assert_eq!(
1167 resp.data[4].settings,
1168 ProviderSettings::Gcp {
1169 project_id: Some("p".to_string()),
1170 location: None,
1171 credential_file: Some("/creds.json".to_string()),
1172 }
1173 );
1174 assert_eq!(serde_json::to_value(&resp).unwrap(), wire);
1175
1176 let none: GatewayAuth =
1177 serde_json::from_value(serde_json::json!({"mode": "none"})).unwrap();
1178 assert_eq!(none, GatewayAuth::None);
1179 }
1180
1181 #[test]
1182 fn test_model_provider_config_write_patch_round_trips() {
1183 use crate::protocol_generated::types::{
1184 BedrockProviderId, GcpProviderId, ModelProviderConfigWriteParams, ProviderSettingsPatch,
1185 };
1186
1187 let wire = serde_json::json!({
1189 "settings": {"type": "bedrock", "providerId": "amazon-bedrock-runtime",
1190 "region": null}
1191 });
1192 let params: ModelProviderConfigWriteParams = serde_json::from_value(wire.clone()).unwrap();
1193 assert_eq!(
1194 params.settings,
1195 ProviderSettingsPatch::Bedrock {
1196 provider_id: BedrockProviderId::AmazonBedrockRuntime,
1197 profile: None,
1198 region: Some(None),
1199 }
1200 );
1201 assert_eq!(serde_json::to_value(¶ms).unwrap(), wire);
1202
1203 let gcp = ProviderSettingsPatch::Gcp {
1204 provider_id: GcpProviderId::Gcp,
1205 project_id: Some(Some("p".to_string())),
1206 location: None,
1207 credential_file: Some(None),
1208 };
1209 assert_eq!(
1210 serde_json::to_value(&gcp).unwrap(),
1211 serde_json::json!({"type": "gcp", "providerId": "gcp",
1212 "projectId": "p", "credentialFile": null})
1213 );
1214
1215 let gateway: ProviderSettingsPatch = serde_json::from_value(serde_json::json!({
1216 "type": "customGateway", "name": "GW", "baseUrl": "https://gw.example",
1217 "envKey": null
1218 }))
1219 .unwrap();
1220 assert_eq!(
1221 gateway,
1222 ProviderSettingsPatch::CustomGateway {
1223 provider_id: None,
1224 name: Some("GW".to_string()),
1225 base_url: Some(Some("https://gw.example".to_string())),
1226 env_key: Some(None),
1227 }
1228 );
1229
1230 assert!(serde_json::from_value::<ProviderSettingsPatch>(
1232 serde_json::json!({"type": "customGateway", "name": null})
1233 )
1234 .is_err());
1235 }
1236
1237 #[test]
1238 fn test_model_provider_default_set_round_trips() {
1239 use crate::protocol_generated::types::ModelProviderDefaultSetParams;
1240
1241 let wire = serde_json::json!({"providerId": "amazon-bedrock"});
1242 let params: ModelProviderDefaultSetParams = serde_json::from_value(wire.clone()).unwrap();
1243 assert_eq!(params.provider_id, "amazon-bedrock");
1244 assert_eq!(serde_json::to_value(¶ms).unwrap(), wire);
1245 assert!(
1246 serde_json::from_value::<ModelProviderDefaultSetParams>(serde_json::json!({})).is_err()
1247 );
1248 }
1249
1250 #[test]
1251 fn test_thread_list_included_thread_ids_round_trips() {
1252 use crate::protocol_generated::types::ThreadListParams;
1253
1254 let params = ThreadListParams {
1255 included_thread_ids: Some(vec!["t1".to_string()]),
1256 ..Default::default()
1257 };
1258 assert_eq!(
1259 serde_json::to_value(¶ms).unwrap(),
1260 serde_json::json!({"includedThreadIds": ["t1"]})
1261 );
1262 assert_eq!(
1263 serde_json::to_value(ThreadListParams::default()).unwrap(),
1264 serde_json::json!({})
1265 );
1266 }
1267
1268 #[test]
1269 fn test_thread_realtime_persisted_items_round_trip() {
1270 use crate::protocol_generated::types::{
1271 ThreadRealtimeCoverage, ThreadRealtimeEntry, ThreadRealtimeHandoffMode,
1272 ThreadRealtimeItem, ThreadRealtimeItemDetail, ThreadRealtimeOrder,
1273 ThreadRealtimeTranscriptRole,
1274 };
1275
1276 let frame = serde_json::json!({
1277 "id": "f1", "realtimeSessionId": "voice", "observedAtMs": 1_700_000_000_000_i64,
1278 "type": "frame",
1279 "channels": {"voice_transcript": {"role": "user", "text": "hi"}}
1280 });
1281 let item: ThreadRealtimeItem = serde_json::from_value(frame.clone()).unwrap();
1282 assert_eq!(item.observed_at_ms, Some(1_700_000_000_000));
1283 match &item.detail {
1284 ThreadRealtimeItemDetail::Frame { channels } => {
1285 assert_eq!(
1286 channels.voice_transcript.role,
1287 ThreadRealtimeTranscriptRole::User
1288 );
1289 assert_eq!(channels.voice_transcript.text, "hi");
1290 }
1291 other => panic!("expected frame, got {other:?}"),
1292 }
1293 assert_eq!(serde_json::to_value(&item).unwrap(), frame);
1294
1295 let live = serde_json::json!({
1297 "id": "speech", "realtimeSessionId": "voice", "type": "transcriptSegment",
1298 "role": "assistant", "text": "hello"
1299 });
1300 let item: ThreadRealtimeItem = serde_json::from_value(live.clone()).unwrap();
1301 assert_eq!(item.observed_at_ms, None);
1302 assert_eq!(serde_json::to_value(&item).unwrap(), live);
1303
1304 let accepted = serde_json::json!({
1305 "id": "a1", "realtimeSessionId": "voice", "type": "turnInputAccepted",
1306 "handoff_id": "h1", "turn_id": "turn1", "mode": "steer",
1307 "source_event_ids": ["e1", "e2"], "input_item_id": "in1", "input": "do it"
1308 });
1309 let entry: ThreadRealtimeEntry = serde_json::from_value(serde_json::json!({
1310 "position": 7, "item": accepted.clone()
1311 }))
1312 .unwrap();
1313 assert_eq!(entry.position, 7);
1314 match &entry.item.detail {
1315 ThreadRealtimeItemDetail::TurnInputAccepted {
1316 mode,
1317 source_event_ids,
1318 ..
1319 } => {
1320 assert_eq!(*mode, ThreadRealtimeHandoffMode::Steer);
1321 assert_eq!(source_event_ids.len(), 2);
1322 }
1323 other => panic!("expected turnInputAccepted, got {other:?}"),
1324 }
1325 assert_eq!(serde_json::to_value(&entry.item).unwrap(), accepted);
1326
1327 let saved = serde_json::json!({
1328 "id": "s1", "realtimeSessionId": "voice", "type": "sessionEndInputSaved",
1329 "source_event_ids": [], "input_item_id": "in2", "input": "later"
1330 });
1331 let item: ThreadRealtimeItem = serde_json::from_value(saved.clone()).unwrap();
1332 assert!(matches!(
1333 item.detail,
1334 ThreadRealtimeItemDetail::SessionEndInputSaved { .. }
1335 ));
1336 assert_eq!(serde_json::to_value(&item).unwrap(), saved);
1337
1338 assert_eq!(
1339 ThreadRealtimeOrder::default(),
1340 ThreadRealtimeOrder::ObservedAt
1341 );
1342 assert_eq!(
1343 serde_json::to_value(ThreadRealtimeOrder::ObservedAt).unwrap(),
1344 "observedAt"
1345 );
1346 assert_eq!(
1347 serde_json::from_value::<ThreadRealtimeCoverage>(serde_json::json!("pending")).unwrap(),
1348 ThreadRealtimeCoverage::Pending
1349 );
1350 }
1351
1352 #[test]
1353 fn test_human_command_approval_request_decodes() {
1354 use crate::protocol_generated::types::CommandExecutionApprovalKind;
1355
1356 let msg = ServerMessage::from_value(serde_json::json!({
1357 "jsonrpc": "2.0",
1358 "id": 7,
1359 "method": "item/commandExecution/requestApproval",
1360 "params": {
1361 "threadId": "t1",
1362 "turnId": "u1",
1363 "itemId": "i1",
1364 "startedAtMs": 0,
1365 "kind": "humanCommand",
1366 },
1367 }))
1368 .unwrap();
1369 match msg {
1370 ServerMessage::Request {
1371 request: ServerRequest::CmdExecApproval(params),
1372 ..
1373 } => assert_eq!(
1374 params.kind,
1375 Some(CommandExecutionApprovalKind::HumanCommand)
1376 ),
1377 other => panic!("expected CmdExecApproval, got {:?}", other),
1378 }
1379 }
1380
1381 #[test]
1382 fn test_codex_error_info_other_accepts_unknown_codes() {
1383 use crate::protocol_generated::types::CodexErrorInfo;
1384
1385 assert_eq!(
1387 serde_json::from_value::<CodexErrorInfo>(serde_json::json!("contextWindowExceeded"))
1388 .unwrap(),
1389 CodexErrorInfo::ContextWindowExceeded
1390 );
1391 assert_eq!(
1392 serde_json::from_value::<CodexErrorInfo>(serde_json::json!("modelNotFound")).unwrap(),
1393 CodexErrorInfo::ModelNotFound
1394 );
1395 assert_eq!(
1396 serde_json::from_value::<CodexErrorInfo>(serde_json::json!(
1397 "policyCapacityUnavailable"
1398 ))
1399 .unwrap(),
1400 CodexErrorInfo::PolicyCapacityUnavailable
1401 );
1402 assert_eq!(
1403 serde_json::from_value::<CodexErrorInfo>(serde_json::json!("inferenceHookDenied"))
1404 .unwrap(),
1405 CodexErrorInfo::InferenceHookDenied
1406 );
1407 assert_eq!(
1408 serde_json::from_value::<CodexErrorInfo>(
1409 serde_json::json!({"httpConnectionFailed": {"httpStatusCode": 502}})
1410 )
1411 .unwrap(),
1412 CodexErrorInfo::HttpConnectionFailed {
1413 http_status_code: Some(502)
1414 }
1415 );
1416
1417 for unknown in [
1419 serde_json::json!("other"),
1420 serde_json::json!("bioPolicy"),
1421 serde_json::json!({"someFutureVariant": {"detail": 1}}),
1422 ] {
1423 assert_eq!(
1424 serde_json::from_value::<CodexErrorInfo>(unknown).unwrap(),
1425 CodexErrorInfo::Other
1426 );
1427 }
1428 assert!(serde_json::from_value::<CodexErrorInfo>(serde_json::json!(42)).is_err());
1429 assert!(serde_json::from_value::<CodexErrorInfo>(serde_json::Value::Null).is_err());
1430
1431 assert_eq!(
1433 serde_json::to_value(CodexErrorInfo::Other).unwrap(),
1434 serde_json::json!("other")
1435 );
1436 }
1437
1438 #[test]
1439 fn test_thread_goal_params_round_trip_origin() {
1440 use crate::protocol_generated::types::{
1441 ThreadGoalClearParams, ThreadGoalMutationOrigin, ThreadGoalSetParams,
1442 };
1443
1444 let set = ThreadGoalSetParams {
1445 thread_id: "t1".into(),
1446 objective: Some("ship it".into()),
1447 origin: Some(ThreadGoalMutationOrigin::User),
1448 ..Default::default()
1449 };
1450 assert_eq!(
1451 serde_json::to_value(&set).unwrap(),
1452 serde_json::json!({"threadId": "t1", "objective": "ship it", "origin": "user"})
1453 );
1454
1455 let clear: ThreadGoalClearParams =
1456 serde_json::from_value(serde_json::json!({"threadId": "t1", "origin": "automatic"}))
1457 .unwrap();
1458 assert_eq!(clear.origin, Some(ThreadGoalMutationOrigin::Automatic));
1459
1460 let bare: ThreadGoalClearParams =
1462 serde_json::from_value(serde_json::json!({"threadId": "t1"})).unwrap();
1463 assert_eq!(bare.origin, None);
1464 assert_eq!(
1465 serde_json::to_value(&bare).unwrap(),
1466 serde_json::json!({"threadId": "t1"})
1467 );
1468 }
1469
1470 #[test]
1471 fn test_thread_prediction_updated_round_trips_both_results() {
1472 use crate::protocol_generated::types::ThreadPredictionResult;
1473
1474 for (result, expected) in [
1475 (
1476 serde_json::json!({"type": "completed", "text": "next prompt"}),
1477 ThreadPredictionResult::Completed {
1478 text: Some("next prompt".into()),
1479 },
1480 ),
1481 (
1482 serde_json::json!({"type": "completed"}),
1483 ThreadPredictionResult::Completed { text: None },
1484 ),
1485 (
1486 serde_json::json!({"type": "failed"}),
1487 ThreadPredictionResult::Failed,
1488 ),
1489 ] {
1490 let wire = serde_json::json!({
1491 "method": "thread/prediction/updated",
1492 "params": {"result": result, "sourceTurnId": "u1", "threadId": "t1"},
1493 });
1494 let n: Notification = serde_json::from_value(wire.clone()).unwrap();
1495 let Notification::ThreadPredictionUpdated(p) = &n else {
1496 panic!("expected ThreadPredictionUpdated, got {:?}", n);
1497 };
1498 assert_eq!(p.result, expected);
1499 assert_eq!(p.source_turn_id, "u1");
1500 assert_eq!(serde_json::to_value(&n).unwrap(), wire);
1501 }
1502 }
1503
1504 #[test]
1505 fn test_turn_id_from_turn_started() {
1506 let wire = serde_json::json!({
1508 "method": "turn/started",
1509 "params": {"threadId": "t1", "turn": {"id": "turn_42", "status": "inProgress"}},
1510 });
1511 let n: Notification = serde_json::from_value(wire).unwrap();
1512 assert!(matches!(n, Notification::TurnStarted(_)));
1513 assert_eq!(n.turn_id(), Some("turn_42"));
1514 }
1515
1516 #[test]
1517 fn test_turn_id_from_delta_field() {
1518 let wire = serde_json::json!({
1520 "method": "item/agentMessage/delta",
1521 "params": {"threadId": "t1", "turnId": "turn_7", "itemId": "i1", "delta": "hi"},
1522 });
1523 let n: Notification = serde_json::from_value(wire).unwrap();
1524 assert_eq!(n.turn_id(), Some("turn_7"));
1525 }
1526
1527 #[test]
1528 fn test_turn_id_none_for_non_turn_notification() {
1529 let n = Notification::from_envelope(
1530 "thread/deleted",
1531 Some(serde_json::json!({"threadId": "t1"})),
1532 )
1533 .unwrap();
1534 assert_eq!(n.turn_id(), None);
1535 }
1536
1537 #[test]
1538 fn test_thread_item_accessor() {
1539 let wire = serde_json::json!({
1540 "method": "item/started",
1541 "params": {
1542 "threadId": "t1",
1543 "turnId": "turn_1",
1544 "startedAtMs": 0,
1545 "item": {"type": "agentMessage", "id": "i1", "text": "hi"},
1546 },
1547 });
1548 let n: Notification = serde_json::from_value(wire).unwrap();
1549 assert!(n.thread_item().is_some());
1550 let other = Notification::from_envelope(
1552 "thread/deleted",
1553 Some(serde_json::json!({"threadId": "t1"})),
1554 )
1555 .unwrap();
1556 assert!(other.thread_item().is_none());
1557 }
1558
1559 #[test]
1560 fn test_server_message_from_json_str_notification() {
1561 let line = r#"{"method":"turn/started","params":{"threadId":"t1","turn":{"id":"turn_9","status":"inProgress"}}}"#;
1562 let msg = ServerMessage::from_json_str(line).expect("parses a notification frame");
1563 match msg {
1564 ServerMessage::Notification(n) => assert_eq!(n.turn_id(), Some("turn_9")),
1565 other => panic!("expected Notification, got {other:?}"),
1566 }
1567 }
1568
1569 #[test]
1570 fn test_server_message_from_value_request() {
1571 let frame = serde_json::json!({
1572 "id": 7,
1573 "method": "execCommandApproval",
1574 "params": {
1575 "callId": "c1",
1576 "command": ["ls"],
1577 "conversationId": "th_1",
1578 "cwd": "/tmp",
1579 "parsedCmd": [],
1580 },
1581 });
1582 let msg = ServerMessage::from_value(frame).expect("parses a request frame");
1583 match msg {
1584 ServerMessage::Request { id, request } => {
1585 assert_eq!(id, RequestId::Integer(7));
1586 assert!(matches!(request, ServerRequest::ExecCommandApproval(_)));
1587 }
1588 other => panic!("expected Request, got {other:?}"),
1589 }
1590 }
1591
1592 #[test]
1593 fn test_server_message_from_json_str_rejects_response() {
1594 let line = r#"{"id":1,"result":{"threadId":"th_abc"}}"#;
1596 let err = ServerMessage::from_json_str(line).unwrap_err();
1597 assert!(matches!(err, Error::Protocol(_)), "got: {err:?}");
1598 }
1599
1600 #[test]
1601 fn test_server_message_from_json_str_unknown_method_ok() {
1602 let line = r#"{"method":"some/future/notification","params":{"x":1}}"#;
1603 let msg = ServerMessage::from_json_str(line).unwrap();
1604 assert!(msg.is_unknown());
1605 }
1606}