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.
This commit is contained in:
2026-10-23 02:38:35 +01:00
parent d7b78ed0fd
commit c728429d04
29 changed files with 829 additions and 104 deletions
+22
View File
@@ -0,0 +1,22 @@
use sausage_plugin::{rpc, on_stop, on_retired, Result};
struct Stops;
#[rpc]
impl Stops {
/// First stop hook.
#[on_stop]
fn a(&self) {}
/// Second stop hook.
#[on_stop]
fn b(&self) {}
}
struct Retires;
#[rpc]
impl Retires {
/// First retired hook.
#[on_retired]
fn a(&self) -> Result<()> { Ok(()) }
/// Second retired hook.
#[on_retired]
fn b(&self) -> Result<()> { Ok(()) }
}
fn main() {}
+19
View File
@@ -0,0 +1,19 @@
error: only one on_stop method is allowed
--> tests/ui/hooks.rs:8:5
|
8 | /// Second stop hook.
| ^^^^^^^^^^^^^^^^^^^^^
error: only one on_retired method is allowed
--> tests/ui/hooks.rs:18:5
|
18 | /// Second retired hook.
| ^^^^^^^^^^^^^^^^^^^^^^^^
warning: unused imports: `Result`, `on_retired`, and `on_stop`
--> tests/ui/hooks.rs:1:27
|
1 | use sausage_plugin::{rpc, on_stop, on_retired, Result};
| ^^^^^^^ ^^^^^^^^^^ ^^^^^^
|
= note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default
+11
View File
@@ -0,0 +1,11 @@
#![deny(unused_must_use)]
use sausage_plugin::{call, lock, Endpoint, Guard, TypedCall};
fn builders(endpoint: Endpoint) {
call("p.m", ());
TypedCall::<()>::new(call("p.m", ()));
lock("/state/file");
endpoint.call::<()>("m", ());
}
fn guard(g: Guard) -> Guard { g }
fn dropped(g: Guard) { guard(g); }
fn main() {}
+64
View File
@@ -0,0 +1,64 @@
error: unused `Call` that must be used
--> tests/ui/must_use.rs:4:5
|
4 | call("p.m", ());
| ^^^^^^^^^^^^^^^
|
= note: await the call to send it
note: the lint level is defined here
--> tests/ui/must_use.rs:1:9
|
1 | #![deny(unused_must_use)]
| ^^^^^^^^^^^^^^^
help: use `let _ = ...` to ignore the resulting value
|
4 | let _ = call("p.m", ());
| +++++++
error: unused `TypedCall` that must be used
--> tests/ui/must_use.rs:5:5
|
5 | TypedCall::<()>::new(call("p.m", ()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: await the call to send it
help: use `let _ = ...` to ignore the resulting value
|
5 | let _ = TypedCall::<()>::new(call("p.m", ()));
| +++++++
error: unused `Lock` that must be used
--> tests/ui/must_use.rs:6:5
|
6 | lock("/state/file");
| ^^^^^^^^^^^^^^^^^^^
|
= note: await the lock to acquire it
help: use `let _ = ...` to ignore the resulting value
|
6 | let _ = lock("/state/file");
| +++++++
error: unused `EndpointCall` that must be used
--> tests/ui/must_use.rs:7:5
|
7 | endpoint.call::<()>("m", ());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: await the endpoint call to send it
help: use `let _ = ...` to ignore the resulting value
|
7 | let _ = endpoint.call::<()>("m", ());
| +++++++
error: unused `Guard` that must be used
--> tests/ui/must_use.rs:10:24
|
10 | fn dropped(g: Guard) { guard(g); }
| ^^^^^^^^
|
= note: bind the guard to hold the lock
help: use `let _ = ...` to ignore the resulting value
|
10 | fn dropped(g: Guard) { let _ = guard(g); }
| +++++++
+1 -1
View File
@@ -12,7 +12,7 @@ help: the trait `Params` is implemented for `sausage_plugin::Empty`
note: required by a bound in `sausage_plugin::method`
--> src/discovery.rs
|
| pub fn method<P: Params, R: SchemaType>(doc: &mut Value, name: &str, docs: &str) {
| pub fn method<P: Params, R: SchemaType>(doc: &mut Discovery, name: &str, docs: &str) {
| ^^^^^^ required by this bound in `method`
error[E0277]: RPC params must be a #[schema] struct with named fields
+9
View File
@@ -0,0 +1,9 @@
use sausage_plugin::schema;
/// A documented object.
#[schema]
struct Object {
/// A documented field.
#[schemars(with = "std::collections::HashMap<String, String>")]
value: String,
}
fn main() {}
+5
View File
@@ -0,0 +1,5 @@
error: schemars attributes are outside the schema subset
--> tests/ui/schemars.rs:6:5
|
6 | #[schemars(with = "std::collections::HashMap<String, String>")]
| ^