supercode_harness/
runtime_mail.rs1use crate::frontend::{FrontendRuntime, FrontendTurnState, HttpFrontendRuntime};
15use crate::live_runtime::{list_live_runtimes, resolve_live_runtime, LiveRuntimeRecord};
16
17pub fn controlled_runtime(harness: &str, session_id: &str) -> Option<LiveRuntimeRecord> {
20 list_live_runtimes().ok()?.into_iter().find(|record| {
21 record.source.harness == harness
22 && (record.source.session_id == session_id || record.runtime_session_id == session_id)
23 })
24}
25
26pub fn controlled_runtimes() -> Vec<LiveRuntimeRecord> {
28 list_live_runtimes().unwrap_or_default()
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum RuntimeDelivery {
34 Steered,
36 Started,
38 NotWoken,
41}
42
43pub async fn runtime_turn_state(record: &LiveRuntimeRecord) -> Result<FrontendTurnState, String> {
45 let remote = connect(record).await?;
46 FrontendRuntime::describe(remote.as_ref())
47 .await
48 .map(|descriptor| descriptor.turn_state)
49 .map_err(|error| error.to_string())
50}
51
52pub async fn deliver_to_runtime(
55 record: &LiveRuntimeRecord,
56 text: String,
57 wake: bool,
58) -> Result<RuntimeDelivery, String> {
59 let remote = connect(record).await?;
60 let descriptor = FrontendRuntime::describe(remote.as_ref())
61 .await
62 .map_err(|error| error.to_string())?;
63 if descriptor.session_id != record.runtime_session_id {
64 return Err("the live runtime's identity did not match its receipt".into());
65 }
66 match descriptor.turn_state {
67 FrontendTurnState::Busy if descriptor.actions.steer => {
68 FrontendRuntime::steer(remote.as_ref(), text)
69 .await
70 .map_err(|error| error.to_string())?;
71 Ok(RuntimeDelivery::Steered)
72 }
73 FrontendTurnState::Busy => Err(
74 "a turn is running and this runtime does not accept steering; the message was not \
75 delivered into it"
76 .into(),
77 ),
78 FrontendTurnState::Idle if wake => {
79 FrontendRuntime::send_input_with_images(remote.clone(), text, Vec::new())
80 .await
81 .map_err(|error| error.to_string())?;
82 Ok(RuntimeDelivery::Started)
83 }
84 FrontendTurnState::Idle => Ok(RuntimeDelivery::NotWoken),
85 }
86}
87
88pub fn answer_to(
93 homes: &crate::HarnessHomes,
94 record: &LiveRuntimeRecord,
95 message_id: &str,
96) -> Option<String> {
97 let query = crate::DiscoveryQuery {
98 harnesses: vec![crate::HarnessId::new(record.source.harness.clone())],
99 homes: homes.clone(),
100 workspace: Some(record.source.workspace.clone()),
101 ..Default::default()
102 };
103 let descriptor = crate::discover_sessions(&query)
104 .ok()?
105 .into_iter()
106 .find(|descriptor| {
107 descriptor.locator.session_id == record.source.session_id
108 || descriptor.locator.session_id == record.runtime_session_id
109 })?;
110 let session =
111 crate::sdk::load_session_with_fidelity(&descriptor.locator, crate::Fidelity::Semantic)
112 .ok()?;
113 let text = |message: &crate::ChatMessage| message.content.clone().unwrap_or_default();
114 let asked = session.messages.iter().rposition(|message| {
115 message.role == crate::Role::User && text(message).contains(message_id)
116 })?;
117 session.messages[asked + 1..]
118 .iter()
119 .rev()
120 .find(|message| message.role == crate::Role::Assistant && !text(message).trim().is_empty())
121 .map(text)
122}
123
124async fn connect(
125 record: &LiveRuntimeRecord,
126) -> Result<std::sync::Arc<HttpFrontendRuntime>, String> {
127 let receipt = resolve_live_runtime(&record.endpoint, &record.source)
128 .map_err(|error| error.to_string())?;
129 HttpFrontendRuntime::connect(receipt.base_url, receipt.token)
130 .await
131 .map_err(|error| error.to_string())
132}