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