Skip to main content

synaptic_deep/
lib.rs

1//! Deep agent harness for Synaptic.
2//!
3//! Provides an opinionated agent harness that bundles filesystem tools,
4//! subagent spawning, skills, memory, and auto-summarization — all
5//! implemented as [`AgentMiddleware`](synaptic_middleware::AgentMiddleware).
6//!
7//! # Quick Start
8//!
9//! ```rust,ignore
10//! use synaptic_deep::{create_deep_agent, DeepAgentOptions, backend::StateBackend};
11//!
12//! let backend = Arc::new(StateBackend::new());
13//! let options = DeepAgentOptions::new(backend);
14//! let agent = create_deep_agent(model, options)?;
15//! let result = agent.invoke(MessageState::with_messages(vec![
16//!     Message::human("Write hello.txt"),
17//! ])).await?;
18//! ```
19
20pub mod backend;
21#[cfg(feature = "config-builder")]
22pub mod builder;
23pub mod middleware;
24pub mod tools;
25
26use std::sync::Arc;
27
28use synaptic_core::{ChatModel, Store, SynapticError, Tool};
29use synaptic_graph::{create_agent, AgentOptions, Checkpointer, CompiledGraph, MessageState};
30use synaptic_macros::traceable;
31use synaptic_middleware::AgentMiddleware;
32
33use backend::Backend;
34pub use middleware::subagent::SubAgentDef;
35
36#[cfg(feature = "config-builder")]
37pub use builder::build_agent_from_config;
38
39/// Configuration for [`create_deep_agent`].
40pub struct DeepAgentOptions {
41    /// Backend for filesystem operations.
42    pub backend: Arc<dyn Backend>,
43    /// Optional system prompt prepended to all model calls.
44    pub system_prompt: Option<String>,
45    /// Additional tools beyond the built-in filesystem tools.
46    pub tools: Vec<Arc<dyn Tool>>,
47    /// Additional middleware beyond the built-in stack.
48    pub middleware: Vec<Arc<dyn AgentMiddleware>>,
49    /// Optional checkpointer for graph state persistence.
50    pub checkpointer: Option<Arc<dyn Checkpointer>>,
51    /// Optional store for runtime tool injection.
52    pub store: Option<Arc<dyn Store>>,
53    /// Maximum input tokens before summarization (default 128,000).
54    pub max_input_tokens: usize,
55    /// Fraction of max_input_tokens that triggers summarization (default 0.85).
56    pub summarization_threshold: f64,
57    /// Token count above which tool results are evicted to files (default 20,000).
58    pub eviction_threshold: usize,
59    /// Maximum nested subagent depth (default 3).
60    pub max_subagent_depth: usize,
61    /// Skills directory path in the backend (default ".skills").
62    pub skills_dir: Option<String>,
63    /// Memory file path in the backend (default "AGENTS.md").
64    pub memory_file: Option<String>,
65    /// Custom subagent definitions for the task tool.
66    pub subagents: Vec<SubAgentDef>,
67    /// Enable subagent spawning via task tool (default true).
68    pub enable_subagents: bool,
69    /// Enable filesystem tools (default true).
70    pub enable_filesystem: bool,
71    /// Enable skills middleware (default true).
72    pub enable_skills: bool,
73    /// Enable memory middleware (default true).
74    pub enable_memory: bool,
75}
76
77impl DeepAgentOptions {
78    /// Create options with the given backend and sensible defaults.
79    pub fn new(backend: Arc<dyn Backend>) -> Self {
80        Self {
81            backend,
82            system_prompt: None,
83            tools: Vec::new(),
84            middleware: Vec::new(),
85            checkpointer: None,
86            store: None,
87            max_input_tokens: 128_000,
88            summarization_threshold: 0.85,
89            eviction_threshold: 20_000,
90            max_subagent_depth: 3,
91            skills_dir: Some(".skills".to_string()),
92            memory_file: Some("AGENTS.md".to_string()),
93            subagents: Vec::new(),
94            enable_subagents: true,
95            enable_filesystem: true,
96            enable_skills: true,
97            enable_memory: true,
98        }
99    }
100}
101
102/// Create a deep agent with the given model and options.
103///
104/// Assembles a middleware stack and tool set:
105/// 1. **DeepMemoryMiddleware** — loads memory file into system prompt
106/// 2. **SkillsMiddleware** — progressive disclosure of skills
107/// 3. **FilesystemMiddleware** — 6–7 filesystem tools + large result eviction
108/// 4. **SubAgentMiddleware** — `task` tool for child agent spawning
109/// 5. **DeepSummarizationMiddleware** — auto-summarize context on overflow
110/// 6. **PatchToolCallsMiddleware** — fix malformed tool calls
111/// 7. User-provided middleware
112#[traceable(skip = "model,options")]
113pub fn create_deep_agent(
114    model: Arc<dyn ChatModel>,
115    options: DeepAgentOptions,
116) -> Result<CompiledGraph<MessageState>, SynapticError> {
117    let mut all_middleware: Vec<Arc<dyn AgentMiddleware>> = Vec::new();
118    let mut all_tools: Vec<Arc<dyn Tool>> = Vec::new();
119
120    // 1. Memory middleware
121    if options.enable_memory {
122        let memory_file = options
123            .memory_file
124            .clone()
125            .unwrap_or_else(|| "AGENTS.md".to_string());
126        all_middleware.push(Arc::new(middleware::memory::DeepMemoryMiddleware::new(
127            options.backend.clone(),
128            memory_file,
129        )));
130    }
131
132    // 2. Skills middleware
133    if options.enable_skills {
134        let skills_dir = options
135            .skills_dir
136            .clone()
137            .unwrap_or_else(|| ".skills".to_string());
138        all_middleware.push(Arc::new(middleware::skills::SkillsMiddleware::new(
139            options.backend.clone(),
140            skills_dir,
141        )));
142    }
143
144    // 3. Filesystem middleware + tools
145    if options.enable_filesystem {
146        let fs_tools = tools::create_filesystem_tools(options.backend.clone());
147        all_tools.extend(fs_tools);
148        all_middleware.push(Arc::new(middleware::filesystem::FilesystemMiddleware::new(
149            options.backend.clone(),
150            options.eviction_threshold,
151        )));
152    }
153
154    // 4. Subagent middleware + task tool
155    if options.enable_subagents {
156        let subagent_mw = middleware::subagent::SubAgentMiddleware::new(
157            options.backend.clone(),
158            model.clone(),
159            options.max_subagent_depth,
160            options.subagents.clone(),
161        );
162        all_tools.push(subagent_mw.create_task_tool());
163    }
164
165    // 5. Summarization middleware
166    all_middleware.push(Arc::new(
167        middleware::summarization::DeepSummarizationMiddleware::new(
168            options.backend.clone(),
169            model.clone(),
170            options.max_input_tokens,
171            options.summarization_threshold,
172        ),
173    ));
174
175    // 6. Patch tool calls middleware
176    all_middleware.push(Arc::new(
177        middleware::patch_tool_calls::PatchToolCallsMiddleware,
178    ));
179
180    // 7. User-provided middleware
181    all_middleware.extend(options.middleware);
182
183    // Add user-provided tools
184    all_tools.extend(options.tools);
185
186    // Build agent options
187    let agent_options = AgentOptions {
188        checkpointer: options.checkpointer,
189        interrupt_before: Vec::new(),
190        interrupt_after: Vec::new(),
191        system_prompt: options.system_prompt,
192        middleware: all_middleware,
193        store: options.store,
194        name: Some("deep_agent".to_string()),
195        pre_model_hook: None,
196        post_model_hook: None,
197        response_format: None,
198    };
199
200    create_agent(model, all_tools, agent_options)
201}