typesec_agent/interop.rs
1//! Wire-level interop: guard LLM tool calls from any agent framework.
2//!
3//! Agent frameworks (Pydantic AI, LangChain, the OpenAI and Anthropic SDKs)
4//! all share the same last-mile shape: the model emits a *tool call* — a tool
5//! name plus JSON arguments — and the host process decides whether to run it.
6//! That decision point is exactly where Typesec belongs, and it is the one
7//! place none of the frameworks guard for you.
8//!
9//! This module provides:
10//!
11//! - [`ToolCallRequest`] — a framework-neutral, normalized tool call.
12//! - [`ToolBinding`] — the declaration of how one tool maps onto the Typesec
13//! `(action, resource)` plane, optionally taking the resource from a tool
14//! argument.
15//! - [`ToolCallGuard`] — evaluates normalized calls against any
16//! [`PolicyEngine`](typesec_core::policy::PolicyEngine), **deny-by-default**
17//! for tools without a binding.
18//! - Dialect codecs ([`openai`], [`anthropic`], [`langchain`],
19//! [`pydantic_ai`]) that parse each framework's wire shape into
20//! [`ToolCallRequest`]s and render denials back in the shape the framework
21//! expects (an error tool-result / retry part), so a blocked call flows back
22//! to the model as feedback instead of crashing the run.
23//!
24//! ```text
25//! model output ─▶ dialect::parse_tool_calls ─▶ ToolCallGuard::check_all
26//! │ Allow ─▶ run the tool
27//! └ Deny ─▶ dialect::denial ─▶ model
28//! ```
29//!
30//! The typed [`ProtectedTool`](crate::ProtectedTool) path remains the
31//! strongest boundary (a capability is required to *compile* the call); this
32//! module is the runtime bridge for tools that live on the other side of a
33//! JSON wire, where Rust types cannot reach.
34
35mod call;
36mod guard;
37mod taint;
38mod wire;
39
40pub mod anthropic;
41pub mod dialects;
42pub mod langchain;
43pub mod mcp;
44pub mod openai;
45pub mod pydantic_ai;
46
47pub use call::{GuardedToolCall, InteropError, ToolBinding, ToolCallRequest, ToolCallVerdict};
48pub use guard::ToolCallGuard;
49pub use taint::{TOOL_OUTPUT_KIND, TaintError};
50
51#[cfg(test)]
52mod tests;