- 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.
80 lines
2.6 KiB
Rust
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")))
|
|
})
|
|
}
|
|
}
|