Skip to main content

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}