Skip to main content

vv_agent/app_server/
processor.rs

1mod helpers;
2mod resume;
3mod turns;
4
5use helpers::{load_thread_resume_snapshot, parse_params, parse_params_or_default, store_error};
6
7use std::collections::{HashMap, HashSet};
8use std::sync::Arc;
9use std::time::Duration;
10
11use serde_json::{json, Value};
12
13use crate::app_server::durable_resume::DurableTurnResumeProvider;
14use crate::app_server::host::{AppServerHost, DefaultAppServerHost};
15use crate::app_server::outgoing::{OutgoingEnvelope, OutgoingMessageSender};
16use crate::app_server::protocol::{
17    generate_app_server_json_schema_bundle, generate_app_server_typescript_bundle, AppClientInfo,
18    AppServerCapabilities, AppServerError, AppServerErrorCode, ApprovalResolveParams,
19    InitializeParams, InitializeResponse, JsonRpcMessage, JsonRpcNotification, JsonRpcRequest,
20    JsonRpcResponse, ModelListParams, SchemaExportResponse, ServerNotification,
21    ThreadArchiveParams, ThreadArchiveResponse, ThreadClosedParams, ThreadListParams,
22    ThreadListResponse, ThreadReadParams, ThreadReadResponse, ThreadResumeParams,
23    ThreadResumeResponse, ThreadStartParams, ThreadStartResponse, ThreadStatus,
24    ThreadStatusChangedParams, ThreadUnsubscribeParams, ThreadUnsubscribeResponse,
25};
26use crate::app_server::request_serialization::{
27    RequestSerializationQueue, RequestSerializationScope,
28};
29use crate::app_server::run_adapter::AppServerRunAdapter;
30use crate::app_server::thread_state::ThreadStateManager;
31use crate::app_server::thread_store::{SqliteThreadStore, ThreadStoreError};
32use crate::app_server::transport::ConnectionId;
33use crate::{Agent, Runner};
34
35pub struct MessageProcessor {
36    outgoing: OutgoingMessageSender,
37    host: Arc<dyn AppServerHost>,
38    connections: HashMap<ConnectionId, ConnectionSessionState>,
39    run_adapter: Option<AppServerRunAdapter>,
40    thread_state: ThreadStateManager,
41    request_queue: RequestSerializationQueue,
42}
43#[derive(Debug, Clone, Default)]
44pub struct ConnectionSessionState {
45    initialized: bool,
46    ready_for_notifications: bool,
47    client_info: Option<AppClientInfo>,
48    experimental_api: bool,
49    opt_out_notification_methods: HashSet<String>,
50}
51
52impl ConnectionSessionState {
53    pub fn initialized(&self) -> bool {
54        self.initialized
55    }
56
57    pub fn ready_for_notifications(&self) -> bool {
58        self.ready_for_notifications
59    }
60
61    pub fn client_info(&self) -> Option<&AppClientInfo> {
62        self.client_info.as_ref()
63    }
64
65    pub fn experimental_api(&self) -> bool {
66        self.experimental_api
67    }
68
69    pub fn opt_out_notification_methods(&self) -> &HashSet<String> {
70        &self.opt_out_notification_methods
71    }
72}
73
74impl MessageProcessor {
75    pub fn new(outgoing_capacity: usize) -> (Self, tokio::sync::mpsc::Receiver<OutgoingEnvelope>) {
76        Self::new_with_host(outgoing_capacity, Arc::new(DefaultAppServerHost::default()))
77    }
78
79    pub fn new_with_host(
80        outgoing_capacity: usize,
81        host: Arc<dyn AppServerHost>,
82    ) -> (Self, tokio::sync::mpsc::Receiver<OutgoingEnvelope>) {
83        let (outgoing, rx) = OutgoingMessageSender::channel(outgoing_capacity);
84        (
85            Self {
86                outgoing,
87                host,
88                connections: HashMap::new(),
89                run_adapter: None,
90                thread_state: ThreadStateManager::default(),
91                request_queue: RequestSerializationQueue::default(),
92            },
93            rx,
94        )
95    }
96
97    pub fn with_runtime(
98        outgoing_capacity: usize,
99        runner: Runner,
100        agent: Agent,
101        store: SqliteThreadStore,
102    ) -> (Self, tokio::sync::mpsc::Receiver<OutgoingEnvelope>) {
103        Self::with_runtime_and_approval_timeout(
104            outgoing_capacity,
105            runner,
106            agent,
107            store,
108            Duration::from_secs(30),
109        )
110    }
111
112    pub fn with_runtime_and_approval_timeout(
113        outgoing_capacity: usize,
114        runner: Runner,
115        agent: Agent,
116        store: SqliteThreadStore,
117        approval_request_timeout: Duration,
118    ) -> (Self, tokio::sync::mpsc::Receiver<OutgoingEnvelope>) {
119        Self::with_host_and_approval_timeout(
120            outgoing_capacity,
121            runner,
122            Arc::new(DefaultAppServerHost::from_agent(agent)),
123            store,
124            approval_request_timeout,
125        )
126    }
127
128    pub fn with_host(
129        outgoing_capacity: usize,
130        runner: Runner,
131        host: Arc<dyn AppServerHost>,
132        store: SqliteThreadStore,
133    ) -> (Self, tokio::sync::mpsc::Receiver<OutgoingEnvelope>) {
134        Self::with_host_and_approval_timeout(
135            outgoing_capacity,
136            runner,
137            host,
138            store,
139            Duration::from_secs(30),
140        )
141    }
142
143    pub fn with_host_and_approval_timeout(
144        outgoing_capacity: usize,
145        runner: Runner,
146        host: Arc<dyn AppServerHost>,
147        store: SqliteThreadStore,
148        approval_request_timeout: Duration,
149    ) -> (Self, tokio::sync::mpsc::Receiver<OutgoingEnvelope>) {
150        Self::with_host_options(
151            outgoing_capacity,
152            runner,
153            host,
154            store,
155            approval_request_timeout,
156            None,
157        )
158    }
159
160    pub fn with_host_and_durable_resume_provider(
161        outgoing_capacity: usize,
162        runner: Runner,
163        host: Arc<dyn AppServerHost>,
164        store: SqliteThreadStore,
165        provider: Arc<dyn DurableTurnResumeProvider>,
166    ) -> (Self, tokio::sync::mpsc::Receiver<OutgoingEnvelope>) {
167        Self::with_host_options(
168            outgoing_capacity,
169            runner,
170            host,
171            store,
172            Duration::from_secs(30),
173            Some(provider),
174        )
175    }
176
177    pub fn with_runtime_and_durable_resume_provider(
178        outgoing_capacity: usize,
179        runner: Runner,
180        agent: Agent,
181        store: SqliteThreadStore,
182        provider: Arc<dyn DurableTurnResumeProvider>,
183    ) -> (Self, tokio::sync::mpsc::Receiver<OutgoingEnvelope>) {
184        Self::with_host_and_durable_resume_provider(
185            outgoing_capacity,
186            runner,
187            Arc::new(DefaultAppServerHost::from_agent(agent)),
188            store,
189            provider,
190        )
191    }
192
193    fn with_host_options(
194        outgoing_capacity: usize,
195        runner: Runner,
196        host: Arc<dyn AppServerHost>,
197        store: SqliteThreadStore,
198        approval_request_timeout: Duration,
199        durable_resume_provider: Option<Arc<dyn DurableTurnResumeProvider>>,
200    ) -> (Self, tokio::sync::mpsc::Receiver<OutgoingEnvelope>) {
201        let (outgoing, rx) = OutgoingMessageSender::channel(outgoing_capacity);
202        let thread_state = ThreadStateManager::default();
203        let mut run_adapter = AppServerRunAdapter::with_host(
204            runner,
205            host.clone(),
206            store,
207            thread_state.clone(),
208            outgoing.clone(),
209        )
210        .with_approval_request_timeout(approval_request_timeout);
211        if let Some(provider) = durable_resume_provider {
212            run_adapter = run_adapter.with_durable_resume_provider(provider);
213        }
214        (
215            Self {
216                outgoing,
217                host,
218                connections: HashMap::new(),
219                run_adapter: Some(run_adapter),
220                thread_state,
221                request_queue: RequestSerializationQueue::default(),
222            },
223            rx,
224        )
225    }
226
227    pub fn new_for_tests(
228        outgoing_capacity: usize,
229    ) -> (Self, tokio::sync::mpsc::Receiver<OutgoingEnvelope>) {
230        Self::new(outgoing_capacity)
231    }
232
233    pub fn new_for_tests_with_runtime(
234        outgoing_capacity: usize,
235        runner: Runner,
236        agent: Agent,
237        store: SqliteThreadStore,
238    ) -> (Self, tokio::sync::mpsc::Receiver<OutgoingEnvelope>) {
239        Self::with_runtime(outgoing_capacity, runner, agent, store)
240    }
241
242    pub fn new_for_tests_with_runtime_and_approval_timeout(
243        outgoing_capacity: usize,
244        runner: Runner,
245        agent: Agent,
246        store: SqliteThreadStore,
247        approval_request_timeout: Duration,
248    ) -> (Self, tokio::sync::mpsc::Receiver<OutgoingEnvelope>) {
249        Self::with_runtime_and_approval_timeout(
250            outgoing_capacity,
251            runner,
252            agent,
253            store,
254            approval_request_timeout,
255        )
256    }
257
258    pub fn new_for_tests_with_runtime_and_durable_resume_provider(
259        outgoing_capacity: usize,
260        runner: Runner,
261        agent: Agent,
262        store: SqliteThreadStore,
263        provider: Arc<dyn DurableTurnResumeProvider>,
264    ) -> (Self, tokio::sync::mpsc::Receiver<OutgoingEnvelope>) {
265        Self::with_runtime_and_durable_resume_provider(
266            outgoing_capacity,
267            runner,
268            agent,
269            store,
270            provider,
271        )
272    }
273
274    pub fn outgoing(&self) -> &OutgoingMessageSender {
275        &self.outgoing
276    }
277
278    pub fn connection_state(&self, connection_id: ConnectionId) -> Option<&ConnectionSessionState> {
279        self.connections.get(&connection_id)
280    }
281
282    pub fn connection_ids(&self) -> Vec<ConnectionId> {
283        self.connections.keys().copied().collect()
284    }
285
286    pub async fn disconnect_connection(&mut self, connection_id: ConnectionId) {
287        self.connections.remove(&connection_id);
288        self.thread_state
289            .unsubscribe_connection(connection_id)
290            .await;
291        self.outgoing.unregister_connection(connection_id).await;
292    }
293
294    pub async fn process_message(&mut self, connection_id: ConnectionId, message: JsonRpcMessage) {
295        self.outgoing.register_connection(connection_id).await;
296        match message {
297            JsonRpcMessage::Request(request) => {
298                self.process_request(connection_id, request).await;
299            }
300            JsonRpcMessage::Notification(notification) => {
301                self.process_notification(connection_id, notification).await;
302            }
303            JsonRpcMessage::Response(response) => {
304                let _ = self
305                    .outgoing
306                    .resolve_server_response(connection_id, response)
307                    .await;
308            }
309            JsonRpcMessage::Error(error) => {
310                let _ = self
311                    .outgoing
312                    .resolve_server_error(connection_id, error)
313                    .await;
314            }
315        }
316    }
317
318    async fn process_request(&mut self, connection_id: ConnectionId, request: JsonRpcRequest) {
319        if request.method == "initialize" {
320            self.process_initialize(connection_id, request).await;
321            return;
322        }
323
324        if !self
325            .connections
326            .get(&connection_id)
327            .is_some_and(ConnectionSessionState::initialized)
328        {
329            let _ = self
330                .outgoing
331                .send_error(connection_id, request.id, AppServerError::not_initialized())
332                .await;
333            return;
334        }
335
336        if let Some(scope) =
337            RequestSerializationScope::for_method(&request.method, request.params.as_ref())
338        {
339            let queue = self.request_queue.clone();
340            queue
341                .run(scope, async move {
342                    self.process_initialized_request(connection_id, request)
343                        .await;
344                })
345                .await;
346            return;
347        }
348
349        self.process_initialized_request(connection_id, request)
350            .await;
351    }
352
353    async fn process_initialized_request(
354        &mut self,
355        connection_id: ConnectionId,
356        request: JsonRpcRequest,
357    ) {
358        match request.method.as_str() {
359            "thread/start" => self.process_thread_start(connection_id, request).await,
360            "thread/resume" => self.process_thread_resume(connection_id, request).await,
361            "thread/read" => self.process_thread_read(connection_id, request).await,
362            "thread/list" => self.process_thread_list(connection_id, request).await,
363            "thread/archive" => self.process_thread_archive(connection_id, request).await,
364            "thread/unsubscribe" => {
365                self.process_thread_unsubscribe(connection_id, request)
366                    .await
367            }
368            "turn/start" => self.process_turn_start(connection_id, request).await,
369            "turn/resume" => self.process_turn_resume(connection_id, request).await,
370            "turn/interrupt" => self.process_turn_interrupt(connection_id, request).await,
371            "turn/steer" => self.process_turn_steer(connection_id, request).await,
372            "turn/followUp" => self.process_turn_follow_up(connection_id, request).await,
373            "approval/resolve" => self.process_approval_resolve(connection_id, request).await,
374            "model/list" => self.process_model_list(connection_id, request).await,
375            "schema/export" => self.process_schema_export(connection_id, request).await,
376            _ => {
377                let _ = self
378                    .outgoing
379                    .send_error(
380                        connection_id,
381                        request.id,
382                        AppServerError::new(
383                            AppServerErrorCode::MethodNotFound,
384                            format!("Method not found: {}", request.method),
385                        )
386                        .with_data(serde_json::json!({ "method": request.method })),
387                    )
388                    .await;
389            }
390        }
391    }
392
393    async fn process_schema_export(
394        &mut self,
395        connection_id: ConnectionId,
396        request: JsonRpcRequest,
397    ) {
398        if request
399            .params
400            .as_ref()
401            .is_some_and(|params| !params.as_object().is_some_and(serde_json::Map::is_empty))
402        {
403            let _ = self
404                .outgoing
405                .send_error(
406                    connection_id,
407                    request.id,
408                    AppServerError::invalid_params("params must be an empty object"),
409                )
410                .await;
411            return;
412        }
413        let json_schema = match generate_app_server_json_schema_bundle() {
414            Ok(bundle) => bundle,
415            Err(error) => {
416                let _ = self
417                    .outgoing
418                    .send_error(
419                        connection_id,
420                        request.id,
421                        AppServerError::internal(error.to_string()),
422                    )
423                    .await;
424                return;
425            }
426        };
427        let typescript = match generate_app_server_typescript_bundle() {
428            Ok(bundle) => bundle,
429            Err(error) => {
430                let _ = self
431                    .outgoing
432                    .send_error(
433                        connection_id,
434                        request.id,
435                        AppServerError::internal(error.to_string()),
436                    )
437                    .await;
438                return;
439            }
440        };
441        let result = serde_json::to_value(SchemaExportResponse {
442            json_schema,
443            typescript,
444        })
445        .expect("schema export response serializes");
446        let _ = self
447            .outgoing
448            .send_response(connection_id, request.id, result)
449            .await;
450    }
451
452    async fn process_model_list(&mut self, connection_id: ConnectionId, request: JsonRpcRequest) {
453        let params = match parse_params_or_default::<ModelListParams>(request.params) {
454            Ok(params) => params,
455            Err(error) => {
456                let _ = self
457                    .outgoing
458                    .send_error(connection_id, request.id, error)
459                    .await;
460                return;
461            }
462        };
463        let models = match self.host.list_models(&params) {
464            Ok(models) => models,
465            Err(error) => {
466                let _ = self
467                    .outgoing
468                    .send_error(
469                        connection_id,
470                        request.id,
471                        AppServerError::internal(error.to_string()),
472                    )
473                    .await;
474                return;
475            }
476        };
477        let result = serde_json::to_value(models).expect("model list response serializes");
478        let _ = self
479            .outgoing
480            .send_response(connection_id, request.id, result)
481            .await;
482    }
483
484    async fn process_approval_resolve(
485        &mut self,
486        connection_id: ConnectionId,
487        request: JsonRpcRequest,
488    ) {
489        let params = match parse_params::<ApprovalResolveParams>(request.params) {
490            Ok(params) => params,
491            Err(error) => {
492                let _ = self
493                    .outgoing
494                    .send_error(connection_id, request.id, error)
495                    .await;
496                return;
497            }
498        };
499        let decision = params.decision.as_wire();
500        let resolved = self
501            .outgoing
502            .resolve_server_response_bound(
503                connection_id,
504                Some("approval/request"),
505                Some(&params.thread_id),
506                Some(&params.turn_id),
507                JsonRpcResponse {
508                    id: crate::app_server::protocol::RequestId::String(params.request_id.clone()),
509                    result: json!({
510                        "decision": decision,
511                        "reason": params.reason,
512                        "metadata": params.metadata,
513                    }),
514                },
515            )
516            .await;
517        if !resolved {
518            let _ = self
519                .outgoing
520                .send_error(
521                    connection_id,
522                    request.id,
523                    AppServerError::invalid_params("Unknown approval request"),
524                )
525                .await;
526            return;
527        }
528        let _ = self
529            .outgoing
530            .send_response(connection_id, request.id, json!({}))
531            .await;
532    }
533
534    async fn process_initialize(&mut self, connection_id: ConnectionId, request: JsonRpcRequest) {
535        let state = self.connections.entry(connection_id).or_default();
536        if state.initialized {
537            let _ = self
538                .outgoing
539                .send_error(
540                    connection_id,
541                    request.id,
542                    AppServerError::already_initialized(),
543                )
544                .await;
545            return;
546        }
547
548        let params = match parse_params::<InitializeParams>(request.params) {
549            Ok(params) => params,
550            Err(error) => {
551                let _ = self
552                    .outgoing
553                    .send_error(connection_id, request.id, error)
554                    .await;
555                return;
556            }
557        };
558        if params.client_info.name.trim().is_empty() {
559            let _ = self
560                .outgoing
561                .send_error(
562                    connection_id,
563                    request.id,
564                    AppServerError::invalid_params("clientInfo.name is required"),
565                )
566                .await;
567            return;
568        }
569        state.initialized = true;
570        state.ready_for_notifications = true;
571        state.client_info = Some(params.client_info);
572        state.experimental_api = params.capabilities.experimental_api;
573        state.opt_out_notification_methods = params
574            .capabilities
575            .opt_out_notification_methods
576            .into_iter()
577            .collect();
578        self.outgoing
579            .configure_connection(connection_id, state.opt_out_notification_methods.clone())
580            .await;
581        self.outgoing
582            .mark_ready_for_notifications(connection_id)
583            .await;
584
585        let result = serde_json::to_value(InitializeResponse::new(
586            AppServerCapabilities::for_runtime(self.run_adapter.is_some()),
587        ))
588        .expect("initialize response serializes");
589        let _ = self
590            .outgoing
591            .send_response(connection_id, request.id, result)
592            .await;
593    }
594
595    async fn process_thread_list(&mut self, connection_id: ConnectionId, request: JsonRpcRequest) {
596        let params = match parse_params_or_default::<ThreadListParams>(request.params) {
597            Ok(params) => params,
598            Err(error) => {
599                let _ = self
600                    .outgoing
601                    .send_error(connection_id, request.id, error)
602                    .await;
603                return;
604            }
605        };
606        let Some(adapter) = self.run_adapter.clone() else {
607            let _ = self
608                .outgoing
609                .send_error(
610                    connection_id,
611                    request.id,
612                    AppServerError::internal("App Server runtime is not configured"),
613                )
614                .await;
615            return;
616        };
617        let include_archived = params.include_archived || params.archived.unwrap_or(false);
618        let mut threads = match adapter.store().list_threads(include_archived) {
619            Ok(threads) => threads,
620            Err(error) => {
621                let _ = self
622                    .outgoing
623                    .send_error(connection_id, request.id, store_error(error))
624                    .await;
625                return;
626            }
627        };
628        if let Some(archived) = params.archived {
629            threads.retain(|thread| thread.archived_at.is_some() == archived);
630        }
631        for thread in &mut threads {
632            if self.thread_state.is_closed(&thread.thread_id).await {
633                thread.status = ThreadStatus::Closed;
634            }
635        }
636        let offset = params.offset.unwrap_or_default();
637        let limit = params.limit.unwrap_or(threads.len());
638        let threads = threads.into_iter().skip(offset).take(limit).collect();
639        let result =
640            serde_json::to_value(ThreadListResponse { threads }).expect("thread list serializes");
641        let _ = self
642            .outgoing
643            .send_response(connection_id, request.id, result)
644            .await;
645    }
646
647    async fn process_thread_archive(
648        &mut self,
649        connection_id: ConnectionId,
650        request: JsonRpcRequest,
651    ) {
652        let params = match parse_params::<ThreadArchiveParams>(request.params) {
653            Ok(params) => params,
654            Err(error) => {
655                let _ = self
656                    .outgoing
657                    .send_error(connection_id, request.id, error)
658                    .await;
659                return;
660            }
661        };
662        let Some(adapter) = self.run_adapter.clone() else {
663            let _ = self
664                .outgoing
665                .send_error(
666                    connection_id,
667                    request.id,
668                    AppServerError::internal("App Server runtime is not configured"),
669                )
670                .await;
671            return;
672        };
673        match adapter.store().get_thread(&params.thread_id) {
674            Ok(Some(_)) => {}
675            Ok(None) => {
676                let _ = self
677                    .outgoing
678                    .send_error(
679                        connection_id,
680                        request.id,
681                        AppServerError::thread_not_found(),
682                    )
683                    .await;
684                return;
685            }
686            Err(error) => {
687                let _ = self
688                    .outgoing
689                    .send_error(connection_id, request.id, store_error(error))
690                    .await;
691                return;
692            }
693        }
694        if let Err(error) = adapter.store().archive_thread(&params.thread_id) {
695            let _ = self
696                .outgoing
697                .send_error(connection_id, request.id, store_error(error))
698                .await;
699            return;
700        }
701        let archived = ThreadArchiveResponse {
702            thread_id: params.thread_id.clone(),
703            archived: true,
704        };
705        let result = serde_json::to_value(&archived).expect("thread archive response serializes");
706        let _ = self
707            .outgoing
708            .send_response(connection_id, request.id, result)
709            .await;
710        let _ = self
711            .outgoing
712            .send_notification(connection_id, ServerNotification::ThreadArchived(archived))
713            .await;
714        let _ = self
715            .outgoing
716            .send_notification(
717                connection_id,
718                ServerNotification::ThreadStatusChanged(ThreadStatusChangedParams {
719                    thread_id: params.thread_id,
720                    status: ThreadStatus::Archived,
721                }),
722            )
723            .await;
724    }
725
726    async fn process_thread_unsubscribe(
727        &mut self,
728        connection_id: ConnectionId,
729        request: JsonRpcRequest,
730    ) {
731        let params = match parse_params::<ThreadUnsubscribeParams>(request.params) {
732            Ok(params) => params,
733            Err(error) => {
734                let _ = self
735                    .outgoing
736                    .send_error(connection_id, request.id, error)
737                    .await;
738                return;
739            }
740        };
741        let Some(adapter) = self.run_adapter.clone() else {
742            let _ = self
743                .outgoing
744                .send_error(
745                    connection_id,
746                    request.id,
747                    AppServerError::internal("App Server runtime is not configured"),
748                )
749                .await;
750            return;
751        };
752        match adapter.store().get_thread(&params.thread_id) {
753            Ok(Some(_)) => {}
754            Ok(None) => {
755                let _ = self
756                    .outgoing
757                    .send_error(
758                        connection_id,
759                        request.id,
760                        AppServerError::thread_not_found(),
761                    )
762                    .await;
763                return;
764            }
765            Err(error) => {
766                let _ = self
767                    .outgoing
768                    .send_error(connection_id, request.id, store_error(error))
769                    .await;
770                return;
771            }
772        }
773        let closed = self
774            .thread_state
775            .unsubscribe(&params.thread_id, connection_id)
776            .await;
777        let result = serde_json::to_value(ThreadUnsubscribeResponse {
778            thread_id: params.thread_id.clone(),
779            subscribed: false,
780            closed,
781        })
782        .expect("thread unsubscribe response serializes");
783        let _ = self
784            .outgoing
785            .send_response(connection_id, request.id, result)
786            .await;
787        if closed {
788            let _ = self
789                .outgoing
790                .send_notification(
791                    connection_id,
792                    ServerNotification::ThreadClosed(ThreadClosedParams {
793                        thread_id: params.thread_id.clone(),
794                    }),
795                )
796                .await;
797            let _ = self
798                .outgoing
799                .send_notification(
800                    connection_id,
801                    ServerNotification::ThreadStatusChanged(ThreadStatusChangedParams {
802                        thread_id: params.thread_id,
803                        status: ThreadStatus::Closed,
804                    }),
805                )
806                .await;
807        }
808    }
809
810    async fn process_notification(
811        &mut self,
812        connection_id: ConnectionId,
813        notification: JsonRpcNotification,
814    ) {
815        if notification.method != "initialized" {
816            return;
817        }
818        let Some(state) = self.connections.get_mut(&connection_id) else {
819            return;
820        };
821        if !state.initialized {
822            return;
823        }
824        state.ready_for_notifications = true;
825        self.outgoing
826            .mark_ready_for_notifications(connection_id)
827            .await;
828    }
829
830    async fn process_thread_start(&mut self, connection_id: ConnectionId, request: JsonRpcRequest) {
831        let params = match parse_params_or_default::<ThreadStartParams>(request.params) {
832            Ok(params) => params,
833            Err(error) => {
834                let _ = self
835                    .outgoing
836                    .send_error(connection_id, request.id, error)
837                    .await;
838                return;
839            }
840        };
841        let Some(adapter) = self.run_adapter.clone() else {
842            let _ = self
843                .outgoing
844                .send_error(
845                    connection_id,
846                    request.id,
847                    AppServerError::internal("App Server runtime is not configured"),
848                )
849                .await;
850            return;
851        };
852        let thread = match adapter.store().create_thread(params) {
853            Ok(thread) => thread,
854            Err(error) => {
855                let _ = self
856                    .outgoing
857                    .send_error(connection_id, request.id, store_error(error))
858                    .await;
859                return;
860            }
861        };
862        self.thread_state
863            .subscribe(thread.thread_id.clone(), connection_id)
864            .await;
865        let started = ThreadStartResponse::from_thread(&thread);
866        let result = serde_json::to_value(&started).expect("thread start response serializes");
867        let _ = self
868            .outgoing
869            .send_response(connection_id, request.id, result)
870            .await;
871        let _ = self
872            .outgoing
873            .send_notification(connection_id, ServerNotification::ThreadStarted(started))
874            .await;
875    }
876
877    async fn process_thread_read(&mut self, connection_id: ConnectionId, request: JsonRpcRequest) {
878        let params = match parse_params::<ThreadReadParams>(request.params) {
879            Ok(params) => params,
880            Err(error) => {
881                let _ = self
882                    .outgoing
883                    .send_error(connection_id, request.id, error)
884                    .await;
885                return;
886            }
887        };
888        let Some(adapter) = self.run_adapter.clone() else {
889            let _ = self
890                .outgoing
891                .send_error(
892                    connection_id,
893                    request.id,
894                    AppServerError::internal("App Server runtime is not configured"),
895                )
896                .await;
897            return;
898        };
899        let Some(mut thread) = (match adapter.store().get_thread(&params.thread_id) {
900            Ok(thread) => thread,
901            Err(error) => {
902                let _ = self
903                    .outgoing
904                    .send_error(connection_id, request.id, store_error(error))
905                    .await;
906                return;
907            }
908        }) else {
909            let _ = self
910                .outgoing
911                .send_error(
912                    connection_id,
913                    request.id,
914                    AppServerError::thread_not_found(),
915                )
916                .await;
917            return;
918        };
919        let mut items = match adapter.store().replay_items(&params.thread_id) {
920            Ok(items) => items,
921            Err(error) => {
922                let _ = self
923                    .outgoing
924                    .send_error(connection_id, request.id, store_error(error))
925                    .await;
926                return;
927            }
928        };
929        if let Some(after_item_id) = params.after_item_id {
930            if let Some(index) = items.iter().position(|item| item.item_id == after_item_id) {
931                items = items.into_iter().skip(index + 1).collect();
932            }
933        }
934        let turns = match adapter.store().list_turns(&params.thread_id) {
935            Ok(turns) => turns,
936            Err(error) => {
937                let _ = self
938                    .outgoing
939                    .send_error(connection_id, request.id, store_error(error))
940                    .await;
941                return;
942            }
943        };
944        if self.thread_state.is_closed(&params.thread_id).await {
945            thread.status = ThreadStatus::Closed;
946        }
947        let result = serde_json::to_value(ThreadReadResponse {
948            thread,
949            turns,
950            items,
951        })
952        .expect("thread read response serializes");
953        let _ = self
954            .outgoing
955            .send_response(connection_id, request.id, result)
956            .await;
957    }
958}