1use anyhow::{Context, Result};
4use serde_json::Value;
5use std::collections::HashMap;
6use std::io::{BufRead, Write};
7use tokio::sync::oneshot;
8
9use super::protocol::*;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum PasteState {
18 Normal,
20 Pasting,
22}
23
24pub struct PasteHandler {
26 pub state: PasteState,
28 pub buffer: Vec<u8>,
30 start_sequence: Vec<u8>,
32 end_sequence: Vec<u8>,
34}
35
36impl PasteHandler {
37 pub fn new() -> Self {
39 Self {
40 state: PasteState::Normal,
41 buffer: Vec::new(),
42 start_sequence: vec![0x1B, 0x5B, 0x32, 0x30, 0x30, 0x7E], end_sequence: vec![0x1B, 0x5B, 0x32, 0x30, 0x31, 0x7E], }
45 }
46
47 pub fn reset(&mut self) {
49 self.state = PasteState::Normal;
50 self.buffer.clear();
51 }
52
53 pub fn state(&self) -> PasteState {
55 self.state.clone()
56 }
57
58 pub fn buffer(&self) -> &[u8] {
60 &self.buffer
61 }
62
63 pub fn process_byte(&mut self, byte: u8) -> Option<u8> {
66 match self.state {
67 PasteState::Normal => {
68 if (self.buffer.is_empty() && byte == 0x1B)
69 || (!self.buffer.is_empty() && self.buffer[0] == 0x1B && byte == 0x5B)
70 || (self.buffer.len() >= 2
71 && self.buffer[0] == 0x1B
72 && self.buffer[1] == 0x5B
73 && byte == 0x32)
74 || (self.buffer.len() >= 3
75 && self.buffer[0] == 0x1B
76 && self.buffer[1] == 0x5B
77 && self.buffer[2] == 0x32
78 && byte == 0x30)
79 || (self.buffer.len() >= 4
80 && self.buffer[0] == 0x1B
81 && self.buffer[1] == 0x5B
82 && self.buffer[2] == 0x32
83 && self.buffer[3] == 0x30
84 && byte == 0x30)
85 {
86 self.buffer.push(byte);
87 None
88 } else if self.buffer.len() >= 5
89 && self.buffer[0] == 0x1B
90 && self.buffer[1] == 0x5B
91 && self.buffer[2] == 0x32
92 && self.buffer[3] == 0x30
93 && self.buffer[4] == 0x30
94 && byte == 0x7E
95 {
96 self.buffer.clear();
98 self.state = PasteState::Pasting;
99 None
100 } else {
101 let first_byte = self.buffer.first().copied();
102 self.buffer.clear();
103 first_byte
104 }
105 }
106 PasteState::Pasting => {
107 if (self.buffer.is_empty() && byte == 0x1B)
108 || (!self.buffer.is_empty() && self.buffer[0] == 0x1B && byte == 0x5B)
109 || (self.buffer.len() >= 2
110 && self.buffer[0] == 0x1B
111 && self.buffer[1] == 0x5B
112 && byte == 0x32)
113 || (self.buffer.len() >= 3
114 && self.buffer[0] == 0x1B
115 && self.buffer[1] == 0x5B
116 && self.buffer[2] == 0x32
117 && byte == 0x30)
118 || (self.buffer.len() >= 4
119 && self.buffer[0] == 0x1B
120 && self.buffer[1] == 0x5B
121 && self.buffer[2] == 0x32
122 && self.buffer[3] == 0x30
123 && byte == 0x31)
124 {
125 self.buffer.push(byte);
126 None
127 } else if self.buffer.len() >= 5
128 && self.buffer[0] == 0x1B
129 && self.buffer[1] == 0x5B
130 && self.buffer[2] == 0x32
131 && self.buffer[3] == 0x30
132 && self.buffer[4] == 0x31
133 && byte == 0x7E
134 {
135 self.buffer.clear();
137 self.state = PasteState::Normal;
138 None
139 } else {
140 self.buffer.push(byte);
141 None
142 }
143 }
144 }
145 }
146
147 pub fn ends_with(&self, sequence: &[u8]) -> bool {
149 if self.buffer.len() < sequence.len() {
150 return false;
151 }
152 let end_pos = self.buffer.len() - sequence.len();
153 &self.buffer[end_pos..] == sequence
154 }
155
156 pub fn extract_image_data(&self) -> Option<Vec<u8>> {
158 let buffer = self.buffer();
159 if buffer.len() < 8 {
160 return None;
161 }
162
163 if buffer.starts_with(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) {
165 return Some(buffer.to_vec());
166 }
167
168 if buffer.starts_with(&[0xFF, 0xD8, 0xFF]) {
170 return Some(buffer.to_vec());
171 }
172
173 if buffer.iter().take(100).filter(|&&b| b == 0).count() > 5 {
175 return Some(buffer.to_vec());
176 }
177
178 None
179 }
180}
181
182impl Default for PasteHandler {
183 fn default() -> Self {
184 Self::new()
185 }
186}
187
188#[derive(Debug, Clone)]
194pub struct RpcClientConfig {
195 pub binary_path: String,
197 pub cwd: Option<String>,
199 pub env: Vec<(String, String)>,
201 pub provider: Option<String>,
203 pub model: Option<String>,
205 pub args: Vec<String>,
207}
208
209impl Default for RpcClientConfig {
210 fn default() -> Self {
211 Self {
212 binary_path: "oxi".to_string(),
213 cwd: None,
214 env: Vec::new(),
215 provider: None,
216 model: None,
217 args: Vec::new(),
218 }
219 }
220}
221
222pub type BoxedEventListener = Box<dyn Fn(RpcEvent) + Send>;
224
225pub struct RpcClient {
229 config: RpcClientConfig,
230 child: Option<std::process::Child>,
231 line_reader: JsonlLineReader,
232 pending_requests: HashMap<String, oneshot::Sender<RpcResponse>>,
233 request_counter: u64,
234 event_listeners: Vec<BoxedEventListener>,
235 stderr_buffer: String,
236}
237
238impl RpcClient {
239 pub fn new(config: RpcClientConfig) -> Self {
241 Self {
242 config,
243 child: None,
244 line_reader: JsonlLineReader::new(),
245 pending_requests: HashMap::new(),
246 request_counter: 0,
247 event_listeners: Vec::new(),
248 stderr_buffer: String::new(),
249 }
250 }
251
252 pub fn start(&mut self) -> Result<()> {
254 if self.child.is_some() {
255 anyhow::bail!("Client already started");
256 }
257
258 let mut args = vec!["--mode".to_string(), "rpc".to_string()];
259 if let Some(ref provider) = self.config.provider {
260 args.push("--provider".to_string());
261 args.push(provider.clone());
262 }
263 if let Some(ref model) = self.config.model {
264 args.push("--model".to_string());
265 args.push(model.clone());
266 }
267 args.extend(self.config.args.iter().cloned());
268
269 let mut cmd = std::process::Command::new(&self.config.binary_path);
270 cmd.args(&args)
271 .stdin(std::process::Stdio::piped())
272 .stdout(std::process::Stdio::piped())
273 .stderr(std::process::Stdio::piped());
274
275 if let Some(ref cwd) = self.config.cwd {
276 cmd.current_dir(cwd);
277 }
278
279 for (key, value) in &self.config.env {
280 cmd.env(key, value);
281 }
282
283 let child = cmd.spawn().context("Failed to spawn oxi RPC process")?;
284 self.child = Some(child);
285
286 Ok(())
287 }
288
289 pub fn stop(&mut self) -> Result<()> {
291 if let Some(mut child) = self.child.take() {
292 let _ = child.kill();
293 let _ = child.wait();
294 }
295 self.pending_requests.clear();
296 Ok(())
297 }
298
299 pub fn on_event<F>(&mut self, listener: F)
301 where
302 F: Fn(RpcEvent) + Send + 'static,
303 {
304 self.event_listeners.push(Box::new(listener));
305 }
306
307 pub fn stderr(&self) -> &str {
309 &self.stderr_buffer
310 }
311
312 pub fn prompt(&mut self, message: &str) -> Result<()> {
314 Self::require_success(self.send_and_wait(serde_json::json!({
315 "type": "prompt",
316 "message": message
317 }))?)
318 }
319
320 pub fn steer(&mut self, message: &str) -> Result<()> {
322 Self::require_success(self.send_and_wait(serde_json::json!({
323 "type": "steer",
324 "message": message
325 }))?)
326 }
327
328 pub fn follow_up(&mut self, message: &str) -> Result<()> {
330 Self::require_success(self.send_and_wait(serde_json::json!({
331 "type": "follow_up",
332 "message": message
333 }))?)
334 }
335
336 pub fn abort(&mut self) -> Result<()> {
338 Self::require_success(self.send_and_wait(serde_json::json!({ "type": "abort" }))?)
339 }
340
341 pub fn new_session(&mut self, parent_session: Option<&str>) -> Result<RpcResponse> {
343 let mut cmd = serde_json::json!({ "type": "new_session" });
344 if let Some(parent) = parent_session {
345 cmd["parent_session"] = Value::String(parent.to_string());
346 }
347 self.send_and_wait(cmd)
348 }
349
350 pub fn get_state(&mut self) -> Result<RpcResponse> {
352 self.send_and_wait(serde_json::json!({ "type": "get_state" }))
353 }
354
355 pub fn set_model(&mut self, provider: &str, model_id: &str) -> Result<RpcResponse> {
357 self.send_and_wait(serde_json::json!({
358 "type": "set_model",
359 "provider": provider,
360 "model_id": model_id
361 }))
362 }
363
364 pub fn cycle_model(&mut self) -> Result<RpcResponse> {
366 self.send_and_wait(serde_json::json!({ "type": "cycle_model" }))
367 }
368
369 pub fn get_available_models(&mut self) -> Result<RpcResponse> {
371 self.send_and_wait(serde_json::json!({ "type": "get_available_models" }))
372 }
373
374 pub fn set_thinking_level(&mut self, level: &str) -> Result<()> {
376 Self::require_success(self.send_and_wait(serde_json::json!({
377 "type": "set_thinking_level",
378 "level": level
379 }))?)
380 }
381
382 pub fn cycle_thinking_level(&mut self) -> Result<RpcResponse> {
384 self.send_and_wait(serde_json::json!({ "type": "cycle_thinking_level" }))
385 }
386
387 pub fn set_steering_mode(&mut self, mode: &str) -> Result<()> {
389 Self::require_success(self.send_and_wait(serde_json::json!({
390 "type": "set_steering_mode",
391 "mode": mode
392 }))?)
393 }
394
395 pub fn set_follow_up_mode(&mut self, mode: &str) -> Result<()> {
397 Self::require_success(self.send_and_wait(serde_json::json!({
398 "type": "set_follow_up_mode",
399 "mode": mode
400 }))?)
401 }
402
403 pub fn compact(&mut self, custom_instructions: Option<&str>) -> Result<RpcResponse> {
405 let mut cmd = serde_json::json!({ "type": "compact" });
406 if let Some(instructions) = custom_instructions {
407 cmd["custom_instructions"] = Value::String(instructions.to_string());
408 }
409 self.send_and_wait(cmd)
410 }
411
412 pub fn set_auto_compaction(&mut self, enabled: bool) -> Result<()> {
414 Self::require_success(self.send_and_wait(serde_json::json!({
415 "type": "set_auto_compaction",
416 "enabled": enabled
417 }))?)
418 }
419
420 pub fn set_auto_retry(&mut self, enabled: bool) -> Result<()> {
422 Self::require_success(self.send_and_wait(serde_json::json!({
423 "type": "set_auto_retry",
424 "enabled": enabled
425 }))?)
426 }
427
428 pub fn abort_retry(&mut self) -> Result<()> {
430 Self::require_success(self.send_and_wait(serde_json::json!({ "type": "abort_retry" }))?)
431 }
432
433 pub fn send_raw(&mut self, command: Value) -> Result<()> {
437 Self::require_success(self.send_and_wait(command)?)
438 }
439
440 pub fn bash(&mut self, command: &str) -> Result<RpcResponse> {
442 self.send_and_wait(serde_json::json!({
443 "type": "bash",
444 "command": command
445 }))
446 }
447
448 pub fn abort_bash(&mut self) -> Result<()> {
450 Self::require_success(self.send_and_wait(serde_json::json!({ "type": "abort_bash" }))?)
451 }
452
453 pub fn get_session_stats(&mut self) -> Result<RpcResponse> {
455 self.send_and_wait(serde_json::json!({ "type": "get_session_stats" }))
456 }
457
458 pub fn export_html(&mut self, output_path: Option<&str>) -> Result<RpcResponse> {
460 let mut cmd = serde_json::json!({ "type": "export_html" });
461 if let Some(path) = output_path {
462 cmd["output_path"] = Value::String(path.to_string());
463 }
464 self.send_and_wait(cmd)
465 }
466
467 pub fn switch_session(&mut self, session_path: &str) -> Result<RpcResponse> {
469 self.send_and_wait(serde_json::json!({
470 "type": "switch_session",
471 "session_path": session_path
472 }))
473 }
474
475 pub fn fork(&mut self, entry_id: &str) -> Result<RpcResponse> {
477 self.send_and_wait(serde_json::json!({
478 "type": "fork",
479 "entry_id": entry_id
480 }))
481 }
482
483 pub fn clone_session(&mut self) -> Result<RpcResponse> {
485 self.send_and_wait(serde_json::json!({ "type": "clone" }))
486 }
487
488 pub fn get_fork_messages(&mut self) -> Result<RpcResponse> {
490 self.send_and_wait(serde_json::json!({ "type": "get_fork_messages" }))
491 }
492
493 pub fn get_last_assistant_text(&mut self) -> Result<RpcResponse> {
495 self.send_and_wait(serde_json::json!({ "type": "get_last_assistant_text" }))
496 }
497
498 pub fn set_session_name(&mut self, name: &str) -> Result<()> {
500 Self::require_success(self.send_and_wait(serde_json::json!({
501 "type": "set_session_name",
502 "name": name
503 }))?)
504 }
505
506 pub fn get_messages(&mut self) -> Result<RpcResponse> {
508 self.send_and_wait(serde_json::json!({ "type": "get_messages" }))
509 }
510
511 pub fn get_commands(&mut self) -> Result<RpcResponse> {
513 self.send_and_wait(serde_json::json!({ "type": "get_commands" }))
514 }
515
516 fn require_success(response: RpcResponse) -> Result<()> {
519 match response {
520 RpcResponse::Response { success: true, .. } => Ok(()),
521 RpcResponse::Response { error, command, .. } => anyhow::bail!(
522 "RPC command {command} failed: {}",
523 error.unwrap_or_else(|| "unknown error".to_string())
524 ),
525 RpcResponse::ExtensionUiRequest(_) => {
526 anyhow::bail!("unexpected extension UI request response")
527 }
528 }
529 }
530
531 fn next_request_id(&mut self) -> String {
533 self.request_counter += 1;
534 format!("req_{}", self.request_counter)
535 }
536
537 fn send_and_wait(&mut self, mut command: Value) -> Result<RpcResponse> {
539 let id = self.next_request_id();
540 if let Some(obj) = command.as_object_mut() {
541 obj.insert("id".to_string(), Value::String(id.clone()));
542 }
543
544 let line = serialize_json_line(&command);
545
546 {
548 let child = self.child.as_mut().context("Client not started")?;
549 if let Some(ref mut stdin) = child.stdin {
550 stdin
551 .write_all(line.as_bytes())
552 .context("Failed to write to stdin")?;
553 stdin.flush().context("Failed to flush stdin")?;
554 }
555 }
556
557 let child = self.child.as_mut().context("Client not started")?;
559 if let Some(ref mut stdout) = child.stdout {
560 let mut buf_reader = std::io::BufReader::new(std::io::BufReader::new(stdout));
561 let mut buf = String::new();
562 loop {
563 buf.clear();
564 match buf_reader.read_line(&mut buf) {
565 Ok(0) => anyhow::bail!("EOF while waiting for response"),
566 Ok(_) => {
567 let trimmed = buf.trim();
568 if trimmed.is_empty() {
569 continue;
570 }
571 match parse_json_line(trimmed) {
572 Ok(value) => {
573 if let Some(obj) = value.as_object() {
574 if obj.get("type").and_then(|v| v.as_str()) == Some("response")
576 && obj.get("id").and_then(|v| v.as_str())
577 == Some(id.as_str())
578 {
579 let success = obj
580 .get("success")
581 .and_then(|v| v.as_bool())
582 .unwrap_or(false);
583 let cmd_name = obj
584 .get("command")
585 .and_then(|v| v.as_str())
586 .unwrap_or("")
587 .to_string();
588 let data = obj.get("data").cloned();
589 let error = obj
590 .get("error")
591 .and_then(|v| v.as_str())
592 .map(|s| s.to_string());
593 return Ok(RpcResponse::Response {
594 id: Some(id.clone()),
595 command: cmd_name,
596 success,
597 data,
598 error,
599 });
600 }
601
602 let event_type = obj
604 .get("type")
605 .and_then(|v: &Value| v.as_str())
606 .unwrap_or("");
607 let event = match event_type {
608 "agent_start" => Some(RpcEvent::AgentStart),
609 "agent_end" => Some(RpcEvent::AgentEnd),
610 "thinking" => Some(RpcEvent::Thinking),
611 "thinking_end" => Some(RpcEvent::ThinkingEnd),
612 "tool_call_delta" => {
613 let tool_call_id = obj
614 .get("tool_call_id")
615 .and_then(|v: &Value| v.as_str())
616 .unwrap_or("")
617 .to_string();
618 let args_delta = obj
619 .get("args_delta")
620 .and_then(|v: &Value| v.as_str())
621 .unwrap_or("")
622 .to_string();
623 Some(RpcEvent::ToolCallDelta {
624 tool_call_id,
625 args_delta,
626 })
627 }
628 "error" => {
629 let msg = obj
630 .get("message")
631 .and_then(|v: &Value| v.as_str())
632 .unwrap_or("")
633 .to_string();
634 Some(RpcEvent::Error { message: msg })
635 }
636 "text_chunk" => {
637 let text = obj
638 .get("text")
639 .and_then(|v: &Value| v.as_str())
640 .unwrap_or("")
641 .to_string();
642 Some(RpcEvent::TextChunk { text })
643 }
644 "tool_start" => {
645 let tool = obj
646 .get("tool")
647 .and_then(|v: &Value| v.as_str())
648 .unwrap_or("")
649 .to_string();
650 Some(RpcEvent::ToolStart { tool })
651 }
652 "tool_end" => {
653 let tool = obj
654 .get("tool")
655 .and_then(|v: &Value| v.as_str())
656 .unwrap_or("")
657 .to_string();
658 Some(RpcEvent::ToolEnd { tool })
659 }
660 _ => None,
661 };
662 if let Some(event) = event {
663 for listener in &self.event_listeners {
664 listener(event.clone());
665 }
666 }
667 }
668 }
669 Err(_) => continue,
670 }
671 }
672 Err(e) => anyhow::bail!("Error reading stdout: {}", e),
673 }
674 }
675 } else {
676 anyhow::bail!("No stdout available");
677 }
678 }
679
680 fn handle_incoming_value(&mut self, expected_id: &str, value: Value) -> Result<RpcResponse> {
683 if let Some(obj) = value.as_object() {
684 if obj.get("type").and_then(|v| v.as_str()) == Some("response")
686 && obj.get("id").and_then(|v| v.as_str()) == Some(expected_id)
687 {
688 let success = obj
689 .get("success")
690 .and_then(|v| v.as_bool())
691 .unwrap_or(false);
692 let command = obj
693 .get("command")
694 .and_then(|v| v.as_str())
695 .unwrap_or("")
696 .to_string();
697 let data = obj.get("data").cloned();
698 let error = obj
699 .get("error")
700 .and_then(|v| v.as_str())
701 .map(|s| s.to_string());
702 return Ok(RpcResponse::Response {
703 id: Some(expected_id.to_string()),
704 command,
705 success,
706 data,
707 error,
708 });
709 }
710
711 let event_type = obj
713 .get("type")
714 .and_then(|v: &Value| v.as_str())
715 .unwrap_or("");
716 let event = match event_type {
717 "agent_start" => Some(RpcEvent::AgentStart),
718 "agent_end" => Some(RpcEvent::AgentEnd),
719 "thinking" => Some(RpcEvent::Thinking),
720 "thinking_end" => Some(RpcEvent::ThinkingEnd),
721 "tool_call_delta" => {
722 let tool_call_id = obj
723 .get("tool_call_id")
724 .and_then(|v: &Value| v.as_str())
725 .unwrap_or("")
726 .to_string();
727 let args_delta = obj
728 .get("args_delta")
729 .and_then(|v: &Value| v.as_str())
730 .unwrap_or("")
731 .to_string();
732 Some(RpcEvent::ToolCallDelta {
733 tool_call_id,
734 args_delta,
735 })
736 }
737 "error" => {
738 let msg = obj
739 .get("message")
740 .and_then(|v: &Value| v.as_str())
741 .unwrap_or("")
742 .to_string();
743 Some(RpcEvent::Error { message: msg })
744 }
745 "text_chunk" => {
746 let text = obj
747 .get("text")
748 .and_then(|v: &Value| v.as_str())
749 .unwrap_or("")
750 .to_string();
751 Some(RpcEvent::TextChunk { text })
752 }
753 "tool_start" => {
754 let tool = obj
755 .get("tool")
756 .and_then(|v: &Value| v.as_str())
757 .unwrap_or("")
758 .to_string();
759 Some(RpcEvent::ToolStart { tool })
760 }
761 "tool_end" => {
762 let tool = obj
763 .get("tool")
764 .and_then(|v: &Value| v.as_str())
765 .unwrap_or("")
766 .to_string();
767 Some(RpcEvent::ToolEnd { tool })
768 }
769 _ => None,
770 };
771 if let Some(event) = event {
772 for listener in &self.event_listeners {
773 listener(event.clone());
774 }
775 }
776 }
777
778 anyhow::bail!("Internal: not our response, caller should retry")
782 }
783}
784
785impl Drop for RpcClient {
786 fn drop(&mut self) {
787 let _ = self.stop();
788 }
789}