Files
sausage/sdk/src/calls.rs
T
surma c728429d04 sdk, agent: review fixes (T18R)
- The serve loop ends the process after EOF or exit_after_response,
  also with background tasks or listeners.
- One schema generator per direction for the whole document, so types
  with the same name get distinct names.
- The agent gives the complete error (code, message, data) to the
  model, writes SKIP only after a successful request, caps tool output
  at 64 KiB, writes each history line in one write, and skips a damaged
  line.
- Endpoints skip blank and non-RPC lines. client_rs does not panic.
- #[must_use] on builders and the lock guard; #[schemars] attributes
  and duplicate hooks are compile errors; raw identifiers; -32000 for
  local SDK errors; x-sausage-params only for methods with params.
2026-10-23 02:38:35 +01:00

80 lines
2.6 KiB
Rust

//! Outgoing core calls and notification callbacks.
use super::*;
/// An outgoing request builder.
#[must_use = "await the call to send it"]
pub struct Call {
method: String,
params: Value,
detach: bool,
notify: Option<Notify>,
pub(super) unlock: Option<std::rc::Rc<Lease>>,
}
/// Build an outgoing call with object parameters.
pub fn call(method: impl Into<String>, params: impl serde::Serialize) -> Call {
Call {
method: method.into(),
params: serde_json::to_value(params).expect("serializable params"),
detach: false,
notify: None,
unlock: None,
}
}
impl Call {
/// Detach the child process from its caller.
pub fn detach(mut self) -> Self {
self.detach = true;
self
}
/// Receive progress notifications before the result arrives.
pub fn on_notify(mut self, f: impl FnMut(&str, Value) + 'static) -> Self {
self.notify = Some(Box::new(f));
self
}
}
impl IntoFuture for Call {
type Output = Result<Value>;
type IntoFuture = LocalBoxFuture<'static, Self::Output>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
if !self.params.is_object() {
return Err(Error::new(-32602, "params must be an object"));
}
let (tx, rx) = oneshot::channel();
let id = STATE.with(|s| {
let mut s = s.borrow_mut();
if !s.active || s.stopping {
return Err(Error::new(
-32000,
"no core transport (or stdin reached EOF)",
));
}
s.next += 1;
let id = s.next;
s.pending.insert(
id,
Pending {
tx,
notify: self.notify,
unlock: self.unlock,
},
);
Ok(id)
})?;
let (method, params) = if self.detach {
(
"core.call".to_string(),
json!({"method":self.method,"params":self.params,"detach":true}),
)
} else {
(self.method, self.params)
};
if let Err(e) = send(json!({"jsonrpc":"2.0","id":id,"method":method,"params":params})) {
STATE.with(|s| s.borrow_mut().pending.remove(&id));
return Err(e);
}
rx.await
.unwrap_or_else(|_| Err(Error::new(-32000, "core transport closed")))
})
}
}