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