1use std::pin::Pin;
14use std::sync::atomic::{AtomicUsize, Ordering};
15use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
16use std::task::Poll;
17use std::time::Duration;
18
19use futures::stream::{BoxStream, FuturesUnordered};
20use futures::{Future, FutureExt, StreamExt};
21use oino_ai::JsonValue;
22use oino_client::{
23 AgentCommand, ClientError, OinoCommand, RpcCommand, RpcRequest, RpcResponse, SessionBackend,
24};
25use oino_core::events::{AgentSessionEvent, ClientEvent};
26use oino_core::ui::{UiAnswer, UiResponse};
27use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
28use tokio::sync::mpsc;
29
30use crate::jsonl::{LineReader, serialize_line};
31use crate::wire::{Connection, WireEvent, rpc_event};
32
33const REPLY_QUEUE: usize = 1024;
35const FORWARD_GRACE: Duration = Duration::from_millis(500);
37const PREFLIGHT_HOLD: Duration = Duration::from_secs(1);
39
40#[derive(Debug, thiserror::Error)]
42#[non_exhaustive]
43pub enum RpcServerError {
44 #[error("reading commands: {0}")]
46 Read(#[source] std::io::Error),
47}
48
49struct Reply {
51 response: RpcResponse,
52 prompt: bool,
54}
55
56struct Shared {
57 backend: Arc<dyn SessionBackend>,
58 replies: mpsc::Sender<Reply>,
59 connection: Arc<Mutex<Arc<Connection>>>,
61 preflight: Arc<AtomicUsize>,
63}
64
65fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
66 mutex.lock().unwrap_or_else(PoisonError::into_inner)
67}
68
69impl Shared {
70 async fn reply(&self, response: RpcResponse, prompt: bool) {
71 let _ = self.replies.send(Reply { response, prompt }).await;
72 }
73
74 async fn handle_line(self: Arc<Self>, line: String) {
75 let value: JsonValue = match serde_json::from_str(&line) {
76 Ok(value) => value,
77 Err(error) => {
78 let message = format!("Failed to parse command: {error}");
79 self.reply(
80 RpcResponse::error(None, Some("parse".to_owned()), message),
81 false,
82 )
83 .await;
84 return;
85 }
86 };
87 if value.get("type").and_then(JsonValue::as_str) == Some("extension_ui_response") {
88 if let Ok(response) = serde_json::from_value::<UiResponse>(value) {
89 self.backend.ui_respond(response);
90 }
91 return;
92 }
93 let request = match RpcRequest::from_value(value) {
94 Ok(request) => request,
95 Err(rejected) => {
96 let response = RpcResponse::error(rejected.id, rejected.command, rejected.error);
97 self.reply(response, false).await;
98 return;
99 }
100 };
101 let kind = request.command.kind();
102 let prompt = matches!(
103 request.command,
104 RpcCommand::Agent(AgentCommand::Prompt { .. })
105 );
106 let hello = match &request.command {
107 RpcCommand::Oino(OinoCommand::Hello { ui, .. }) => Some(hello_connection(ui.as_ref())),
108 _ => None,
109 };
110 if prompt {
111 self.preflight.fetch_add(1, Ordering::AcqRel);
112 }
113 let request_id = request.id.as_ref().map(id_text);
114 let result = self.backend.send_with_id(request_id, request.command).await;
115 if let (Some(connection), true) = (hello, result.is_ok()) {
116 *lock(&self.connection) = Arc::new(connection);
117 }
118 let response = match result {
119 Ok(data) => RpcResponse::success(request.id, kind, data),
120 Err(ClientError::Command(message)) => {
121 RpcResponse::error(request.id, Some(kind.to_owned()), message)
122 }
123 Err(error) => RpcResponse::error(request.id, Some(kind.to_owned()), error.to_string()),
124 };
125 self.reply(response, prompt).await;
126 }
127}
128
129fn hello_connection(ui: Option<&JsonValue>) -> Connection {
132 match ui.and_then(|value| {
133 serde_json::from_value::<oino_ui::slot::UiCapabilities>(value.clone()).ok()
134 }) {
135 Some(capabilities) if capabilities.protocol >= 2 => Connection::v2(capabilities),
136 _ => Connection::v1(),
137 }
138}
139
140fn id_text(id: &JsonValue) -> String {
142 match id {
143 JsonValue::String(text) => text.clone(),
144 other => other.to_string(),
145 }
146}
147
148struct Writer<W> {
150 output: W,
151 events: BoxStream<'static, ClientEvent>,
152 backend: Arc<dyn SessionBackend>,
153 connection: Arc<Mutex<Arc<Connection>>>,
154 preflight: Arc<AtomicUsize>,
155 held: Option<ClientEvent>,
157 broken: bool,
158}
159
160fn is_agent_start(event: &ClientEvent) -> bool {
161 matches!(event, ClientEvent::Session(AgentSessionEvent::AgentStart))
162}
163
164impl<W: AsyncWrite + Unpin> Writer<W> {
165 async fn line(&mut self, value: &impl serde::Serialize) {
166 if self.broken {
167 return;
168 }
169 let line = serialize_line(value);
170 if self.output.write_all(line.as_bytes()).await.is_err()
171 || self.output.flush().await.is_err()
172 {
173 self.broken = true;
174 }
175 }
176
177 async fn event(&mut self, event: &ClientEvent) {
178 let connection = Arc::clone(&lock(&self.connection));
179 match rpc_event(event, &connection) {
180 WireEvent::Line(value) => self.line(&value).await,
181 WireEvent::Drop => {}
182 WireEvent::CancelDialog(id) => self.backend.ui_respond(UiResponse {
183 id,
184 answer: UiAnswer::Cancelled,
185 }),
186 }
187 }
188
189 async fn drain(&mut self, prompt: bool) {
191 while self.held.is_none() {
192 let Some(Some(event)) = self.events.next().now_or_never() else {
193 return;
194 };
195 if prompt && is_agent_start(&event) {
196 self.held = Some(event);
197 return;
198 }
199 self.event(&event).await;
200 }
201 }
202
203 async fn reply(&mut self, reply: Reply) {
204 self.drain(reply.prompt).await;
205 self.line(&reply.response).await;
206 if reply.prompt {
207 self.preflight.fetch_sub(1, Ordering::AcqRel);
208 self.release().await;
209 }
210 }
211
212 async fn release(&mut self) {
213 if let Some(event) = self.held.take() {
214 self.event(&event).await;
215 }
216 }
217
218 async fn run(mut self, mut replies: mpsc::Receiver<Reply>) {
219 let mut events_open = true;
220 loop {
221 let holding = self.held.is_some();
222 tokio::select! {
223 reply = replies.recv() => match reply {
224 Some(reply) => self.reply(reply).await,
225 None => break,
226 },
227 event = self.events.next(), if events_open && !holding => match event {
228 Some(event) if is_agent_start(&event)
229 && self.preflight.load(Ordering::Acquire) > 0 =>
230 {
231 self.held = Some(event);
232 }
233 Some(event) => self.event(&event).await,
234 None => events_open = false,
235 },
236 () = tokio::time::sleep(PREFLIGHT_HOLD), if holding => self.release().await,
237 }
238 }
239 self.release().await;
240 if events_open {
241 let rest = async {
242 while let Some(event) = self.events.next().await {
243 self.event(&event).await;
244 }
245 };
246 let _ = tokio::time::timeout(FORWARD_GRACE, rest).await;
247 }
248 let _ = self.output.flush().await;
249 }
250}
251
252pub struct RpcServer {
254 backend: Arc<dyn SessionBackend>,
255}
256
257impl std::fmt::Debug for RpcServer {
258 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259 f.debug_struct("RpcServer").finish_non_exhaustive()
260 }
261}
262
263type Pending = Pin<Box<dyn Future<Output = ()> + Send>>;
264
265impl RpcServer {
266 #[must_use]
268 pub fn new(backend: Arc<dyn SessionBackend>) -> Self {
269 Self { backend }
270 }
271
272 pub async fn run<R, W>(self, input: R, output: W) -> Result<(), RpcServerError>
280 where
281 R: AsyncRead + Unpin,
282 W: AsyncWrite + Unpin + Send + 'static,
283 {
284 let (replies, replies_rx) = mpsc::channel::<Reply>(REPLY_QUEUE);
285 let connection = Arc::new(Mutex::new(Arc::new(Connection::plain())));
286 let preflight = Arc::new(AtomicUsize::new(0));
287 let writer = Writer {
288 output,
289 events: self.backend.events(),
290 backend: Arc::clone(&self.backend),
291 connection: Arc::clone(&connection),
292 preflight: Arc::clone(&preflight),
293 held: None,
294 broken: false,
295 };
296 let writer = tokio::spawn(writer.run(replies_rx));
297 let shared = Arc::new(Shared {
298 backend: Arc::clone(&self.backend),
299 replies,
300 connection,
301 preflight,
302 });
303 let mut reader = LineReader::new(input);
304 let mut pending: FuturesUnordered<Pending> = FuturesUnordered::new();
305 let mut shutdown = self.backend.shutdown_requested();
306 let result = loop {
307 tokio::select! {
308 () = &mut shutdown => break Ok(()),
309 line = reader.next_line() => match line {
310 Ok(Some(line)) => {
311 let mut handler: Pending = Box::pin(Arc::clone(&shared).handle_line(line));
312 if futures::poll!(handler.as_mut()) == Poll::Pending {
313 pending.push(handler);
314 }
315 }
316 Ok(None) => break Ok(()),
317 Err(error) => break Err(RpcServerError::Read(error)),
318 },
319 Some(()) = pending.next(), if !pending.is_empty() => {}
320 }
321 };
322 drop(pending);
323 self.backend.shutdown().await;
324 drop(shared);
325 let _ = writer.await;
326 result
327 }
328}