1#![allow(dead_code)]
2
3use crate::error::AgentError;
11use crate::services::model_cost::TokenUsage;
12use crate::types::*;
13use serde_json::Value;
14use std::collections::{HashMap, HashSet};
15use std::sync::{Arc, Mutex};
16use uuid::Uuid;
17
18pub type SDKMessage = Value;
20
21pub struct QueryEngineConfig {
23 pub cwd: String,
24 pub tools: Vec<ToolDefinition>,
25 pub commands: Vec<Value>, pub mcp_clients: Vec<crate::mcp::McpConnection>,
27 pub agents: Vec<Value>, pub initial_messages: Option<Vec<Message>>,
29 pub read_file_cache: Option<FileStateCache>,
30 pub custom_system_prompt: Option<String>,
31 pub append_system_prompt: Option<String>,
32 pub user_specified_model: Option<String>,
33 pub fallback_model: Option<String>,
34 pub thinking_config: Option<ThinkingConfig>,
35 pub max_turns: Option<u32>,
36 pub max_budget_usd: Option<f64>,
37 pub task_budget: Option<TaskBudget>,
38 pub json_schema: Option<Value>,
39 pub verbose: bool,
40 pub replay_user_messages: bool,
41 pub include_partial_messages: bool,
42 pub abort_controller: Option<AbortController>,
43 pub orphaned_permission: Option<OrphanedPermission>,
44}
45
46impl Default for QueryEngineConfig {
47 fn default() -> Self {
48 Self {
49 cwd: String::new(),
50 tools: vec![],
51 commands: vec![],
52 mcp_clients: vec![],
53 agents: vec![],
54 initial_messages: None,
55 read_file_cache: None,
56 custom_system_prompt: None,
57 append_system_prompt: None,
58 user_specified_model: None,
59 fallback_model: None,
60 thinking_config: None,
61 max_turns: None,
62 max_budget_usd: None,
63 task_budget: None,
64 json_schema: None,
65 verbose: false,
66 replay_user_messages: false,
67 include_partial_messages: false,
68 abort_controller: None,
69 orphaned_permission: None,
70 }
71 }
72}
73
74#[derive(Debug, Clone)]
76pub struct TaskBudget {
77 pub total: f64,
78}
79
80#[derive(Debug, Clone)]
82pub struct ThinkingConfig {
83 pub thinking_type: ThinkingType,
84}
85
86#[derive(Debug, Clone)]
87pub enum ThinkingType {
88 Adaptive,
89 Enabled,
90 Disabled,
91}
92
93impl Default for ThinkingConfig {
94 fn default() -> Self {
95 Self {
96 thinking_type: ThinkingType::Adaptive,
97 }
98 }
99}
100
101#[derive(Debug, Clone, PartialEq, Default)]
103pub enum PermissionMode {
104 #[default]
105 Ask,
106 Allow,
107 Deny,
108 Bypass,
109}
110
111#[derive(Debug, Clone)]
113pub struct SDKPermissionDenial {
114 pub tool_name: String,
115 pub tool_use_id: String,
116 pub tool_input: Value,
117}
118
119#[derive(Debug, Clone)]
121pub struct SDKStatus {
122 pub status: String,
123 pub message: Option<String>,
124}
125
126#[derive(Debug, Clone)]
128pub struct AbortController {
129 aborted: Arc<Mutex<bool>>,
130}
131
132impl AbortController {
133 pub fn new() -> Self {
134 Self {
135 aborted: Arc::new(Mutex::new(false)),
136 }
137 }
138
139 pub fn abort(&self) {
140 *self.aborted.lock().unwrap() = true;
141 }
142
143 pub fn is_aborted(&self) -> bool {
144 *self.aborted.lock().unwrap()
145 }
146}
147
148impl Default for AbortController {
149 fn default() -> Self {
150 Self::new()
151 }
152}
153
154#[derive(Debug, Clone, Default)]
156pub struct FileStateCache {
157 pub cache: HashMap<String, Value>,
158}
159
160#[derive(Debug, Clone)]
162pub struct OrphanedPermission {
163 pub tool_name: String,
164 pub tool_input: Value,
165 pub tool_use_id: String,
166}
167
168#[derive(Debug, Clone)]
170pub struct ElicitationRequest {
171 pub tool_name: String,
172 pub message: String,
173 pub url: Option<String>,
174}
175
176#[derive(Debug, Clone)]
178pub struct ElicitationResponse {
179 pub url: Option<String>,
180 pub selection: Option<String>,
181}
182
183#[derive(Debug, Clone)]
185pub struct SnipResult {
186 pub messages: Vec<SDKMessage>,
187 pub executed: bool,
188}
189
190pub struct ToolUseContext {
192 pub cwd: String,
193 pub session_id: String,
194 pub agent_id: Option<String>,
195 pub query_tracking: Option<QueryTracking>,
196 pub options: ToolUseContextOptions,
197 pub abort_controller: AbortController,
198 pub read_file_state: FileStateCache,
199}
200
201pub struct ToolUseContextOptions {
202 pub commands: Vec<Value>,
203 pub debug: bool,
204 pub tools: Vec<ToolDefinition>,
205 pub verbose: bool,
206 pub main_loop_model: Option<String>,
207 pub thinking_config: Option<ThinkingConfig>,
208 pub mcp_clients: Vec<crate::mcp::McpConnection>,
209 pub mcp_resources: HashMap<String, Value>,
210 pub ide_installation_status: Option<Value>,
211 pub is_non_interactive_session: bool,
212 pub custom_system_prompt: Option<String>,
213 pub append_system_prompt: Option<String>,
214 pub agent_definitions: AgentDefinitions,
215 pub theme: Option<String>,
216 pub max_budget_usd: Option<f64>,
217}
218
219impl Default for ToolUseContextOptions {
220 fn default() -> Self {
221 Self {
222 commands: vec![],
223 debug: false,
224 tools: vec![],
225 verbose: false,
226 main_loop_model: None,
227 thinking_config: None,
228 mcp_clients: vec![],
229 mcp_resources: HashMap::new(),
230 ide_installation_status: None,
231 is_non_interactive_session: false,
232 custom_system_prompt: None,
233 append_system_prompt: None,
234 agent_definitions: AgentDefinitions::default(),
235 theme: None,
236 max_budget_usd: None,
237 }
238 }
239}
240
241#[derive(Debug, Clone, Default)]
242pub struct AgentDefinitions {
243 pub active_agents: Vec<Value>,
244 pub all_agents: Vec<Value>,
245 pub allowed_agent_types: Option<Vec<String>>,
246}
247
248#[derive(Debug, Clone)]
250pub struct QueryTracking {
251 pub chain_id: String,
252 pub depth: u32,
253}
254
255pub type CanUseToolFn = dyn Fn(
257 &ToolDefinition,
258 &Value,
259 &ToolUseContext,
260 &Option<Message>,
261 &str,
262 Option<bool>,
263 )
264 -> std::pin::Pin<Box<dyn std::future::Future<Output = PermissionDecision> + Send + Sync>>
265 + Send
266 + Sync;
267
268pub type HandleElicitationFn = Box<
270 dyn Fn(
271 ElicitationRequest,
272 ) -> std::pin::Pin<
273 Box<dyn std::future::Future<Output = Option<ElicitationResponse>> + Send + Sync>,
274 > + Send
275 + Sync,
276>;
277
278pub type SetSdkStatusFn = Box<dyn Fn(SDKStatus) + Send + Sync>;
280
281pub type SnipReplayFn = Box<dyn Fn(&Message, &[Message]) -> Option<SnipResult> + Send + Sync>;
283
284#[derive(Debug, Clone)]
286pub enum PermissionDecision {
287 Allow,
288 Deny { reason: Option<String> },
289 Ask { expires_at: Option<u64> },
290}
291
292#[derive(Debug, Clone, Default)]
294pub struct AppState {
295 pub tool_permission_context: ToolPermissionContext,
296 pub fast_mode: bool,
297 pub file_history: Value,
298 pub attribution: Value,
299 pub mcp: McpState,
300 pub effort_value: Option<f64>,
301 pub advisor_model: Option<String>,
302}
303
304#[derive(Debug, Clone, Default)]
305pub struct ToolPermissionContext {
306 pub mode: PermissionMode,
307 pub always_allow_rules: AlwaysAllowRules,
308 pub additional_working_directories: HashMap<String, String>,
309}
310
311#[derive(Debug, Clone, Default)]
312pub struct AlwaysAllowRules {
313 pub command: Vec<String>,
314}
315
316#[derive(Debug, Clone, Default)]
317pub struct McpState {
318 pub tools: Vec<Value>,
319 pub clients: Vec<McpClient>,
320}
321
322#[derive(Debug, Clone)]
323pub struct McpClient {
324 pub name: String,
325 pub client_type: String, }
327
328#[derive(Debug, Clone, Default)]
330pub struct NonNullableUsage {
331 pub input_tokens: u64,
332 pub output_tokens: u64,
333 pub cache_creation_input_tokens: Option<u64>,
334 pub cache_read_input_tokens: Option<u64>,
335}
336
337impl From<TokenUsage> for NonNullableUsage {
338 fn from(usage: TokenUsage) -> Self {
339 Self {
340 input_tokens: usage.input_tokens as u64,
341 output_tokens: usage.output_tokens as u64,
342 cache_creation_input_tokens: Some(usage.prompt_cache_write_tokens as u64),
343 cache_read_input_tokens: Some(usage.prompt_cache_read_tokens as u64),
344 }
345 }
346}
347
348#[derive(Debug, Default)]
350pub struct SubmitOptions {
351 pub uuid: Option<String>,
352 pub is_meta: Option<bool>,
353}
354
355pub async fn ask(config: AskConfig) -> Result<Vec<SDKMessage>, AgentError> {
358 let initial_messages: Option<Vec<Message>> = config.mutable_messages.map(|msgs| {
360 msgs.into_iter()
361 .map(|v| Message {
362 role: MessageRole::User,
363 content: v.to_string(),
364 attachments: None,
365 tool_call_id: None,
366 tool_calls: None,
367 is_error: None,
368 })
369 .collect()
370 });
371
372 let engine = QueryEngine::new(QueryEngineConfig {
373 cwd: config.cwd,
374 tools: config.tools,
375 commands: vec![],
376 mcp_clients: config.mcp_clients.unwrap_or_default(),
377 agents: config.agents.unwrap_or_default(),
378 initial_messages,
379 read_file_cache: None,
380 custom_system_prompt: config.custom_system_prompt,
381 append_system_prompt: config.append_system_prompt,
382 user_specified_model: config.user_specified_model,
383 fallback_model: config.fallback_model,
384 thinking_config: config.thinking_config,
385 max_turns: config.max_turns,
386 max_budget_usd: config.max_budget_usd,
387 task_budget: config.task_budget,
388 json_schema: config.json_schema,
389 verbose: config.verbose.unwrap_or(false),
390 replay_user_messages: config.replay_user_messages.unwrap_or(false),
391 include_partial_messages: config.include_partial_messages.unwrap_or(false),
392 abort_controller: config.abort_controller,
393 orphaned_permission: config.orphaned_permission,
394 });
395
396 let messages: Vec<SDKMessage> = engine
399 .get_messages()
400 .iter()
401 .map(|m| {
402 serde_json::json!({
403 "role": format!("{:?}", m.role),
404 "content": m.content,
405 })
406 })
407 .collect();
408 Ok(messages)
409}
410
411pub struct AskConfig {
413 pub prompt: String,
414 pub prompt_uuid: Option<String>,
415 pub is_meta: Option<bool>,
416 pub cwd: String,
417 pub tools: Vec<ToolDefinition>,
418 pub mcp_clients: Option<Vec<crate::mcp::McpConnection>>,
419 pub verbose: Option<bool>,
420 pub thinking_config: Option<ThinkingConfig>,
421 pub max_turns: Option<u32>,
422 pub max_budget_usd: Option<f64>,
423 pub task_budget: Option<TaskBudget>,
424 pub mutable_messages: Option<Vec<SDKMessage>>,
425 pub custom_system_prompt: Option<String>,
426 pub append_system_prompt: Option<String>,
427 pub user_specified_model: Option<String>,
428 pub fallback_model: Option<String>,
429 pub json_schema: Option<Value>,
430 pub abort_controller: Option<AbortController>,
431 pub replay_user_messages: Option<bool>,
432 pub include_partial_messages: Option<bool>,
433 pub agents: Option<Vec<Value>>,
434 pub orphaned_permission: Option<OrphanedPermission>,
435}
436
437impl Default for AskConfig {
438 fn default() -> Self {
439 Self {
440 prompt: String::new(),
441 prompt_uuid: None,
442 is_meta: None,
443 cwd: String::new(),
444 tools: vec![],
445 mcp_clients: None,
446 verbose: None,
447 thinking_config: None,
448 max_turns: None,
449 max_budget_usd: None,
450 task_budget: None,
451 mutable_messages: None,
452 custom_system_prompt: None,
453 append_system_prompt: None,
454 user_specified_model: None,
455 fallback_model: None,
456 json_schema: None,
457 abort_controller: None,
458 replay_user_messages: None,
459 include_partial_messages: None,
460 agents: None,
461 orphaned_permission: None,
462 }
463 }
464}
465
466pub struct QueryEngine {
468 config: QueryEngineConfig,
469 mutable_messages: Vec<Message>,
470 abort_controller: AbortController,
471 permission_denials: Vec<SDKPermissionDenial>,
472 total_usage: NonNullableUsage,
473 has_handled_orphaned_permission: bool,
474 read_file_state: FileStateCache,
475 discovered_skill_names: HashSet<String>,
476 loaded_nested_memory_paths: HashSet<String>,
477}
478
479impl QueryEngine {
480 pub fn new(config: QueryEngineConfig) -> Self {
481 Self {
482 config,
483 mutable_messages: vec![],
484 abort_controller: AbortController::new(),
485 permission_denials: vec![],
486 total_usage: NonNullableUsage::default(),
487 has_handled_orphaned_permission: false,
488 read_file_state: FileStateCache::default(),
489 discovered_skill_names: HashSet::new(),
490 loaded_nested_memory_paths: HashSet::new(),
491 }
492 }
493
494 pub fn interrupt(&mut self) {
495 self.abort_controller.abort();
496 }
497
498 pub fn get_messages(&self) -> &Vec<Message> {
499 &self.mutable_messages
500 }
501
502 pub fn get_read_file_state(&self) -> &FileStateCache {
503 &self.read_file_state
504 }
505
506 pub fn get_session_id(&self) -> String {
507 Uuid::new_v4().to_string()
508 }
509
510 pub fn set_model(&mut self, model: String) {
511 self.config.user_specified_model = Some(model);
512 }
513}