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(¶ms) {
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(¶ms.thread_id),
506 Some(¶ms.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(¶ms.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(¶ms.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(¶ms.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(¶ms.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(¶ms.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(¶ms.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(¶ms.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(¶ms.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}