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