diff --git a/rust/crates/api/src/client.rs b/rust/crates/api/src/client.rs index 8a9c286..b596777 100644 --- a/rust/crates/api/src/client.rs +++ b/rust/crates/api/src/client.rs @@ -1,5 +1,5 @@ use crate::error::ApiError; -use crate::providers::claw_provider::{self, ClawApiClient, AuthSource}; +use crate::providers::claw_provider::{self, AuthSource, ClawApiClient}; use crate::providers::openai_compat::{self, OpenAiCompatClient, OpenAiCompatConfig}; use crate::providers::{self, Provider, ProviderKind}; use crate::types::{MessageRequest, MessageResponse, StreamEvent}; diff --git a/rust/crates/api/src/lib.rs b/rust/crates/api/src/lib.rs index 2b2584a..3306f53 100644 --- a/rust/crates/api/src/lib.rs +++ b/rust/crates/api/src/lib.rs @@ -9,7 +9,7 @@ pub use client::{ resolve_startup_auth_source, MessageStream, OAuthTokenSet, ProviderClient, }; pub use error::ApiError; -pub use providers::claw_provider::{ClawApiClient, ClawApiClient as ApiClient, AuthSource}; +pub use providers::claw_provider::{AuthSource, ClawApiClient, ClawApiClient as ApiClient}; pub use providers::openai_compat::{OpenAiCompatClient, OpenAiCompatConfig}; pub use providers::{ detect_provider_kind, max_tokens_for_model, resolve_model_alias, ProviderKind, diff --git a/rust/crates/api/src/providers/claw_provider.rs b/rust/crates/api/src/providers/claw_provider.rs index 55d7f37..d9046cd 100644 --- a/rust/crates/api/src/providers/claw_provider.rs +++ b/rust/crates/api/src/providers/claw_provider.rs @@ -652,7 +652,7 @@ mod tests { use super::{ now_unix_timestamp, oauth_token_is_expired, resolve_saved_oauth_token, - resolve_startup_auth_source, ClawApiClient, AuthSource, OAuthTokenSet, + resolve_startup_auth_source, AuthSource, ClawApiClient, OAuthTokenSet, }; use crate::types::{ContentBlockDelta, MessageRequest}; diff --git a/rust/crates/api/tests/client_integration.rs b/rust/crates/api/tests/client_integration.rs index ae810b8..3b6a3c3 100644 --- a/rust/crates/api/tests/client_integration.rs +++ b/rust/crates/api/tests/client_integration.rs @@ -290,8 +290,7 @@ async fn live_stream_smoke_test() { let client = ApiClient::from_env().expect("ANTHROPIC_API_KEY must be set"); let mut stream = client .stream_message(&MessageRequest { - model: std::env::var("CLAW_MODEL") - .unwrap_or_else(|_| "claude-sonnet-4-6".to_string()), + model: std::env::var("CLAW_MODEL").unwrap_or_else(|_| "claude-sonnet-4-6".to_string()), max_tokens: 32, messages: vec![InputMessage::user_text( "Reply with exactly: hello from rust", diff --git a/rust/crates/claw-cli/src/init.rs b/rust/crates/claw-cli/src/init.rs index 69095cd..f4db53a 100644 --- a/rust/crates/claw-cli/src/init.rs +++ b/rust/crates/claw-cli/src/init.rs @@ -386,8 +386,7 @@ mod tests { let root = temp_dir(); fs::create_dir_all(&root).expect("create root"); fs::write(root.join("CLAW.md"), "custom guidance\n").expect("write existing claw md"); - fs::write(root.join(".gitignore"), ".claw/settings.local.json\n") - .expect("write gitignore"); + fs::write(root.join(".gitignore"), ".claw/settings.local.json\n").expect("write gitignore"); let first = initialize_repo(&root).expect("first init should succeed"); assert!(first diff --git a/rust/crates/claw-cli/src/main.rs b/rust/crates/claw-cli/src/main.rs index 0abaad5..d0b90f7 100644 --- a/rust/crates/claw-cli/src/main.rs +++ b/rust/crates/claw-cli/src/main.rs @@ -16,7 +16,7 @@ use std::thread; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use api::{ - resolve_startup_auth_source, ClawApiClient, AuthSource, ContentBlockDelta, InputContentBlock, + resolve_startup_auth_source, AuthSource, ClawApiClient, ContentBlockDelta, InputContentBlock, InputMessage, MessageRequest, MessageResponse, OutputContentBlock, StreamEvent as ApiStreamEvent, ToolChoice, ToolDefinition, ToolResultContentBlock, }; @@ -2037,8 +2037,7 @@ fn render_memory_report() -> Result> { if project_context.instruction_files.is_empty() { lines.push("Discovered files".to_string()); lines.push( - " No CLAW instruction files discovered in the current directory ancestry." - .to_string(), + " No CLAW instruction files discovered in the current directory ancestry.".to_string(), ); } else { lines.push("Discovered files".to_string()); @@ -2790,7 +2789,8 @@ fn build_runtime( allowed_tools: Option, permission_mode: PermissionMode, progress_reporter: Option, -) -> Result, Box> { +) -> Result, Box> +{ let (feature_config, tool_registry) = build_runtime_plugin_state()?; Ok(ConversationRuntime::new_with_features( session, diff --git a/rust/crates/runtime/src/lib.rs b/rust/crates/runtime/src/lib.rs index 9dcb239..a610f04 100644 --- a/rust/crates/runtime/src/lib.rs +++ b/rust/crates/runtime/src/lib.rs @@ -24,8 +24,8 @@ pub use compact::{ get_compact_continuation_message, should_compact, CompactionConfig, CompactionResult, }; pub use config::{ - ConfigEntry, ConfigError, ConfigLoader, ConfigSource, McpManagedProxyServerConfig, - McpConfigCollection, McpOAuthConfig, McpRemoteServerConfig, McpSdkServerConfig, + ConfigEntry, ConfigError, ConfigLoader, ConfigSource, McpConfigCollection, + McpManagedProxyServerConfig, McpOAuthConfig, McpRemoteServerConfig, McpSdkServerConfig, McpServerConfig, McpStdioServerConfig, McpTransport, McpWebSocketServerConfig, OAuthConfig, ResolvedPermissionMode, RuntimeConfig, RuntimeFeatureConfig, RuntimeHookConfig, RuntimePluginConfig, ScopedMcpServerConfig, CLAW_SETTINGS_SCHEMA_NAME, @@ -45,7 +45,7 @@ pub use mcp::{ scoped_mcp_config_hash, unwrap_ccr_proxy_url, }; pub use mcp_client::{ - McpManagedProxyTransport, McpClientAuth, McpClientBootstrap, McpClientTransport, + McpClientAuth, McpClientBootstrap, McpClientTransport, McpManagedProxyTransport, McpRemoteTransport, McpSdkTransport, McpStdioTransport, }; pub use mcp_stdio::{ diff --git a/rust/crates/runtime/src/mcp_client.rs b/rust/crates/runtime/src/mcp_client.rs index 0e94391..e0e1f2c 100644 --- a/rust/crates/runtime/src/mcp_client.rs +++ b/rust/crates/runtime/src/mcp_client.rs @@ -97,12 +97,10 @@ impl McpClientTransport { McpServerConfig::Sdk(config) => Self::Sdk(McpSdkTransport { name: config.name.clone(), }), - McpServerConfig::ManagedProxy(config) => { - Self::ManagedProxy(McpManagedProxyTransport { - url: config.url.clone(), - id: config.id.clone(), - }) - } + McpServerConfig::ManagedProxy(config) => Self::ManagedProxy(McpManagedProxyTransport { + url: config.url.clone(), + id: config.id.clone(), + }), } } } diff --git a/rust/crates/runtime/src/mcp_stdio.rs b/rust/crates/runtime/src/mcp_stdio.rs index 9c8d346..27402d6 100644 --- a/rust/crates/runtime/src/mcp_stdio.rs +++ b/rust/crates/runtime/src/mcp_stdio.rs @@ -1163,8 +1163,12 @@ mod tests { } fn cleanup_script(script_path: &Path) { - fs::remove_file(script_path).expect("cleanup script"); - fs::remove_dir_all(script_path.parent().expect("script parent")).expect("cleanup dir"); + if let Err(error) = fs::remove_file(script_path) { + assert_eq!(error.kind(), std::io::ErrorKind::NotFound, "cleanup script"); + } + if let Err(error) = fs::remove_dir_all(script_path.parent().expect("script parent")) { + assert_eq!(error.kind(), std::io::ErrorKind::NotFound, "cleanup dir"); + } } fn manager_server_config( diff --git a/rust/crates/runtime/src/session.rs b/rust/crates/runtime/src/session.rs index beaa435..ec37070 100644 --- a/rust/crates/runtime/src/session.rs +++ b/rust/crates/runtime/src/session.rs @@ -3,10 +3,13 @@ use std::fmt::{Display, Formatter}; use std::fs; use std::path::Path; +use serde::{Deserialize, Serialize}; + use crate::json::{JsonError, JsonValue}; use crate::usage::TokenUsage; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] pub enum MessageRole { System, User, @@ -14,7 +17,8 @@ pub enum MessageRole { Tool, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(tag = "type", rename_all = "snake_case")] pub enum ContentBlock { Text { text: String, @@ -32,14 +36,14 @@ pub enum ContentBlock { }, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct ConversationMessage { pub role: MessageRole, pub blocks: Vec, pub usage: Option, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Session { pub version: u32, pub messages: Vec, diff --git a/rust/crates/runtime/src/usage.rs b/rust/crates/runtime/src/usage.rs index 7e5bfcb..0570bc1 100644 --- a/rust/crates/runtime/src/usage.rs +++ b/rust/crates/runtime/src/usage.rs @@ -1,4 +1,5 @@ use crate::session::Session; +use serde::{Deserialize, Serialize}; const DEFAULT_INPUT_COST_PER_MILLION: f64 = 15.0; const DEFAULT_OUTPUT_COST_PER_MILLION: f64 = 75.0; @@ -25,7 +26,7 @@ impl ModelPricing { } } -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)] pub struct TokenUsage { pub input_tokens: u32, pub output_tokens: u32, diff --git a/rust/crates/tools/src/lib.rs b/rust/crates/tools/src/lib.rs index b5fab79..9da2097 100644 --- a/rust/crates/tools/src/lib.rs +++ b/rust/crates/tools/src/lib.rs @@ -91,7 +91,10 @@ impl GlobalToolRegistry { Ok(Self { plugin_tools }) } - pub fn normalize_allowed_tools(&self, values: &[String]) -> Result>, String> { + pub fn normalize_allowed_tools( + &self, + values: &[String], + ) -> Result>, String> { if values.is_empty() { return Ok(None); } @@ -100,7 +103,11 @@ impl GlobalToolRegistry { let canonical_names = builtin_specs .iter() .map(|spec| spec.name.to_string()) - .chain(self.plugin_tools.iter().map(|tool| tool.definition().name.clone())) + .chain( + self.plugin_tools + .iter() + .map(|tool| tool.definition().name.clone()), + ) .collect::>(); let mut name_map = canonical_names .iter() @@ -151,7 +158,8 @@ impl GlobalToolRegistry { .plugin_tools .iter() .filter(|tool| { - allowed_tools.is_none_or(|allowed| allowed.contains(tool.definition().name.as_str())) + allowed_tools + .is_none_or(|allowed| allowed.contains(tool.definition().name.as_str())) }) .map(|tool| ToolDefinition { name: tool.definition().name.clone(), @@ -174,7 +182,8 @@ impl GlobalToolRegistry { .plugin_tools .iter() .filter(|tool| { - allowed_tools.is_none_or(|allowed| allowed.contains(tool.definition().name.as_str())) + allowed_tools + .is_none_or(|allowed| allowed.contains(tool.definition().name.as_str())) }) .map(|tool| { (