elicit_server/lib.rs
1//! Cross-crate workflow plugins for elicitation.
2//!
3//! `elicit_server` houses workflows that require visibility across multiple
4//! elicitation crates simultaneously — things that can't live in `elicit_reqwest`
5//! or `elicit_serde_json` without creating circular dependencies.
6//!
7//! # Plugins
8//!
9//! - [`EmitBinaryPlugin`] — recover agent tool compositions as compiled Rust
10//! binaries (requires `feature = "emit"`)
11//! - [`SecureFetchPlugin`] — HTTPS-enforced URL validation + HTTP fetch
12//! (`elicit_url` + `elicit_reqwest`)
13//! - [`FetchAndParsePlugin`] — HTTP fetch + JSON extraction
14//! (`elicit_reqwest` + `elicit_serde_json`)
15//!
16//! # Feature flags
17//!
18//! | Feature | Enables |
19//! |---|---|
20//! | `emit` | `EmitBinaryPlugin` + full code recovery pipeline |
21
22#![forbid(unsafe_code)]
23#![warn(missing_docs)]
24
25mod fetch_and_parse;
26mod secure_fetch;
27mod util;
28
29#[cfg(feature = "emit")]
30mod emit_plugin;
31
32pub use fetch_and_parse::FetchAndParsePlugin;
33pub use secure_fetch::SecureFetchPlugin;
34
35#[cfg(feature = "emit")]
36pub use emit_plugin::{EmitBinaryParams, EmitBinaryPlugin, WorkflowStep};
37#[cfg(feature = "emit")]
38pub use fetch_and_parse::dispatch_fetch_and_parse_emit;
39
40/// Look up a tool by name and deserialize its params, drawing from all
41/// `elicit_server` handlers and its dep crates registered via `#[elicit_tool]`.
42///
43/// This is a thin wrapper around [`elicitation::emit_code::dispatch_emit`]
44/// that anchors the linker to include every handler module in this crate and
45/// its workflow dependencies, ensuring their `register_emit!` constructors are
46/// present in the final binary. Integration tests must call this function
47/// (not the bare `dispatch_emit`) so the linker does not discard unreferenced
48/// CGUs.
49#[cfg(feature = "emit")]
50pub fn emit_dispatch(
51 tool: &str,
52 params: serde_json::Value,
53) -> Result<Box<dyn elicitation::emit_code::EmitCode>, String> {
54 emit_dispatch_crate(tool, "", params)
55}
56
57/// Dispatch to a specific crate's emit registration by crate name.
58///
59/// Use this when multiple crates register the same tool name (e.g.
60/// `"assert_future"` in `elicit_chrono`, `elicit_jiff`, `elicit_time`).
61#[cfg(feature = "emit")]
62pub fn emit_dispatch_crate(
63 tool: &str,
64 crate_name: &str,
65 params: serde_json::Value,
66) -> Result<Box<dyn elicitation::emit_code::EmitCode>, String> {
67 // Each size_of call references a params type from a handler module,
68 // pulling that CGU (and its register_emit! CTORs) into the link.
69 let _ = [
70 // elicit_server
71 std::mem::size_of::<secure_fetch::SecureFetchParams>(),
72 std::mem::size_of::<secure_fetch::ValidatedApiCallParams>(),
73 std::mem::size_of::<fetch_and_parse::FetchAndExtractParams>(),
74 std::mem::size_of::<fetch_and_parse::FetchAndValidateParams>(),
75 // elicit_url
76 std::mem::size_of::<elicit_url::ParseUrlParams>(),
77 // elicit_reqwest
78 std::mem::size_of::<elicit_reqwest::BuildRequestParams>(),
79 // elicit_chrono
80 std::mem::size_of::<elicit_chrono::ParseDateTimeParams>(),
81 // elicit_jiff
82 std::mem::size_of::<elicit_jiff::ParseTimestampParams>(),
83 // elicit_time
84 std::mem::size_of::<elicit_time::ParseOffsetParams>(),
85 // elicit_serde_json
86 std::mem::size_of::<elicit_serde_json::RawJson>(),
87 ];
88 if crate_name.is_empty() {
89 elicitation::emit_code::dispatch_emit(tool, params)
90 } else {
91 elicitation::emit_code::dispatch_emit_from(tool, crate_name, params)
92 }
93}