diff --git a/crates/llm-worker/examples/record_test_fixtures/main.rs b/crates/llm-worker/examples/record_test_fixtures/main.rs index 1ae80e1c..c6262336 100644 --- a/crates/llm-worker/examples/record_test_fixtures/main.rs +++ b/crates/llm-worker/examples/record_test_fixtures/main.rs @@ -25,11 +25,7 @@ use llm_worker::llm_client::scheme::{ }; use llm_worker::llm_client::transport::{HttpTransport, ResolvedAuth}; -fn make_transport( - scheme: S, - model: &str, - auth: ResolvedAuth, -) -> HttpTransport { +fn make_transport(scheme: S, model: &str, auth: ResolvedAuth) -> HttpTransport { let cap = scheme.default_capability(); let base_url = scheme.default_base_url().to_string(); HttpTransport::new(scheme, model.to_string(), base_url, auth, cap) @@ -71,11 +67,7 @@ async fn run_scenario_with_anthropic( let api_key = std::env::var("ANTHROPIC_API_KEY") .expect("ANTHROPIC_API_KEY environment variable must be set"); let model = model.as_deref().unwrap_or("claude-sonnet-4-20250514"); - let client = make_transport( - AnthropicScheme::new(), - model, - ResolvedAuth::ApiKey(api_key), - ); + let client = make_transport(AnthropicScheme::new(), model, ResolvedAuth::ApiKey(api_key)); recorder::record_request( &client, diff --git a/crates/llm-worker/examples/worker_cli.rs b/crates/llm-worker/examples/worker_cli.rs index 3b46d15f..5f692f98 100644 --- a/crates/llm-worker/examples/worker_cli.rs +++ b/crates/llm-worker/examples/worker_cli.rs @@ -338,11 +338,7 @@ fn default_capability() -> ModelCapability { } } -fn build_transport( - scheme: S, - model: String, - auth: ResolvedAuth, -) -> Box { +fn build_transport(scheme: S, model: String, auth: ResolvedAuth) -> Box { let cap = scheme.default_capability(); let base_url = scheme.default_base_url().to_string(); Box::new(HttpTransport::new(scheme, model, base_url, auth, cap)) diff --git a/crates/llm-worker/src/llm_client/scheme/anthropic/request.rs b/crates/llm-worker/src/llm_client/scheme/anthropic/request.rs index 322d6043..12cce691 100644 --- a/crates/llm-worker/src/llm_client/scheme/anthropic/request.rs +++ b/crates/llm-worker/src/llm_client/scheme/anthropic/request.rs @@ -7,9 +7,9 @@ use std::collections::BTreeSet; use serde::Serialize; use crate::llm_client::{ - capability::{CacheStrategy, ModelCapability, ReasoningControl, ReasoningSupport}, - types::{parse_tool_arguments, ContentPart, Item, Role, ToolDefinition}, Request, + capability::{CacheStrategy, ModelCapability, ReasoningControl, ReasoningSupport}, + types::{ContentPart, Item, Role, ToolDefinition, parse_tool_arguments}, }; use super::AnthropicScheme; @@ -600,7 +600,7 @@ mod tests { let scheme = AnthropicScheme::new(); let mut items = completed_turn(); items.push(Item::user_message("next turn")); // index 5 = latest user - // cache_anchor=None, turn_end=4, head=5. + // cache_anchor=None, turn_end=4, head=5. let request = Request::new().items(items); let req = scheme.build_request("claude-sonnet-4-20250514", &request, &cap_explicit()); diff --git a/crates/llm-worker/src/llm_client/scheme/anthropic/scheme_impl.rs b/crates/llm-worker/src/llm_client/scheme/anthropic/scheme_impl.rs index 6e260e59..fff8a73a 100644 --- a/crates/llm-worker/src/llm_client/scheme/anthropic/scheme_impl.rs +++ b/crates/llm-worker/src/llm_client/scheme/anthropic/scheme_impl.rs @@ -7,9 +7,9 @@ use serde_json::Value; use crate::llm_client::{ ClientError, + auth::AuthRequirement, capability::ModelCapability, event::{BlockStop, BlockType, Event}, - auth::AuthRequirement, scheme::Scheme, types::Request, }; diff --git a/crates/llm-worker/src/llm_client/scheme/gemini/request.rs b/crates/llm-worker/src/llm_client/scheme/gemini/request.rs index e871e4f4..e2fac917 100644 --- a/crates/llm-worker/src/llm_client/scheme/gemini/request.rs +++ b/crates/llm-worker/src/llm_client/scheme/gemini/request.rs @@ -6,9 +6,9 @@ use serde::Serialize; use serde_json::Value; use crate::llm_client::{ - capability::{ModelCapability, ReasoningControl, ReasoningSupport}, - types::{parse_tool_arguments, Item, Role, ToolDefinition}, Request, + capability::{ModelCapability, ReasoningControl, ReasoningSupport}, + types::{Item, Role, ToolDefinition, parse_tool_arguments}, }; use super::GeminiScheme; diff --git a/crates/llm-worker/src/llm_client/scheme/gemini/scheme_impl.rs b/crates/llm-worker/src/llm_client/scheme/gemini/scheme_impl.rs index f50db4dd..5d5724ce 100644 --- a/crates/llm-worker/src/llm_client/scheme/gemini/scheme_impl.rs +++ b/crates/llm-worker/src/llm_client/scheme/gemini/scheme_impl.rs @@ -3,11 +3,7 @@ use serde_json::Value; use crate::llm_client::{ - ClientError, - capability::ModelCapability, - event::Event, - auth::AuthRequirement, - scheme::Scheme, + ClientError, auth::AuthRequirement, capability::ModelCapability, event::Event, scheme::Scheme, types::Request, }; diff --git a/crates/llm-worker/src/llm_client/scheme/mod.rs b/crates/llm-worker/src/llm_client/scheme/mod.rs index 2afd010b..ecea20c2 100644 --- a/crates/llm-worker/src/llm_client/scheme/mod.rs +++ b/crates/llm-worker/src/llm_client/scheme/mod.rs @@ -90,4 +90,3 @@ pub trait Scheme: Clone + Send + Sync + 'static { Vec::new() } } - diff --git a/crates/llm-worker/src/llm_client/scheme/openai_chat/request.rs b/crates/llm-worker/src/llm_client/scheme/openai_chat/request.rs index 6b033a9a..4302cc2f 100644 --- a/crates/llm-worker/src/llm_client/scheme/openai_chat/request.rs +++ b/crates/llm-worker/src/llm_client/scheme/openai_chat/request.rs @@ -6,9 +6,9 @@ use serde::Serialize; use serde_json::Value; use crate::llm_client::{ - capability::{ModelCapability, ReasoningControl, ReasoningSupport}, - types::{parse_tool_arguments, Item, Role, ToolDefinition}, Request, + capability::{ModelCapability, ReasoningControl, ReasoningSupport}, + types::{Item, Role, ToolDefinition, parse_tool_arguments}, }; use super::OpenAIScheme; diff --git a/crates/llm-worker/src/llm_client/scheme/openai_responses/events.rs b/crates/llm-worker/src/llm_client/scheme/openai_responses/events.rs index 78bab0eb..0b011442 100644 --- a/crates/llm-worker/src/llm_client/scheme/openai_responses/events.rs +++ b/crates/llm-worker/src/llm_client/scheme/openai_responses/events.rs @@ -38,11 +38,7 @@ impl OpenAIResponsesState { /// 既存 slot を取得。無ければ `block_type` で暗黙に確保し、 /// 新規確保したかを併せて返す。delta 先行 / content_part.added が /// 抜けたときの防御。 - fn get_or_allocate( - &mut self, - key: SlotKey, - block_type: BlockType, - ) -> (SlotInfo, bool) { + fn get_or_allocate(&mut self, key: SlotKey, block_type: BlockType) -> (SlotInfo, bool) { if let Some(info) = self.slots.get(&key).copied() { (info, false) } else { @@ -303,15 +299,12 @@ pub(crate) fn parse_sse( match ev.item { OutputItem::FunctionCall { call_id, name, .. } | OutputItem::CustomToolCall { call_id, name, .. } => { - let info = state - .allocate(SlotKey::OutputItem(ev.output_index), BlockType::ToolUse); + let info = + state.allocate(SlotKey::OutputItem(ev.output_index), BlockType::ToolUse); Ok(vec![Event::BlockStart(BlockStart { index: info.flat_index, block_type: BlockType::ToolUse, - metadata: BlockMetadata::ToolUse { - id: call_id, - name, - }, + metadata: BlockMetadata::ToolUse { id: call_id, name }, })]) } _ => Ok(Vec::new()), @@ -530,11 +523,7 @@ mod tests { (events, state) } - fn with( - state: &mut OpenAIResponsesState, - event_type: &str, - data: &str, - ) -> Vec { + fn with(state: &mut OpenAIResponsesState, event_type: &str, data: &str) -> Vec { parse_sse(event_type, data, state).unwrap() } @@ -551,7 +540,8 @@ mod tests { #[test] fn completed_emits_usage_and_status() { - let data = r#"{"response":{"usage":{"input_tokens":10,"output_tokens":20,"total_tokens":30}}}"#; + let data = + r#"{"response":{"usage":{"input_tokens":10,"output_tokens":20,"total_tokens":30}}}"#; let (events, _) = run("response.completed", data); assert!(matches!(events[0], Event::Usage(_))); assert!(matches!( @@ -761,8 +751,7 @@ mod tests { #[test] fn failed_response_emits_error_and_status() { - let data = - r#"{"response":{"error":{"type":"invalid_request_error","message":"bad"}}}"#; + let data = r#"{"response":{"error":{"type":"invalid_request_error","message":"bad"}}}"#; let (events, _) = run("response.failed", data); assert_eq!(events.len(), 2); assert!(matches!(events[0], Event::Error(_))); diff --git a/crates/llm-worker/src/llm_client/scheme/openai_responses/scheme_impl.rs b/crates/llm-worker/src/llm_client/scheme/openai_responses/scheme_impl.rs index 80a4b6a0..e9f3f73a 100644 --- a/crates/llm-worker/src/llm_client/scheme/openai_responses/scheme_impl.rs +++ b/crates/llm-worker/src/llm_client/scheme/openai_responses/scheme_impl.rs @@ -3,11 +3,7 @@ use serde_json::Value; use crate::llm_client::{ - ClientError, - auth::AuthRequirement, - capability::ModelCapability, - event::Event, - scheme::Scheme, + ClientError, auth::AuthRequirement, capability::ModelCapability, event::Event, scheme::Scheme, types::Request, }; diff --git a/crates/llm-worker/src/llm_client/transport.rs b/crates/llm-worker/src/llm_client/transport.rs index 4ea61593..42a0a3c4 100644 --- a/crates/llm-worker/src/llm_client/transport.rs +++ b/crates/llm-worker/src/llm_client/transport.rs @@ -46,7 +46,9 @@ impl ResolvedAuth { (Self::Custom(_), _) => true, ( Self::ApiKey(_), - AuthRequirement::Bearer | AuthRequirement::XApiKey | AuthRequirement::QueryParam { .. }, + AuthRequirement::Bearer + | AuthRequirement::XApiKey + | AuthRequirement::QueryParam { .. }, ) => true, _ => false, } diff --git a/crates/llm-worker/src/worker.rs b/crates/llm-worker/src/worker.rs index 770daca1..17a1542b 100644 --- a/crates/llm-worker/src/worker.rs +++ b/crates/llm-worker/src/worker.rs @@ -568,9 +568,7 @@ impl Worker { // Attach the cache prefix anchor (may be narrower than `context` // if the prune projection trimmed items from the head — keep it // in range). - request.cache_anchor = self - .cache_anchor - .filter(|&anchor| anchor < context.len()); + request.cache_anchor = self.cache_anchor.filter(|&anchor| anchor < context.len()); request } diff --git a/crates/manifest/src/cascade.rs b/crates/manifest/src/cascade.rs index 5f5f1463..7c11c8dc 100644 --- a/crates/manifest/src/cascade.rs +++ b/crates/manifest/src/cascade.rs @@ -121,5 +121,4 @@ name = "from-disk" _ => panic!("expected Io variant"), } } - } diff --git a/crates/manifest/src/paths.rs b/crates/manifest/src/paths.rs index 46107f47..081ce3d5 100644 --- a/crates/manifest/src/paths.rs +++ b/crates/manifest/src/paths.rs @@ -161,10 +161,7 @@ mod tests { "XDG_RUNTIME_DIR", "HOME", ]; - let saved: Vec<_> = names - .iter() - .map(|n| (*n, std::env::var(n).ok())) - .collect(); + let saved: Vec<_> = names.iter().map(|n| (*n, std::env::var(n).ok())).collect(); // SAFETY: env_lock() 取得済みなので env への並行アクセスは // この test バイナリ内では発生しない。 unsafe { @@ -206,10 +203,7 @@ mod tests { #[test] fn config_dir_uses_xdg_when_set() { - let _g = EnvGuard::new(&[ - ("HOME", Some("/h")), - ("XDG_CONFIG_HOME", Some("/x")), - ]); + let _g = EnvGuard::new(&[("HOME", Some("/h")), ("XDG_CONFIG_HOME", Some("/x"))]); assert_eq!(config_dir().unwrap(), PathBuf::from("/x/insomnia")); } @@ -241,10 +235,7 @@ mod tests { #[test] fn data_dir_insomnia_home_is_data_dir_itself() { - let _g = EnvGuard::new(&[ - ("HOME", Some("/h")), - ("INSOMNIA_HOME", Some("/sand")), - ]); + let _g = EnvGuard::new(&[("HOME", Some("/h")), ("INSOMNIA_HOME", Some("/sand"))]); assert_eq!(data_dir().unwrap(), PathBuf::from("/sand")); } @@ -278,10 +269,7 @@ mod tests { #[test] fn empty_env_treated_as_unset() { - let _g = EnvGuard::new(&[ - ("HOME", Some("/h")), - ("XDG_CONFIG_HOME", Some("")), - ]); + let _g = EnvGuard::new(&[("HOME", Some("/h")), ("XDG_CONFIG_HOME", Some(""))]); assert_eq!(config_dir().unwrap(), PathBuf::from("/h/.config/insomnia")); } @@ -312,10 +300,7 @@ mod tests { user_catalog_override("providers.toml").unwrap(), PathBuf::from("/sand/config/providers.toml") ); - assert_eq!( - sessions_dir().unwrap(), - PathBuf::from("/sand/sessions") - ); + assert_eq!(sessions_dir().unwrap(), PathBuf::from("/sand/sessions")); assert_eq!( scope_lock_path().unwrap(), PathBuf::from("/sand/run/scope.lock") diff --git a/crates/memory/src/error.rs b/crates/memory/src/error.rs index bde61dad..025c57b1 100644 --- a/crates/memory/src/error.rs +++ b/crates/memory/src/error.rs @@ -61,13 +61,17 @@ pub enum LintError { #[error("Decisions `status` must be one of open|resolved|replaced (got `{0}`)")] InvalidStatus(String), - #[error("Knowledge with model_invokation: true cannot have description longer than {limit} chars (got {actual})")] + #[error( + "Knowledge with model_invokation: true cannot have description longer than {limit} chars (got {actual})" + )] DescriptionTooLong { actual: usize, limit: usize }, #[error("body exceeds the size limit for this record kind: {actual} chars > {limit}")] BodyTooLong { actual: usize, limit: usize }, - #[error("write to `memory/workflow/` is forbidden via the memory tool — Workflows are human-edited")] + #[error( + "write to `memory/workflow/` is forbidden via the memory tool — Workflows are human-edited" + )] WorkflowWriteForbidden, #[error("slug `{0}` already exists; use the edit tool instead of creating a new record")] diff --git a/crates/memory/src/linter/mod.rs b/crates/memory/src/linter/mod.rs index cf95c91b..943695a2 100644 --- a/crates/memory/src/linter/mod.rs +++ b/crates/memory/src/linter/mod.rs @@ -208,23 +208,13 @@ impl Linter { report.push_error(LintError::ReplacedBySelf); } } - references::check_replaced_by( - cp.slug.as_ref(), - target, - existing, - report, - ); + references::check_replaced_by(cp.slug.as_ref(), target, existing, report); } warnings::check_warnings_with_sources(parsed.body, fm.sources.len(), report); } - fn check_knowledge( - &self, - content: &str, - cp: &ClassifiedPath, - report: &mut LintReport, - ) { + fn check_knowledge(&self, content: &str, cp: &ClassifiedPath, report: &mut LintReport) { let parsed = match parse_frontmatter::(content) { Ok(p) => p, Err(e) => { @@ -236,8 +226,7 @@ impl Linter { size::check_body::(parsed.body, report); if fm.model_invokation - && fm.description.chars().count() - > crate::schema::KNOWLEDGE_DESCRIPTION_HARD_CAP + && fm.description.chars().count() > crate::schema::KNOWLEDGE_DESCRIPTION_HARD_CAP { report.push_error(LintError::DescriptionTooLong { actual: fm.description.chars().count(), @@ -339,7 +328,12 @@ mod tests { now = iso_now() ); let report = linter.lint(&path, &content, WriteMode::Create); - assert!(report.errors.iter().any(|e| matches!(e, LintError::WorkflowWriteForbidden))); + assert!( + report + .errors + .iter() + .any(|e| matches!(e, LintError::WorkflowWriteForbidden)) + ); } #[test] @@ -347,7 +341,12 @@ mod tests { let (dir, linter) = workspace(); let path = dir.path().join("src/main.rs"); let report = linter.lint(&path, "ignored", WriteMode::Create); - assert!(report.errors.iter().any(|e| matches!(e, LintError::InvalidPath(_)))); + assert!( + report + .errors + .iter() + .any(|e| matches!(e, LintError::InvalidPath(_))) + ); } #[test] @@ -359,10 +358,12 @@ mod tests { now = iso_now() ); let report = linter.lint(&path, &content, WriteMode::Create); - assert!(report.errors.iter().any(|e| matches!( - e, - LintError::UnknownReference { .. } - ))); + assert!( + report + .errors + .iter() + .any(|e| matches!(e, LintError::UnknownReference { .. })) + ); } #[test] @@ -374,7 +375,12 @@ mod tests { now = iso_now() ); let report = linter.lint(&path, &content, WriteMode::Update); - assert!(report.errors.iter().any(|e| matches!(e, LintError::ReplacedBySelf))); + assert!( + report + .errors + .iter() + .any(|e| matches!(e, LintError::ReplacedBySelf)) + ); } #[test] @@ -424,7 +430,12 @@ mod tests { now = iso_now() ); let report = linter.lint(&path, &content, WriteMode::Create); - assert!(report.errors.iter().any(|e| matches!(e, LintError::DescriptionTooLong { .. }))); + assert!( + report + .errors + .iter() + .any(|e| matches!(e, LintError::DescriptionTooLong { .. })) + ); } #[test] @@ -468,7 +479,12 @@ mod tests { now = iso_now() ); let report = linter.lint(&path, &content, WriteMode::Create); - assert!(report.errors.iter().any(|e| matches!(e, LintError::SlugAlreadyExists(_)))); + assert!( + report + .errors + .iter() + .any(|e| matches!(e, LintError::SlugAlreadyExists(_))) + ); } #[test] @@ -549,7 +565,11 @@ mod tests { .warnings .iter() .any(|w| matches!(w, LintWarning::SimilarSlugs(slugs) if slugs.len() >= 3)); - assert!(warned, "expected SimilarSlugs warning, got {:?}", report.warnings); + assert!( + warned, + "expected SimilarSlugs warning, got {:?}", + report.warnings + ); } #[test] @@ -591,7 +611,12 @@ mod tests { body = big_body ); let report = linter.lint(&path, &content, WriteMode::Create); - assert!(report.errors.iter().any(|e| matches!(e, LintError::BodyTooLong { .. }))); + assert!( + report + .errors + .iter() + .any(|e| matches!(e, LintError::BodyTooLong { .. })) + ); // Sanity: ensure path was treated as PathBuf consistently. let _ = PathBuf::from(path); } diff --git a/crates/memory/src/linter/references.rs b/crates/memory/src/linter/references.rs index da33b4c9..00fe02b4 100644 --- a/crates/memory/src/linter/references.rs +++ b/crates/memory/src/linter/references.rs @@ -49,9 +49,7 @@ pub fn check_replaced_by( return; } chain.push(node.to_string()); - cursor = existing - .decision(&node) - .and_then(|m| m.replaced_by.clone()); + cursor = existing.decision(&node).and_then(|m| m.replaced_by.clone()); } } diff --git a/crates/memory/src/linter/warnings.rs b/crates/memory/src/linter/warnings.rs index c6378b4a..2dcc5744 100644 --- a/crates/memory/src/linter/warnings.rs +++ b/crates/memory/src/linter/warnings.rs @@ -82,9 +82,7 @@ fn levenshtein(a: &str, b: &str) -> usize { curr[0] = i + 1; for (j, cb) in b.iter().enumerate() { let cost = if ca == cb { 0 } else { 1 }; - curr[j + 1] = (curr[j] + 1) - .min(prev[j + 1] + 1) - .min(prev[j] + cost); + curr[j + 1] = (curr[j] + 1).min(prev[j + 1] + 1).min(prev[j] + cost); } std::mem::swap(&mut prev, &mut curr); } diff --git a/crates/memory/src/schema/common.rs b/crates/memory/src/schema/common.rs index f10d6064..fcd511dd 100644 --- a/crates/memory/src/schema/common.rs +++ b/crates/memory/src/schema/common.rs @@ -50,9 +50,8 @@ pub fn split_frontmatter(content: &str) -> Result<(&str, &str), LintError> { byte_offset += line.len(); } - let (yaml_end_excl, body_start) = yaml_end.ok_or_else(|| { - LintError::MalformedFrontmatter("missing closing `---` line".to_string()) - })?; + let (yaml_end_excl, body_start) = yaml_end + .ok_or_else(|| LintError::MalformedFrontmatter("missing closing `---` line".to_string()))?; let yaml = &after_open[..yaml_end_excl]; let body = &after_open[body_start..]; diff --git a/crates/memory/src/slug.rs b/crates/memory/src/slug.rs index 511cd1d9..c0650ec5 100644 --- a/crates/memory/src/slug.rs +++ b/crates/memory/src/slug.rs @@ -118,16 +118,7 @@ mod tests { #[test] fn rejects_bad_slugs() { for s in [ - "", - "-", - "-foo", - "foo-", - "Foo", - "foo_bar", - "foo bar", - "foo--bar", - "foo.bar", - "ä", + "", "-", "-foo", "foo-", "Foo", "foo_bar", "foo bar", "foo--bar", "foo.bar", "ä", ] { assert!(!is_valid_slug(s), "expected `{s}` invalid"); assert!(Slug::parse(s).is_err()); diff --git a/crates/memory/src/tool/edit.rs b/crates/memory/src/tool/edit.rs index d9c3e74f..db5d6193 100644 --- a/crates/memory/src/tool/edit.rs +++ b/crates/memory/src/tool/edit.rs @@ -45,9 +45,8 @@ struct EditTool { #[async_trait] impl Tool for EditTool { async fn execute(&self, input_json: &str) -> Result { - let params: EditParams = serde_json::from_str(input_json).map_err(|e| { - ToolError::InvalidArgument(format!("invalid MemoryEdit input: {e}")) - })?; + let params: EditParams = serde_json::from_str(input_json) + .map_err(|e| ToolError::InvalidArgument(format!("invalid MemoryEdit input: {e}")))?; if params.old_string.is_empty() { return Err(ToolError::InvalidArgument( @@ -60,7 +59,9 @@ impl Tool for EditTool { )); } - let path = params.kind.resolve_path(&self.layout, params.slug.as_deref())?; + let path = params + .kind + .resolve_path(&self.layout, params.slug.as_deref())?; let current_bytes = std::fs::read(&path).map_err(|e| match e.kind() { std::io::ErrorKind::NotFound => ToolError::ExecutionFailed(format!( diff --git a/crates/memory/src/tool/mod.rs b/crates/memory/src/tool/mod.rs index 67809047..6a3ab1a2 100644 --- a/crates/memory/src/tool/mod.rs +++ b/crates/memory/src/tool/mod.rs @@ -20,7 +20,7 @@ use crate::workspace::{RecordKind, WorkspaceLayout}; pub use edit::edit_tool; pub use read::read_tool; -pub use search::{knowledge_search_tool, memory_search_tool, SearchConfig}; +pub use search::{SearchConfig, knowledge_search_tool, memory_search_tool}; pub use write::write_tool; /// Kinds the memory tools accept as input. `Workflow` is intentionally @@ -71,13 +71,10 @@ impl MemoryToolKind { } other => { let raw = slug.ok_or_else(|| { - ToolError::InvalidArgument(format!( - "kind={} requires `slug`", - other.as_str() - )) + ToolError::InvalidArgument(format!("kind={} requires `slug`", other.as_str())) })?; - let parsed = Slug::parse(raw) - .map_err(|e| ToolError::InvalidArgument(e.to_string()))?; + let parsed = + Slug::parse(raw).map_err(|e| ToolError::InvalidArgument(e.to_string()))?; Ok(match other { Self::Decision => layout.decision_path(&parsed), Self::Request => layout.request_path(&parsed), diff --git a/crates/memory/src/tool/read.rs b/crates/memory/src/tool/read.rs index c2826376..0d149179 100644 --- a/crates/memory/src/tool/read.rs +++ b/crates/memory/src/tool/read.rs @@ -43,11 +43,12 @@ struct ReadTool { #[async_trait] impl Tool for ReadTool { async fn execute(&self, input_json: &str) -> Result { - let params: ReadParams = serde_json::from_str(input_json).map_err(|e| { - ToolError::InvalidArgument(format!("invalid MemoryRead input: {e}")) - })?; + let params: ReadParams = serde_json::from_str(input_json) + .map_err(|e| ToolError::InvalidArgument(format!("invalid MemoryRead input: {e}")))?; - let path = params.kind.resolve_path(&self.layout, params.slug.as_deref())?; + let path = params + .kind + .resolve_path(&self.layout, params.slug.as_deref())?; let bytes = std::fs::read(&path).map_err(|e| match e.kind() { std::io::ErrorKind::NotFound => { diff --git a/crates/memory/src/tool/search.rs b/crates/memory/src/tool/search.rs index 7c698e59..f6092d10 100644 --- a/crates/memory/src/tool/search.rs +++ b/crates/memory/src/tool/search.rs @@ -116,9 +116,8 @@ struct KnowledgeSearchTool { #[async_trait] impl Tool for MemorySearchTool { async fn execute(&self, input_json: &str) -> Result { - let params: MemorySearchParams = serde_json::from_str(input_json).map_err(|e| { - ToolError::InvalidArgument(format!("invalid MemorySearch input: {e}")) - })?; + let params: MemorySearchParams = serde_json::from_str(input_json) + .map_err(|e| ToolError::InvalidArgument(format!("invalid MemorySearch input: {e}")))?; let needle = validate_query(¶ms.query)?; let mut hits: Vec = Vec::new(); @@ -241,9 +240,7 @@ impl Tool for KnowledgeSearchTool { fn validate_query(query: &str) -> Result { if query.trim().is_empty() { - return Err(ToolError::InvalidArgument( - "query must not be empty".into(), - )); + return Err(ToolError::InvalidArgument("query must not be empty".into())); } Ok(query.to_lowercase()) } diff --git a/crates/memory/src/tool/write.rs b/crates/memory/src/tool/write.rs index 4a159525..d41d8c04 100644 --- a/crates/memory/src/tool/write.rs +++ b/crates/memory/src/tool/write.rs @@ -40,11 +40,12 @@ struct WriteTool { #[async_trait] impl Tool for WriteTool { async fn execute(&self, input_json: &str) -> Result { - let params: WriteParams = serde_json::from_str(input_json).map_err(|e| { - ToolError::InvalidArgument(format!("invalid MemoryWrite input: {e}")) - })?; + let params: WriteParams = serde_json::from_str(input_json) + .map_err(|e| ToolError::InvalidArgument(format!("invalid MemoryWrite input: {e}")))?; - let path = params.kind.resolve_path(&self.layout, params.slug.as_deref())?; + let path = params + .kind + .resolve_path(&self.layout, params.slug.as_deref())?; let already_exists = path.exists(); let mode = if already_exists { @@ -72,7 +73,11 @@ impl Tool for WriteTool { let summary = format!( "{} {}{}", - if already_exists { "Overwrote" } else { "Created" }, + if already_exists { + "Overwrote" + } else { + "Created" + }, path.display(), warning_tail(&report), ); diff --git a/crates/memory/src/workspace.rs b/crates/memory/src/workspace.rs index 6ba489e3..9fe24e99 100644 --- a/crates/memory/src/workspace.rs +++ b/crates/memory/src/workspace.rs @@ -138,11 +138,7 @@ impl WorkspaceLayout { let knowledge = self.knowledge_dir(); if let Ok(rel) = path.strip_prefix(&knowledge) { - return Ok(Some(classify_kinded_md( - rel, - RecordKind::Knowledge, - path, - )?)); + return Ok(Some(classify_kinded_md(rel, RecordKind::Knowledge, path)?)); } let rel = match path.strip_prefix(&memory) { Ok(r) => r, diff --git a/crates/pod/build.rs b/crates/pod/build.rs index f666931c..08e04758 100644 --- a/crates/pod/build.rs +++ b/crates/pod/build.rs @@ -28,12 +28,7 @@ fn main() { let prompt_section = parsed .get("prompt") .and_then(|v| v.as_table()) - .unwrap_or_else(|| { - panic!( - "{} must contain a `[prompt]` table", - toml_path.display() - ) - }); + .unwrap_or_else(|| panic!("{} must contain a `[prompt]` table", toml_path.display())); let mut keys: Vec = prompt_section.keys().cloned().collect(); keys.sort(); diff --git a/crates/pod/src/compact/worker.rs b/crates/pod/src/compact/worker.rs index ce9ed4c0..f09ac021 100644 --- a/crates/pod/src/compact/worker.rs +++ b/crates/pod/src/compact/worker.rs @@ -59,7 +59,8 @@ impl CompactWorkerContext { } fn remaining_budget(&self) -> u64 { - self.auto_read_budget.saturating_sub(self.auto_read_consumed) + self.auto_read_budget + .saturating_sub(self.auto_read_consumed) } } diff --git a/crates/pod/src/controller.rs b/crates/pod/src/controller.rs index 21d93c99..d24ce15d 100644 --- a/crates/pod/src/controller.rs +++ b/crates/pod/src/controller.rs @@ -8,16 +8,16 @@ use tokio::sync::{broadcast, mpsc, oneshot}; use crate::ipc::alerter::Alerter; use crate::ipc::notify_buffer::NotifyBuffer; +use crate::ipc::server::SocketServer; use crate::pod::{Pod, PodError, PodRunResult}; +use crate::runtime::dir::RuntimeDir; +use crate::shared_state::{PodSharedState, PodStatus}; use crate::spawn::comm_tools::{ list_pods_tool, read_pod_output_tool, send_to_pod_tool, stop_pod_tool, }; -use crate::runtime::dir::RuntimeDir; -use crate::shared_state::{PodSharedState, PodStatus}; -use crate::ipc::server::SocketServer; -use crate::spawn::tool::spawn_pod_tool; use crate::spawn::registry::SpawnedPodRegistry; -use protocol::{ErrorCode, Event, Method, AlertLevel, AlertSource, RunResult, TurnResult}; +use crate::spawn::tool::spawn_pod_tool; +use protocol::{AlertLevel, AlertSource, ErrorCode, Event, Method, RunResult, TurnResult}; // --------------------------------------------------------------------------- // PodHandle — client-facing, Clone-able @@ -215,11 +215,7 @@ impl PodController { let alerter_for_worker = alerter.clone(); worker.on_warning(move |message| { - alerter_for_worker.alert( - AlertLevel::Warn, - AlertSource::Worker, - message.to_owned(), - ); + alerter_for_worker.alert(AlertLevel::Warn, AlertSource::Worker, message.to_owned()); }); // Register the builtin file-manipulation tools (Read / Write / @@ -735,9 +731,15 @@ where .map(|def| def().0.name) .collect(); tool_names.extend( - ["SpawnPod", "SendToPod", "ReadPodOutput", "StopPod", "ListPods"] - .iter() - .map(|s| (*s).into()), + [ + "SpawnPod", + "SendToPod", + "ReadPodOutput", + "StopPod", + "ListPods", + ] + .iter() + .map(|s| (*s).into()), ); protocol::Greeting { pod_name: manifest.pod.name.clone(), diff --git a/crates/pod/src/hook.rs b/crates/pod/src/hook.rs index 5b92c483..fa3896ea 100644 --- a/crates/pod/src/hook.rs +++ b/crates/pod/src/hook.rs @@ -15,10 +15,10 @@ //! exposing the underlying mutable state. use async_trait::async_trait; -use llm_worker::tool::ToolOutput; use llm_worker::interceptor::{ PostToolAction, PreRequestAction, PreToolAction, PromptAction, TurnEndAction, }; +use llm_worker::tool::ToolOutput; use serde_json::Value; // ============================================================================= diff --git a/crates/pod/src/interrupt_and_run.rs b/crates/pod/src/interrupt_and_run.rs index 1b0591f1..117cbd07 100644 --- a/crates/pod/src/interrupt_and_run.rs +++ b/crates/pod/src/interrupt_and_run.rs @@ -42,7 +42,8 @@ impl Pod { if !closures.is_empty() { self.worker_mut().extend_history(closures); } - self.worker_mut().push_item(Item::system_message(system_note)); + self.worker_mut() + .push_item(Item::system_message(system_note)); self.run(input).await } } @@ -84,10 +85,7 @@ mod tests { #[test] fn no_orphans_returns_empty() { - let history = vec![ - Item::user_message("hi"), - Item::assistant_message("hello"), - ]; + let history = vec![Item::user_message("hi"), Item::assistant_message("hello")]; let summary = interrupt_tool_result_summary(); assert!(orphan_tool_result_closures(&history, &summary).is_empty()); } diff --git a/crates/pod/src/ipc/event.rs b/crates/pod/src/ipc/event.rs index f18174a9..c08cbb24 100644 --- a/crates/pod/src/ipc/event.rs +++ b/crates/pod/src/ipc/event.rs @@ -26,9 +26,9 @@ use std::sync::Arc; use protocol::{Method, PodEvent, ScopeRule}; -use crate::spawn::comm_tools::connect_and_send; use crate::runtime::dir::SpawnedPodRecord; use crate::runtime::scope_lock::{self, ScopeLockError}; +use crate::spawn::comm_tools::connect_and_send; use crate::spawn::registry::SpawnedPodRegistry; /// Connect to `socket`, send a single `Method::PodEvent(event)`, and diff --git a/crates/pod/src/ipc/interceptor.rs b/crates/pod/src/ipc/interceptor.rs index c014a390..e6e940ec 100644 --- a/crates/pod/src/ipc/interceptor.rs +++ b/crates/pod/src/ipc/interceptor.rs @@ -21,13 +21,13 @@ use session_store::UsageRecord; use tracing::info; use crate::compact::state::CompactState; +use crate::compact::token_counter::total_tokens_impl; use crate::hook::{ AbortInfo, HookRegistry, PreRequestInfo, PromptSubmitInfo, ToolCallSummary, ToolResultSummary, TurnEndInfo, }; use crate::ipc::notify_buffer::{NotifyBuffer, format_notify}; use crate::prompt::catalog::PromptCatalog; -use crate::compact::token_counter::total_tokens_impl; use tracing::warn; /// Maximum number of bytes copied into `TurnEndInfo::final_text_preview`. diff --git a/crates/pod/src/ipc/notify_buffer.rs b/crates/pod/src/ipc/notify_buffer.rs index 63009248..b07c1d8d 100644 --- a/crates/pod/src/ipc/notify_buffer.rs +++ b/crates/pod/src/ipc/notify_buffer.rs @@ -105,7 +105,10 @@ mod tests { assert_eq!(drained.len(), CAPACITY); // Oldest 5 were dropped; first retained is msg5. assert_eq!(drained[0].message, "msg5"); - assert_eq!(drained[CAPACITY - 1].message, format!("msg{}", CAPACITY + 4)); + assert_eq!( + drained[CAPACITY - 1].message, + format!("msg{}", CAPACITY + 4) + ); } #[test] diff --git a/crates/pod/src/lib.rs b/crates/pod/src/lib.rs index 1d84a370..62cb146f 100644 --- a/crates/pod/src/lib.rs +++ b/crates/pod/src/lib.rs @@ -18,7 +18,7 @@ pub use hook::{Hook, HookEventKind, HookRegistryBuilder}; pub use ipc::alerter::Alerter; pub use ipc::server::SocketServer; pub use manifest::{ - AuthRef, ModelManifest, PodManifest, PodManifestConfig, PodMetaConfig, Scope, SchemeKind, + AuthRef, ModelManifest, PodManifest, PodManifestConfig, PodMetaConfig, SchemeKind, Scope, }; pub use pod::{Pod, PodError, PodRunResult, apply_worker_manifest}; pub use prompt::catalog::{CatalogError, PodPrompt, PromptCatalog}; diff --git a/crates/pod/src/main.rs b/crates/pod/src/main.rs index 4fea6215..40747cdc 100644 --- a/crates/pod/src/main.rs +++ b/crates/pod/src/main.rs @@ -171,10 +171,7 @@ async fn main() -> ExitCode { // (e.g. the TUI's interactive `spawn` flow). Tab-separated so a // pod name with spaces still parses cleanly. Emit before the // human line so a stderr-watching parent sees it first. - eprintln!( - "INSOMNIA-READY\t{pod_name}\t{}", - socket_path.display() - ); + eprintln!("INSOMNIA-READY\t{pod_name}\t{}", socket_path.display()); eprintln!("pod: {pod_name} listening on {:?}", socket_path); tokio::select! { diff --git a/crates/pod/src/pod.rs b/crates/pod/src/pod.rs index 8ff1ad28..d3cb9072 100644 --- a/crates/pod/src/pod.rs +++ b/crates/pod/src/pod.rs @@ -13,25 +13,25 @@ use tracing::{info, warn}; use manifest::{PodManifest, PodManifestConfig, ResolveError, Scope, ScopeError, WorkerManifest}; -use crate::prompt::agents_md::read_agents_md; use crate::compact::state::CompactState; +use crate::compact::usage_tracker::UsageTracker; use crate::hook::{ Hook, HookRegistryBuilder, OnAbort, OnPromptSubmit, OnTurnEnd, PostToolCall, PreLlmRequest, PreRequestInfo, PreToolCall, }; use crate::ipc::alerter::Alerter; -use crate::ipc::notify_buffer::NotifyBuffer; use crate::ipc::interceptor::PodInterceptor; -use crate::prompt::loader::PromptLoader; +use crate::ipc::notify_buffer::NotifyBuffer; +use crate::prompt::agents_md::read_agents_md; use crate::prompt::catalog::{CatalogError, PromptCatalog}; +use crate::prompt::loader::PromptLoader; +use crate::prompt::system::{SystemPromptContext, SystemPromptError, SystemPromptTemplate}; use crate::runtime::dir; use crate::runtime::scope_lock::{self, ScopeAllocationGuard, ScopeLockError}; -use crate::prompt::system::{SystemPromptContext, SystemPromptError, SystemPromptTemplate}; -use crate::compact::usage_tracker::UsageTracker; -use protocol::{AlertLevel, AlertSource, Event, Segment}; -use tokio::sync::broadcast; use async_trait::async_trait; use llm_worker::interceptor::PreRequestAction; +use protocol::{AlertLevel, AlertSource, Event, Segment}; +use tokio::sync::broadcast; /// Pre-LLM-request hook that records `history.len()` at send time into a /// shared `UsageTracker`. The on_usage callback later pairs this with the @@ -511,9 +511,7 @@ impl Pod { None }; - let usage_history_handle = compact_state - .as_ref() - .map(|_| self.usage_history.clone()); + let usage_history_handle = compact_state.as_ref().map(|_| self.usage_history.clone()); let interceptor = PodInterceptor::new( registry, @@ -553,11 +551,7 @@ impl Pod { let agents_md_read = read_agents_md(&self.pwd); for warning in agents_md_read.warnings { if let Some(n) = alerter.as_ref() { - n.alert( - AlertLevel::Warn, - AlertSource::AgentsMd, - warning, - ); + n.alert(AlertLevel::Warn, AlertSource::AgentsMd, warning); } } // Resident-injection collection: only when memory is enabled in @@ -603,10 +597,7 @@ impl Pod { /// Equivalent to `run(vec![Segment::text(s)])`. The dumb-client /// counterpart of [`protocol::Method::run_text`]; primarily for /// tests and tools that have only a string in hand. - pub async fn run_text( - &mut self, - s: impl Into, - ) -> Result { + pub async fn run_text(&mut self, s: impl Into) -> Result { self.run(vec![Segment::text(s)]).await } @@ -995,7 +986,12 @@ impl Pod { .manifest .compaction .as_ref() - .map(|c| (c.compact_auto_read_budget, c.compact_worker_max_input_tokens)) + .map(|c| { + ( + c.compact_auto_read_budget, + c.compact_worker_max_input_tokens, + ) + }) .unwrap_or(( manifest::defaults::COMPACT_AUTO_READ_BUDGET, manifest::defaults::COMPACT_WORKER_MAX_INPUT_TOKENS, @@ -1054,8 +1050,7 @@ impl Pod { // Tools: read_file (shared scope, fresh tracker) + the three // compact-specific tools that populate `ctx`. summary_worker.register_tool(tools::read_tool(scoped_fs.clone(), summary_tracker)); - summary_worker - .register_tool(mark_read_required_tool(scoped_fs.clone(), ctx.clone())); + summary_worker.register_tool(mark_read_required_tool(scoped_fs.clone(), ctx.clone())); summary_worker.register_tool(add_reference_tool(ctx.clone())); summary_worker.register_tool(write_summary_tool(ctx.clone())); @@ -1092,10 +1087,7 @@ impl Pod { } }; if let Some(prompt) = nudge { - let _ = locked_worker - .run(prompt) - .await - .map_err(PodError::Worker)?; + let _ = locked_worker.run(prompt).await.map_err(PodError::Worker)?; } let final_ctx = ctx.lock().expect("compact ctx poisoned").clone(); @@ -1154,7 +1146,8 @@ impl Pod { // Build new history: [summary, ...auto-read, references, ...retained]. let mut new_history = Vec::with_capacity( - 1 + auto_read_messages.len() + reference_message.is_some() as usize + 1 + auto_read_messages.len() + + reference_message.is_some() as usize + retained_items.len(), ); new_history.push(Item::system_message(format!( @@ -1456,9 +1449,7 @@ fn build_summary_input(items: &[Item], default_refs: &[PathBuf]) -> String { } out.push_str("## Conversation\n"); out.push_str(&build_summary_prompt(items)); - out.push_str( - "\n\nWhen you are done, call `write_summary` with the final 5-section text.", - ); + out.push_str("\n\nWhen you are done, call `write_summary` with the final 5-section text."); out } @@ -1579,10 +1570,8 @@ fn current_pwd() -> Result { pwd: PathBuf::from("."), source, })?; - cwd.canonicalize().map_err(|source| PodError::InvalidPwd { - pwd: cwd, - source, - }) + cwd.canonicalize() + .map_err(|source| PodError::InvalidPwd { pwd: cwd, source }) } #[cfg(test)] diff --git a/crates/pod/src/prompt/catalog.rs b/crates/pod/src/prompt/catalog.rs index 74ee9271..57e60b23 100644 --- a/crates/pod/src/prompt/catalog.rs +++ b/crates/pod/src/prompt/catalog.rs @@ -310,10 +310,7 @@ impl PromptCatalog { } /// Render `PodPrompt::WorkingBoundariesSection` with `{{ scope_summary }}`. - pub fn working_boundaries_section( - &self, - scope_summary: &str, - ) -> Result { + pub fn working_boundaries_section(&self, scope_summary: &str) -> Result { self.render( PodPrompt::WorkingBoundariesSection, single("scope_summary", scope_summary), @@ -343,8 +340,7 @@ fn single(key: &'static str, value: &str) -> Value { } fn parse_builtin_pack() -> Result, CatalogError> { - let parsed: PackFile = - toml::from_str(INTERNAL_TOML).map_err(CatalogError::ParseBuiltin)?; + let parsed: PackFile = toml::from_str(INTERNAL_TOML).map_err(CatalogError::ParseBuiltin)?; Ok(parsed.prompt) } diff --git a/crates/pod/src/prompt/loader.rs b/crates/pod/src/prompt/loader.rs index 256420c5..76b8473d 100644 --- a/crates/pod/src/prompt/loader.rs +++ b/crates/pod/src/prompt/loader.rs @@ -23,8 +23,7 @@ use std::path::{Path, PathBuf}; use include_dir::{Dir, include_dir}; use thiserror::Error; -static BUILTIN_PROMPTS: Dir<'static> = - include_dir!("$CARGO_MANIFEST_DIR/../../resources/prompts"); +static BUILTIN_PROMPTS: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/../../resources/prompts"); const PREFIX_INSOMNIA: &str = "$insomnia"; const PREFIX_USER: &str = "$user"; @@ -190,10 +189,12 @@ impl PromptLoader { } if let Some(prefix) = trimmed.strip_prefix('$') { let (prefix_name, rest) = - prefix.split_once('/').ok_or_else(|| LoaderError::InvalidRef { - raw: raw.to_string(), - reason: "prefix must be followed by '/'".into(), - })?; + prefix + .split_once('/') + .ok_or_else(|| LoaderError::InvalidRef { + raw: raw.to_string(), + reason: "prefix must be followed by '/'".into(), + })?; let prefix = parse_prefix(raw, prefix_name)?; let path = normalize_path(raw, rest)?; Ok(PromptRef { prefix, path }) @@ -293,10 +294,7 @@ fn load_from_dir(dir: &Path, reference: &PromptRef) -> Result, - reference: &PromptRef, -) -> Result { +fn load_from_include_dir(dir: &Dir<'static>, reference: &PromptRef) -> Result { let path = format!("{}.md", reference.path); dir.get_file(&path) .and_then(|f| f.contents_utf8()) @@ -349,7 +347,9 @@ mod tests { #[test] fn missing_file_is_hard_error() { let loader = PromptLoader::builtins_only(); - let err = loader.resolve("$insomnia/definitely-missing", None).unwrap_err(); + let err = loader + .resolve("$insomnia/definitely-missing", None) + .unwrap_err(); assert!(matches!(err, LoaderError::NotFound { .. })); } @@ -380,7 +380,9 @@ mod tests { #[test] fn unqualified_ref_resolves_relative_to_current() { let loader = PromptLoader::builtins_only(); - let current = loader.parse_ref("$insomnia/common/tool-usage", None).unwrap(); + let current = loader + .parse_ref("$insomnia/common/tool-usage", None) + .unwrap(); // Sibling lookup under the same prefix and directory. let sibling = loader.parse_ref("workspace", Some(¤t)).unwrap(); assert_eq!(sibling.to_qualified_string(), "$insomnia/common/workspace"); diff --git a/crates/pod/src/prompt/system.rs b/crates/pod/src/prompt/system.rs index 5bf6aacb..dfbb94e8 100644 --- a/crates/pod/src/prompt/system.rs +++ b/crates/pod/src/prompt/system.rs @@ -23,8 +23,8 @@ use minijinja::value::Value; use minijinja::{Environment, ErrorKind, UndefinedBehavior}; use thiserror::Error; -use crate::prompt::loader::{LoaderError, PromptLoader, PromptRef}; use crate::prompt::catalog::{CatalogError, PromptCatalog}; +use crate::prompt::loader::{LoaderError, PromptLoader, PromptRef}; #[derive(Debug, Error)] pub enum SystemPromptError { @@ -55,10 +55,7 @@ impl SystemPromptTemplate { /// Parse the instruction asset referenced by `instruction_ref` /// using the supplied [`PromptLoader`]. The reference is resolved /// at parse time so syntax errors surface immediately. - pub fn parse( - instruction_ref: &str, - loader: PromptLoader, - ) -> Result { + pub fn parse(instruction_ref: &str, loader: PromptLoader) -> Result { let root_ref = loader .parse_ref(instruction_ref, None) .map_err(SystemPromptError::LoaderResolve)?; @@ -75,9 +72,7 @@ impl SystemPromptTemplate { // The joined name is then looked up via `set_loader` below. let loader_for_join = loader.clone(); env.set_path_join_callback(move |name, parent| { - let parent_ref = loader_for_join - .parse_ref(parent, None) - .ok(); + let parent_ref = loader_for_join.parse_ref(parent, None).ok(); match loader_for_join.parse_ref(name, parent_ref.as_ref()) { Ok(r) => r.to_qualified_string().into(), // Propagate the raw name on error so set_loader surfaces @@ -93,7 +88,10 @@ impl SystemPromptTemplate { .map_err(|e| minijinja::Error::new(ErrorKind::TemplateNotFound, e.to_string()))?; match loader_for_src.load(&reference) { Ok(source) => Ok(Some(source)), - Err(e) => Err(minijinja::Error::new(ErrorKind::TemplateNotFound, e.to_string())), + Err(e) => Err(minijinja::Error::new( + ErrorKind::TemplateNotFound, + e.to_string(), + )), } }); @@ -459,7 +457,9 @@ mod tests { let tmpl = SystemPromptTemplate::parse("$user/ghost", loader).unwrap(); let dir = TempDir::new().unwrap(); let scope = build_scope(dir.path()); - let err = tmpl.render(&ctx(dir.path(), &scope, vec![], None)).unwrap_err(); + let err = tmpl + .render(&ctx(dir.path(), &scope, vec![], None)) + .unwrap_err(); assert!(matches!(err, SystemPromptError::Render(_))); } diff --git a/crates/pod/src/runtime/dir.rs b/crates/pod/src/runtime/dir.rs index 7cc49a0e..f8276895 100644 --- a/crates/pod/src/runtime/dir.rs +++ b/crates/pod/src/runtime/dir.rs @@ -82,10 +82,7 @@ impl RuntimeDir { /// Write `spawned_pods.json` atomically. The entries are the full /// set of spawned children known to this Pod — callers pass the /// replacement list, no incremental merge. - pub async fn write_spawned_pods( - &self, - records: &[SpawnedPodRecord], - ) -> Result<(), io::Error> { + pub async fn write_spawned_pods(&self, records: &[SpawnedPodRecord]) -> Result<(), io::Error> { let json = serde_json::to_vec_pretty(records).map_err(io::Error::other)?; atomic_write(&self.path.join("spawned_pods.json"), &json).await } diff --git a/crates/pod/src/runtime/scope_lock.rs b/crates/pod/src/runtime/scope_lock.rs index 355e7a38..0395ac8c 100644 --- a/crates/pod/src/runtime/scope_lock.rs +++ b/crates/pod/src/runtime/scope_lock.rs @@ -206,10 +206,7 @@ pub fn is_within_effective_write(lock: &LockFile, parent: &str, rule: &ScopeRule return false; }; if rule.permission != Permission::Write { - return alloc - .scope_allow - .iter() - .any(|r| covers_fully(r, rule)); + return alloc.scope_allow.iter().any(|r| covers_fully(r, rule)); } let covered = alloc .scope_allow @@ -244,7 +241,11 @@ pub fn find_conflict_owner( if rule.permission != Permission::Write { return None; } - for alloc in lock.allocations.iter().filter(|a| a.delegated_from.is_none()) { + for alloc in lock + .allocations + .iter() + .filter(|a| a.delegated_from.is_none()) + { if let Some(owner) = find_conflict_in_subtree(lock, alloc, rule) { if Some(owner.as_str()) == exempt { continue; @@ -526,18 +527,12 @@ pub enum ScopeLockError { #[error("pod name `{0}` is already registered")] DuplicatePodName(String), #[error("requested scope `{}` conflicts with pod `{competitor}`", .rule.target.display())] - WriteConflict { - competitor: String, - rule: ScopeRule, - }, + WriteConflict { competitor: String, rule: ScopeRule }, #[error( "requested scope `{}` is not within spawner `{spawner}`'s effective scope", .rule.target.display() )] - NotSubset { - spawner: String, - rule: ScopeRule, - }, + NotSubset { spawner: String, rule: ScopeRule }, #[error("pod `{0}` is not registered")] UnknownPod(String), } diff --git a/crates/pod/src/spawn/comm_tools.rs b/crates/pod/src/spawn/comm_tools.rs index 041a4c7c..b6d90943 100644 --- a/crates/pod/src/spawn/comm_tools.rs +++ b/crates/pod/src/spawn/comm_tools.rs @@ -43,8 +43,7 @@ struct NameInput { // SendToPod // --------------------------------------------------------------------------- -const SEND_TO_POD_DESCRIPTION: &str = - "Send a text message to a previously spawned Pod. The spawned Pod \ +const SEND_TO_POD_DESCRIPTION: &str = "Send a text message to a previously spawned Pod. The spawned Pod \ processes it as a user turn. Fails if the Pod is already executing a \ turn — retry after it finishes. Does not wait for the turn to complete; \ use `ReadPodOutput` to fetch results afterwards."; @@ -109,8 +108,7 @@ pub fn send_to_pod_tool(registry: Arc) -> ToolDefinition { // ReadPodOutput // --------------------------------------------------------------------------- -const READ_POD_OUTPUT_DESCRIPTION: &str = - "Fetch new assistant text from a spawned Pod since the last read. \ +const READ_POD_OUTPUT_DESCRIPTION: &str = "Fetch new assistant text from a spawned Pod since the last read. \ Uses an internal cursor per-Pod so consecutive calls return only \ newly-produced output. Returns the Pod's current status and the new \ text, or reports `stopped` if the Pod can no longer be reached."; @@ -122,9 +120,8 @@ struct ReadPodOutputTool { #[async_trait] impl Tool for ReadPodOutputTool { async fn execute(&self, input_json: &str) -> Result { - let input: NameInput = serde_json::from_str(input_json).map_err(|e| { - ToolError::InvalidArgument(format!("invalid ReadPodOutput input: {e}")) - })?; + let input: NameInput = serde_json::from_str(input_json) + .map_err(|e| ToolError::InvalidArgument(format!("invalid ReadPodOutput input: {e}")))?; let record = self .registry .get(&input.name) @@ -154,7 +151,10 @@ impl Tool for ReadPodOutputTool { format!("pod `{}` running; no new assistant text", input.name) } else { let lines = new_text.lines().count(); - format!("pod `{}`: {lines} new line(s) of assistant text", input.name) + format!( + "pod `{}`: {lines} new line(s) of assistant text", + input.name + ) }; let content = if new_text.is_empty() { None @@ -183,8 +183,7 @@ pub fn read_pod_output_tool(registry: Arc) -> ToolDefinition // StopPod // --------------------------------------------------------------------------- -const STOP_POD_DESCRIPTION: &str = - "Terminate a spawned Pod and reclaim the delegated scope. The Pod \ +const STOP_POD_DESCRIPTION: &str = "Terminate a spawned Pod and reclaim the delegated scope. The Pod \ receives `Shutdown`; its scope entry is released in the machine-wide \ registry so the spawner can spawn a new Pod over the same paths."; @@ -247,8 +246,7 @@ pub fn stop_pod_tool(registry: Arc) -> ToolDefinition { // ListPods // --------------------------------------------------------------------------- -const LIST_PODS_DESCRIPTION: &str = - "List all Pods spawned by this Pod along with their reachability \ +const LIST_PODS_DESCRIPTION: &str = "List all Pods spawned by this Pod along with their reachability \ status (`alive` / `stopped`) and the scope each was granted."; #[derive(Debug, Deserialize, schemars::JsonSchema)] @@ -364,9 +362,9 @@ async fn send_run_and_confirm(socket: &Path, input: String) -> Result<(), SendRu input: vec![protocol::Segment::text(input)], }), ) - .await - .map_err(|_| SendRunError::Io("write timed out".into()))? - .map_err(|e| SendRunError::Io(format!("write: {e}")))?; + .await + .map_err(|_| SendRunError::Io("write timed out".into()))? + .map_err(|e| SendRunError::Io(format!("write: {e}")))?; loop { let event = tokio::time::timeout(SOCKET_OP_TIMEOUT, reader.next::()) .await diff --git a/crates/pod/src/spawn/registry.rs b/crates/pod/src/spawn/registry.rs index ecf366fc..1849ef69 100644 --- a/crates/pod/src/spawn/registry.rs +++ b/crates/pod/src/spawn/registry.rs @@ -43,7 +43,9 @@ impl SpawnedPodRegistry { pub async fn add(&self, record: SpawnedPodRecord) -> io::Result<()> { let mut records = self.records.lock().await; records.push(record); - self.runtime_dir.write_spawned_pods(records.as_slice()).await + self.runtime_dir + .write_spawned_pods(records.as_slice()) + .await } /// Look up a record by pod name. Cloned so callers can drop the lock. @@ -67,7 +69,9 @@ impl SpawnedPodRegistry { let mut records = self.records.lock().await; let idx = records.iter().position(|r| r.pod_name == pod_name); let removed = idx.map(|i| records.remove(i)); - self.runtime_dir.write_spawned_pods(records.as_slice()).await?; + self.runtime_dir + .write_spawned_pods(records.as_slice()) + .await?; removed }; self.cursors.lock().await.remove(pod_name); diff --git a/crates/pod/src/spawn/tool.rs b/crates/pod/src/spawn/tool.rs index 3ba7998b..9eefd2af 100644 --- a/crates/pod/src/spawn/tool.rs +++ b/crates/pod/src/spawn/tool.rs @@ -294,9 +294,9 @@ impl SpawnPodTool { .stderr(Stdio::from(stderr_file)) .process_group(0); - let child = cmd - .spawn() - .map_err(|e| ToolError::ExecutionFailed(format!("failed to spawn `{pod_command}`: {e}")))?; + let child = cmd.spawn().map_err(|e| { + ToolError::ExecutionFailed(format!("failed to spawn `{pod_command}`: {e}")) + })?; // Default `kill_on_drop = false` keeps the process alive after // the `Child` is dropped. We intentionally do not `.wait()` — @@ -498,7 +498,10 @@ mod tests { assert_eq!(parsed.model.scheme, Some(SchemeKind::Anthropic)); assert_eq!(parsed.model.model_id.as_deref(), Some("claude-sonnet-4")); - assert_eq!(parsed.model.base_url.as_deref(), Some("https://example.test")); + assert_eq!( + parsed.model.base_url.as_deref(), + Some("https://example.test") + ); let file = match parsed.model.auth { Some(AuthRef::ApiKey { file, .. }) => file, _ => panic!("expected ApiKey"), diff --git a/crates/pod/tests/controller_test.rs b/crates/pod/tests/controller_test.rs index 759c053c..826751fd 100644 --- a/crates/pod/tests/controller_test.rs +++ b/crates/pod/tests/controller_test.rs @@ -1,6 +1,6 @@ use std::pin::Pin; -use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex}; use async_trait::async_trait; use futures::{Stream, StreamExt}; @@ -169,10 +169,7 @@ async fn run_updates_shared_state_to_idle_after_completion() { let pod = make_pod(client).await; let handle = spawn_controller(pod).await; - handle - .send(Method::run_text("Hello")) - .await - .unwrap(); + handle.send(Method::run_text("Hello")).await.unwrap(); // Wait for the run to complete tokio::time::sleep(std::time::Duration::from_millis(100)).await; @@ -186,10 +183,7 @@ async fn run_populates_history() { let pod = make_pod(client).await; let handle = spawn_controller(pod).await; - handle - .send(Method::run_text("Hello")) - .await - .unwrap(); + handle.send(Method::run_text("Hello")).await.unwrap(); tokio::time::sleep(std::time::Duration::from_millis(100)).await; @@ -207,10 +201,7 @@ async fn events_are_broadcast() { let handle = spawn_controller(pod).await; let mut rx = handle.subscribe(); - handle - .send(Method::run_text("Hello")) - .await - .unwrap(); + handle.send(Method::run_text("Hello")).await.unwrap(); let mut saw_turn_start = false; let mut saw_text_delta = false; @@ -258,16 +249,10 @@ async fn double_run_returns_error() { let mut rx = handle.subscribe(); // Send first run - handle - .send(Method::run_text("first")) - .await - .unwrap(); + handle.send(Method::run_text("first")).await.unwrap(); // Immediately send second run (should get error) - handle - .send(Method::run_text("second")) - .await - .unwrap(); + handle.send(Method::run_text("second")).await.unwrap(); // Look for the error event let mut saw_already_running = false; @@ -410,8 +395,14 @@ async fn run_with_paste_segment_inlines_content_and_emits_typed_user_message() { .iter() .find_map(|i| i.as_text().map(|s| s.to_string())) .unwrap_or_default(); - assert!(user_text.contains("see line1\nline2 thanks"), "got: {user_text:?}"); - assert!(!user_text.contains("[Clipboard"), "label must not leak: {user_text:?}"); + assert!( + user_text.contains("see line1\nline2 thanks"), + "got: {user_text:?}" + ); + assert!( + !user_text.contains("[Clipboard"), + "label must not leak: {user_text:?}" + ); } #[tokio::test] @@ -424,12 +415,11 @@ async fn run_with_unresolved_segment_emits_alert_and_placeholder() { let segments = vec![ protocol::Segment::text("look at "), - protocol::Segment::FileRef { path: "src/lib.rs".into() }, + protocol::Segment::FileRef { + path: "src/lib.rs".into(), + }, ]; - handle - .send(Method::Run { input: segments }) - .await - .unwrap(); + handle.send(Method::Run { input: segments }).await.unwrap(); let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(2); let mut saw_alert_for_file_ref = false; @@ -527,10 +517,7 @@ async fn notify_while_running_does_not_emit_already_running_error() { let handle = spawn_controller(pod).await; let mut rx = handle.subscribe(); - handle - .send(Method::run_text("start")) - .await - .unwrap(); + handle.send(Method::run_text("start")).await.unwrap(); handle .send(Method::Notify { message: "ping".into(), @@ -591,10 +578,7 @@ async fn socket_run_receives_events() { let mut writer = JsonLineWriter::new(writer); // Send run method via socket - writer - .write(&Method::run_text("Hello")) - .await - .unwrap(); + writer.write(&Method::run_text("Hello")).await.unwrap(); // Collect events let mut saw_turn_start = false; @@ -739,10 +723,7 @@ async fn pause_then_resume_transitions_and_preserves_history_consistency() { let handle = spawn_controller(pod).await; let mut rx = handle.subscribe(); - handle - .send(Method::run_text("hello")) - .await - .unwrap(); + handle.send(Method::run_text("hello")).await.unwrap(); // Wait for the partial text_delta to confirm the first stream is // live before we pause. @@ -794,10 +775,7 @@ async fn pause_then_resume_transitions_and_preserves_history_consistency() { // (partial text is not committed), no orphan tool_use. let history_json = handle.shared_state.history_json(); let items: Vec = serde_json::from_str(&history_json).unwrap(); - let roles: Vec<&str> = items - .iter() - .filter_map(|i| i["role"].as_str()) - .collect(); + let roles: Vec<&str> = items.iter().filter_map(|i| i["role"].as_str()).collect(); assert_eq!( roles, vec!["user", "assistant"], @@ -850,10 +828,7 @@ async fn paused_then_run_closes_orphan_tool_use_for_next_request() { let handle = spawn_controller(pod).await; let mut rx = handle.subscribe(); - handle - .send(Method::run_text("first")) - .await - .unwrap(); + handle.send(Method::run_text("first")).await.unwrap(); // Wait for ToolCallDone — the ToolCall is committed to history // right before the Worker enters tool execution and pends. @@ -883,10 +858,7 @@ async fn paused_then_run_closes_orphan_tool_use_for_next_request() { // New user input while Paused → controller routes to // `Pod::interrupt_and_run`, which closes the orphan + injects a // system note before the fresh user message. - handle - .send(Method::run_text("new request")) - .await - .unwrap(); + handle.send(Method::run_text("new request")).await.unwrap(); assert!( drain_until(&mut rx, std::time::Duration::from_secs(2), |e| matches!( e, @@ -925,9 +897,7 @@ async fn paused_then_run_closes_orphan_tool_use_for_next_request() { saw_interruption_note = true; } } - llm_worker::Item::Message { role, content, .. } - if *role == llm_worker::Role::User => - { + llm_worker::Item::Message { role, content, .. } if *role == llm_worker::Role::User => { let text: String = content.iter().map(|p| p.as_text()).collect(); if text.contains("new request") { saw_new_user = true; @@ -952,10 +922,10 @@ async fn paused_then_run_closes_orphan_tool_use_for_next_request() { // Also confirm the closure chain is ordered: tool_result for the // orphan precedes the system note, which precedes the new user // message. - let idx = |pred: &dyn Fn(&llm_worker::Item) -> bool| { - items.iter().position(pred).unwrap() - }; - let tool_result_idx = idx(&|i| matches!(i, llm_worker::Item::ToolResult { call_id, .. } if call_id == "call_orphan")); + let idx = |pred: &dyn Fn(&llm_worker::Item) -> bool| items.iter().position(pred).unwrap(); + let tool_result_idx = idx( + &|i| matches!(i, llm_worker::Item::ToolResult { call_id, .. } if call_id == "call_orphan"), + ); let sys_idx = idx(&|i| match i { llm_worker::Item::Message { role: llm_worker::Role::System, @@ -980,7 +950,12 @@ async fn paused_then_run_closes_orphan_tool_use_for_next_request() { .contains("new request"), _ => false, }); - assert!(tool_result_idx < sys_idx, "tool_result must precede system note"); - assert!(sys_idx < user_idx, "system note must precede new user message"); + assert!( + tool_result_idx < sys_idx, + "tool_result must precede system note" + ); + assert!( + sys_idx < user_idx, + "system note must precede new user message" + ); } - diff --git a/crates/pod/tests/pod_comm_tools_test.rs b/crates/pod/tests/pod_comm_tools_test.rs index d4aea266..36a62143 100644 --- a/crates/pod/tests/pod_comm_tools_test.rs +++ b/crates/pod/tests/pod_comm_tools_test.rs @@ -14,11 +14,11 @@ use std::sync::{Arc, LazyLock, Mutex}; use llm_worker::llm_client::types::{ContentPart, Item, Role}; use llm_worker::tool::ToolOutput; use manifest::{Permission, ScopeRule}; +use pod::runtime::dir::{RuntimeDir, SpawnedPodRecord}; +use pod::runtime::scope_lock::{self, LockFileGuard}; use pod::spawn::comm_tools::{ list_pods_tool, read_pod_output_tool, send_to_pod_tool, stop_pod_tool, }; -use pod::runtime::dir::{RuntimeDir, SpawnedPodRecord}; -use pod::runtime::scope_lock::{self, LockFileGuard}; use pod::spawn::registry::SpawnedPodRegistry; use protocol::stream::{JsonLineReader, JsonLineWriter}; use protocol::{ErrorCode, Event, Greeting, Method}; @@ -211,7 +211,11 @@ async fn send_to_pod_delivers_run_method() { let (_meta, tool) = def(); let input = json!({ "name": "child", "message": "hello there" }).to_string(); let output: ToolOutput = tool.execute(&input).await.unwrap(); - assert!(output.summary.contains("child"), "summary: {}", output.summary); + assert!( + output.summary.contains("child"), + "summary: {}", + output.summary + ); let method = received.await.unwrap().expect("expected a method"); match method { @@ -292,7 +296,11 @@ async fn read_pod_output_returns_new_assistant_text_then_empty_on_second_call() // Cursor now points past all items — second call returns no new text. let second: ToolOutput = tool.execute(&input).await.unwrap(); - assert!(second.content.is_none(), "unexpected content: {:?}", second.content); + assert!( + second.content.is_none(), + "unexpected content: {:?}", + second.content + ); assert!( second.summary.contains("no new assistant text"), "summary: {}", @@ -451,6 +459,10 @@ async fn list_pods_empty_when_nothing_registered() { let def = list_pods_tool(registry); let (_meta, tool) = def(); let output: ToolOutput = tool.execute("{}").await.unwrap(); - assert!(output.summary.contains("no spawned pods"), "{}", output.summary); + assert!( + output.summary.contains("no spawned pods"), + "{}", + output.summary + ); assert!(output.content.is_none()); } diff --git a/crates/pod/tests/pod_events_test.rs b/crates/pod/tests/pod_events_test.rs index 43b240e3..fd74282d 100644 --- a/crates/pod/tests/pod_events_test.rs +++ b/crates/pod/tests/pod_events_test.rs @@ -77,9 +77,7 @@ fn clear_runtime_dir() { } /// Accept a single connection, read one `Method`, and return it. -fn accept_one_method( - listener: UnixListener, -) -> tokio::task::JoinHandle> { +fn accept_one_method(listener: UnixListener) -> tokio::task::JoinHandle> { tokio::spawn(async move { let (stream, _) = listener.accept().await.ok()?; let (reader, _writer) = stream.into_split(); diff --git a/crates/pod/tests/spawn_pod_test.rs b/crates/pod/tests/spawn_pod_test.rs index 5b54bd19..bd31ae46 100644 --- a/crates/pod/tests/spawn_pod_test.rs +++ b/crates/pod/tests/spawn_pod_test.rs @@ -14,8 +14,8 @@ use llm_worker::tool::{ToolError, ToolOutput}; use manifest::{AuthRef, ModelManifest, Permission, SchemeKind, ScopeRule}; use pod::runtime::dir::{RuntimeDir, SpawnedPodRecord}; use pod::runtime::scope_lock::{self, LockFileGuard}; -use pod::spawn::tool::spawn_pod_tool; use pod::spawn::registry::SpawnedPodRegistry; +use pod::spawn::tool::spawn_pod_tool; use protocol::Method; use protocol::stream::JsonLineReader; use serde_json::json; @@ -99,9 +99,7 @@ async fn bind_mock_pod_socket(runtime_base: &Path, pod_name: &str) -> (PathBuf, /// `Method` line, then returns it. `wait_for_socket` inside the tool /// makes a probe connection that carries no data, so the task must /// tolerate an empty connection and keep listening. -fn accept_one_method( - listener: UnixListener, -) -> tokio::task::JoinHandle> { +fn accept_one_method(listener: UnixListener) -> tokio::task::JoinHandle> { tokio::spawn(async move { loop { let (stream, _) = listener.accept().await.ok()?; @@ -192,7 +190,11 @@ async fn spawn_pod_delegates_scope_and_sends_run() { .to_string(); let output: ToolOutput = tool.execute(&input).await.unwrap(); - assert!(output.summary.contains("child"), "summary: {}", output.summary); + assert!( + output.summary.contains("child"), + "summary: {}", + output.summary + ); // Verify the tool delivered Method::Run to the socket. let method = received.await.unwrap().expect("expected one Method line"); @@ -261,7 +263,10 @@ async fn spawn_pod_rejects_scope_outside_spawner() { let err = tool.execute(&input).await.unwrap_err(); match err { ToolError::InvalidArgument(msg) => { - assert!(msg.contains("not within"), "expected NotSubset wording: {msg}"); + assert!( + msg.contains("not within"), + "expected NotSubset wording: {msg}" + ); } other => panic!("expected InvalidArgument, got {other:?}"), } diff --git a/crates/pod/tests/system_prompt_template_test.rs b/crates/pod/tests/system_prompt_template_test.rs index 5664d176..b3fef240 100644 --- a/crates/pod/tests/system_prompt_template_test.rs +++ b/crates/pod/tests/system_prompt_template_test.rs @@ -246,11 +246,11 @@ async fn agents_md_absent_omits_trailing_section() { #[tokio::test] async fn agents_md_not_reread_after_compact() { let client = MockClient::new(vec![ - single_text_events("a"), // pod.run_text("first") - single_text_events("b"), // pod.run_text("second") + single_text_events("a"), // pod.run_text("first") + single_text_events("b"), // pod.run_text("second") write_summary_tool_use_events("call-1", "compacted summary"), // compact worker: tool_use - single_text_events("done"), // compact worker: close - single_text_events("c"), // pod.run_text("third") + single_text_events("done"), // compact worker: close + single_text_events("c"), // pod.run_text("third") ]); let (mut pod, pwd) = make_pod_with_body("BODY", client).await.unwrap(); let agents_path = pwd.join("AGENTS.md"); @@ -278,11 +278,11 @@ async fn agents_md_not_reread_after_compact() { #[tokio::test] async fn compact_preserves_system_prompt() { let client = MockClient::new(vec![ - single_text_events("a"), // pod.run_text("first") - single_text_events("b"), // pod.run_text("second") + single_text_events("a"), // pod.run_text("first") + single_text_events("b"), // pod.run_text("second") write_summary_tool_use_events("call-1", "compacted summary"), // compact worker: tool_use - single_text_events("done"), // compact worker: close - single_text_events("c"), // pod.run_text("third") + single_text_events("done"), // compact worker: close + single_text_events("c"), // pod.run_text("third") ]); let (mut pod, _pwd) = make_pod_with_body("SP cwd={{ cwd }}", client) .await diff --git a/crates/protocol/src/lib.rs b/crates/protocol/src/lib.rs index ffbc8ab4..1e0a496f 100644 --- a/crates/protocol/src/lib.rs +++ b/crates/protocol/src/lib.rs @@ -11,11 +11,15 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "method", content = "params", rename_all = "snake_case")] pub enum Method { - Run { input: Vec }, + Run { + input: Vec, + }, /// Human-readable text injected into the target Pod's LLM context /// as a non-blocking system message. No side effects beyond LLM /// context; use `PodEvent` for typed lifecycle reports. - Notify { message: String }, + Notify { + message: String, + }, /// Typed lifecycle report from a child Pod to its direct parent. PodEvent(PodEvent), Resume, diff --git a/crates/provider/src/catalog.rs b/crates/provider/src/catalog.rs index 67f11fbd..3a7206e7 100644 --- a/crates/provider/src/catalog.rs +++ b/crates/provider/src/catalog.rs @@ -53,9 +53,7 @@ pub enum ResolveError { MalformedRef(String), #[error("model.ref points to unknown provider `{0}`")] UnknownProvider(String), - #[error( - "model.ref omitted; manifest must specify scheme, model_id, and auth (missing: {0})" - )] + #[error("model.ref omitted; manifest must specify scheme, model_id, and auth (missing: {0})")] InlineMissing(&'static str), } @@ -259,8 +257,8 @@ pub fn resolve_with_catalogs( models: &[ModelEntry], ) -> Result { if let Some(ref_str) = &manifest.ref_ { - let (provider_id, ref_model_id) = split_ref(ref_str) - .ok_or_else(|| ResolveError::MalformedRef(ref_str.clone()))?; + let (provider_id, ref_model_id) = + split_ref(ref_str).ok_or_else(|| ResolveError::MalformedRef(ref_str.clone()))?; let provider = providers .iter() .find(|p| p.id == provider_id) @@ -371,10 +369,7 @@ mod tests { let cfg = resolve_with_catalogs(&manifest, &providers, &models).unwrap(); assert_eq!(cfg.scheme, SchemeKind::Anthropic); assert_eq!(cfg.model_id, "claude-sonnet-4-6"); - assert_eq!( - cfg.base_url.as_deref(), - Some("https://api.anthropic.com") - ); + assert_eq!(cfg.base_url.as_deref(), Some("https://api.anthropic.com")); match cfg.auth { AuthRef::ApiKey { env, file } => { assert_eq!(env.as_deref(), Some("INSOMNIA_API_KEY_ANTHROPIC")); @@ -382,7 +377,10 @@ mod tests { } _ => panic!("expected ApiKey auth from provider hint"), } - assert!(cfg.capability.is_some(), "should fall back to provider.default_capability"); + assert!( + cfg.capability.is_some(), + "should fall back to provider.default_capability" + ); } #[test] diff --git a/crates/provider/src/codex_oauth/auth_json.rs b/crates/provider/src/codex_oauth/auth_json.rs index 4a0da884..6299df6c 100644 --- a/crates/provider/src/codex_oauth/auth_json.rs +++ b/crates/provider/src/codex_oauth/auth_json.rs @@ -44,7 +44,9 @@ impl AuthSnapshot { let refresh_token = tokens .get("refresh_token") .and_then(Value::as_str) - .ok_or_else(|| CodexAuthError::MalformedAuthJson("missing tokens.refresh_token".into()))? + .ok_or_else(|| { + CodexAuthError::MalformedAuthJson("missing tokens.refresh_token".into()) + })? .to_string(); let id_token = tokens @@ -58,9 +60,7 @@ impl AuthSnapshot { .get("account_id") .and_then(Value::as_str) .map(str::to_string) - .or_else(|| { - super::jwt::parse_chatgpt_claims(&id_token).and_then(|c| c.account_id) - }) + .or_else(|| super::jwt::parse_chatgpt_claims(&id_token).and_then(|c| c.account_id)) .ok_or_else(|| { CodexAuthError::MalformedAuthJson( "missing account_id in both tokens and id_token claims".into(), @@ -131,7 +131,10 @@ pub async fn persist_refreshed( } raw.as_object_mut() .ok_or_else(|| CodexAuthError::MalformedAuthJson("auth.json not an object".into()))? - .insert("last_refresh".into(), Value::String(Utc::now().to_rfc3339())); + .insert( + "last_refresh".into(), + Value::String(Utc::now().to_rfc3339()), + ); write_atomic(path, raw)?; AuthSnapshot::from_value(raw.clone()) @@ -139,9 +142,8 @@ pub async fn persist_refreshed( fn write_atomic(path: &Path, value: &Value) -> Result<(), CodexAuthError> { if let Some(parent) = path.parent() { - std::fs::create_dir_all(parent).map_err(|e| { - CodexAuthError::Io(format!("create_dir_all {}: {e}", parent.display())) - })?; + std::fs::create_dir_all(parent) + .map_err(|e| CodexAuthError::Io(format!("create_dir_all {}: {e}", parent.display())))?; } let json = serde_json::to_vec_pretty(value) .map_err(|e| CodexAuthError::Io(format!("serialize: {e}")))?; @@ -203,7 +205,10 @@ mod tests { assert_eq!(snap.account_id, "acc-1"); assert!(snap.last_refresh.is_some()); // 未知フィールドが raw に保持されている - assert_eq!(snap.raw.get("OPENAI_API_KEY").and_then(Value::as_str), Some("sk-extra")); + assert_eq!( + snap.raw.get("OPENAI_API_KEY").and_then(Value::as_str), + Some("sk-extra") + ); } #[tokio::test] @@ -238,7 +243,10 @@ mod tests { "agent_identity":{"workspace_id":"w","agent_runtime_id":"r","agent_private_key":"k","registered_at":"x"} }"#, ); - let updated = persist_refreshed(&path, None, Some("new-acc".into()), Some("new-ref".into())).await.unwrap(); + let updated = + persist_refreshed(&path, None, Some("new-acc".into()), Some("new-ref".into())) + .await + .unwrap(); assert_eq!(updated.access_token, "new-acc"); assert_eq!(updated.refresh_token, "new-ref"); // 未知フィールド agent_identity が保たれる @@ -258,7 +266,9 @@ mod tests { ); // 既存ファイルを 644 に変えてから persist → 600 に直るか std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o644)).unwrap(); - persist_refreshed(&path, None, Some("a2".into()), None).await.unwrap(); + persist_refreshed(&path, None, Some("a2".into()), None) + .await + .unwrap(); let mode = std::fs::metadata(&path).unwrap().permissions().mode() & 0o777; assert_eq!(mode, 0o600); } diff --git a/crates/provider/src/codex_oauth/mod.rs b/crates/provider/src/codex_oauth/mod.rs index e5a6e1e1..3a593c47 100644 --- a/crates/provider/src/codex_oauth/mod.rs +++ b/crates/provider/src/codex_oauth/mod.rs @@ -23,10 +23,7 @@ use std::sync::Arc; use async_trait::async_trait; use chrono::{Duration, Utc}; -use llm_worker::llm_client::{ - ClientError, - auth::AuthProvider, -}; +use llm_worker::llm_client::{ClientError, auth::AuthProvider}; use reqwest::header::{HeaderName, HeaderValue}; use tokio::sync::Mutex; @@ -66,8 +63,8 @@ impl CodexAuthProvider { let codex_home = if let Ok(p) = std::env::var("CODEX_HOME") { PathBuf::from(p) } else { - let home = std::env::var("HOME") - .map_err(|_| ClientError::Config("HOME not set".into()))?; + let home = + std::env::var("HOME").map_err(|_| ClientError::Config("HOME not set".into()))?; PathBuf::from(home).join(".codex") }; Ok(Self::new(codex_home)) @@ -142,7 +139,9 @@ impl CodexAuthProvider { Ok(new_snap) } - fn build_headers(snap: &AuthSnapshot) -> Result, CodexAuthError> { + fn build_headers( + snap: &AuthSnapshot, + ) -> Result, CodexAuthError> { let mut out = Vec::with_capacity(5); let auth_val = HeaderValue::from_str(&format!("Bearer {}", snap.access_token)) @@ -151,10 +150,7 @@ impl CodexAuthProvider { let acc_val = HeaderValue::from_str(&snap.account_id) .map_err(|e| CodexAuthError::InvalidHeader(format!("ChatGPT-Account-Id: {e}")))?; - out.push(( - HeaderName::from_static("chatgpt-account-id"), - acc_val, - )); + out.push((HeaderName::from_static("chatgpt-account-id"), acc_val)); // Cloudflare WAF は ChatGPT backend アクセス元を `originator` / // `User-Agent` で識別する。Codex CLI が送る固定値を流用しないと @@ -186,7 +182,10 @@ impl CodexAuthProvider { #[async_trait] impl AuthProvider for CodexAuthProvider { async fn headers(&self) -> Result, ClientError> { - let snap = self.ensure_fresh().await.map_err(CodexAuthError::to_client_error)?; + let snap = self + .ensure_fresh() + .await + .map_err(CodexAuthError::to_client_error)?; Self::build_headers(&snap).map_err(CodexAuthError::to_client_error) } } @@ -247,7 +246,10 @@ mod tests { let provider = CodexAuthProvider::new(dir.path().to_path_buf()); let headers = provider.headers().await.unwrap(); - let names: Vec<_> = headers.iter().map(|(n, _)| n.as_str().to_string()).collect(); + let names: Vec<_> = headers + .iter() + .map(|(n, _)| n.as_str().to_string()) + .collect(); assert!(names.contains(&"authorization".to_string())); assert!(names.contains(&"chatgpt-account-id".to_string())); assert!(!names.contains(&"x-openai-fedramp".to_string())); @@ -276,7 +278,12 @@ mod tests { #[tokio::test] async fn refreshes_when_expired_and_persists() { let dir = tempfile::tempdir().unwrap(); - let path = write_auth(dir.path(), Utc::now().timestamp() - 60, false, "old-refresh"); + let path = write_auth( + dir.path(), + Utc::now().timestamp() - 60, + false, + "old-refresh", + ); // refresh エンドポイントを mock。新しい JWT (将来 exp) を返す let server = MockServer::start().await; @@ -315,7 +322,12 @@ mod tests { #[tokio::test] async fn permanent_refresh_failure_surfaces_login_message() { let dir = tempfile::tempdir().unwrap(); - write_auth(dir.path(), Utc::now().timestamp() - 60, false, "bad-refresh"); + write_auth( + dir.path(), + Utc::now().timestamp() - 60, + false, + "bad-refresh", + ); let server = MockServer::start().await; Mock::given(method("POST")) diff --git a/crates/provider/src/codex_oauth/refresh.rs b/crates/provider/src/codex_oauth/refresh.rs index 97fc0168..ff15844e 100644 --- a/crates/provider/src/codex_oauth/refresh.rs +++ b/crates/provider/src/codex_oauth/refresh.rs @@ -97,7 +97,10 @@ fn extract_error_code(body: &str) -> Option { return Some(s.to_string()); } } - value.get("code").and_then(|v| v.as_str()).map(str::to_string) + value + .get("code") + .and_then(|v| v.as_str()) + .map(str::to_string) } #[cfg(test)] diff --git a/crates/provider/src/lib.rs b/crates/provider/src/lib.rs index 1a41d6ae..03501290 100644 --- a/crates/provider/src/lib.rs +++ b/crates/provider/src/lib.rs @@ -58,10 +58,7 @@ pub enum ProviderError { /// 1. `AuthRef::ApiKey { env, .. }` で env が指定されていればその変数を参照 /// 2. そうでなければ scheme 既定の環境変数 (`SchemeKind::default_env_var`) /// 3. それでも無ければ `file` を読む(絶対パスのみ) -fn resolve_auth( - scheme: SchemeKind, - auth: &AuthRef, -) -> Result { +fn resolve_auth(scheme: SchemeKind, auth: &AuthRef) -> Result { match auth { AuthRef::None => Ok(ResolvedAuth::None), AuthRef::ApiKey { env, file } => { @@ -161,9 +158,7 @@ pub fn build_client(manifest: &ModelManifest) -> Result, Prov /// `ModelManifest` から既に `catalog::resolve_model_manifest` を通した /// ケース(factory / spawn 経路でカタログ引きを 1 回だけにしたい等)で /// 使う。 -pub fn build_client_from_config( - config: &ModelConfig, -) -> Result, ProviderError> { +pub fn build_client_from_config(config: &ModelConfig) -> Result, ProviderError> { build_from_config(config) } diff --git a/crates/tui/src/app.rs b/crates/tui/src/app.rs index cad6ec84..5df5c6d0 100644 --- a/crates/tui/src/app.rs +++ b/crates/tui/src/app.rs @@ -136,19 +136,14 @@ impl App { } } } - Event::ToolCallDone { - id, arguments, .. - } => { + Event::ToolCallDone { id, arguments, .. } => { self.current_tool = None; if let Some(b) = self.find_tool_call_mut(&id) { b.arguments = Some(arguments); // Only advance the state when it's still in-flight. // If a ToolResult arrived out of order and already // transitioned us to Done/Error, keep that. - if matches!( - b.state, - ToolCallState::Pending | ToolCallState::Streaming - ) { + if matches!(b.state, ToolCallState::Pending | ToolCallState::Streaming) { b.state = ToolCallState::Executing; } } @@ -191,7 +186,12 @@ impl App { } }; if !is_error { - apply_cache_update(&mut self.cache, &name, args.as_deref(), output.as_deref()); + apply_cache_update( + &mut self.cache, + &name, + args.as_deref(), + output.as_deref(), + ); } } else { // Result for an unknown tool call. Surface it as an @@ -291,9 +291,7 @@ impl App { if let Block::ToolCall(tc) = b { if matches!( tc.state, - ToolCallState::Pending - | ToolCallState::Streaming - | ToolCallState::Executing + ToolCallState::Pending | ToolCallState::Streaming | ToolCallState::Executing ) { tc.state = ToolCallState::Incomplete; } else { @@ -450,7 +448,10 @@ impl App { // Incomplete so the replay matches live semantics. for b in self.blocks.iter_mut() { if let Block::ToolCall(tc) = b - && matches!(tc.state, ToolCallState::Executing | ToolCallState::Pending | ToolCallState::Streaming) + && matches!( + tc.state, + ToolCallState::Executing | ToolCallState::Pending | ToolCallState::Streaming + ) { tc.state = ToolCallState::Incomplete; } diff --git a/crates/tui/src/block.rs b/crates/tui/src/block.rs index d3029d72..0d173829 100644 --- a/crates/tui/src/block.rs +++ b/crates/tui/src/block.rs @@ -63,9 +63,15 @@ pub enum ToolCallState { /// `ToolCallDone` received, waiting on the tool result. Executing, /// `ToolResult { is_error: false, .. }` received. - Done { summary: String, output: Option }, + Done { + summary: String, + output: Option, + }, /// `ToolResult { is_error: true, .. }` received. - Error { summary: String, output: Option }, + Error { + summary: String, + output: Option, + }, /// Turn ended before a matching `ToolResult` arrived. Incomplete, } diff --git a/crates/tui/src/main.rs b/crates/tui/src/main.rs index 78b07897..edcf91a7 100644 --- a/crates/tui/src/main.rs +++ b/crates/tui/src/main.rs @@ -33,8 +33,12 @@ fn resolve_socket(pod_name: &str, override_path: Option) -> PathBuf { if let Some(p) = override_path { return p; } - manifest::paths::pod_socket_path(pod_name) - .unwrap_or_else(|| PathBuf::from("/tmp").join("insomnia").join(pod_name).join("sock")) + manifest::paths::pod_socket_path(pod_name).unwrap_or_else(|| { + PathBuf::from("/tmp") + .join("insomnia") + .join(pod_name) + .join("sock") + }) } enum Mode { @@ -172,7 +176,10 @@ async fn run( run_loop(terminal, &mut app, client, shutdown_pod_on_exit).await?; } Err(e) => { - app.push_error(format!("Failed to connect to {}: {e}", socket_path.display())); + app.push_error(format!( + "Failed to connect to {}: {e}", + socket_path.display() + )); terminal.draw(|f| ui::draw(f, &mut app))?; run_disconnected(&mut app)?; } diff --git a/crates/tui/src/spawn.rs b/crates/tui/src/spawn.rs index 48f4590d..b7658eea 100644 --- a/crates/tui/src/spawn.rs +++ b/crates/tui/src/spawn.rs @@ -17,12 +17,8 @@ use std::path::PathBuf; use std::process::Stdio; use std::time::Duration; -use crossterm::event::{ - self, Event as TermEvent, KeyCode, KeyEventKind, KeyModifiers, -}; -use manifest::{ - PodManifestConfig, find_project_manifest_from, load_layer, user_manifest_path, -}; +use crossterm::event::{self, Event as TermEvent, KeyCode, KeyEventKind, KeyModifiers}; +use manifest::{PodManifestConfig, find_project_manifest_from, load_layer, user_manifest_path}; use ratatui::Terminal; use ratatui::backend::CrosstermBackend; use ratatui::layout::{Constraint, Layout}; @@ -103,7 +99,10 @@ pub async fn run() -> Result { let project_layer = find_project_manifest_from(&cwd).and_then(|p| load_layer(&p).ok()); let mut cascade = PodManifestConfig::builtin_defaults(); - for layer in [user_layer.as_ref(), project_layer.as_ref()].into_iter().flatten() { + for layer in [user_layer.as_ref(), project_layer.as_ref()] + .into_iter() + .flatten() + { cascade = cascade.merge(layer.clone()); } let cascade_has_scope = !cascade.scope.allow.is_empty(); @@ -147,8 +146,7 @@ pub async fn run() -> Result { None => continue, Some(Action::Submit) => { if form.name.trim().is_empty() { - form.message = - Some(("name is required".to_string(), MessageKind::Error)); + form.message = Some(("name is required".to_string(), MessageKind::Error)); continue; } break; @@ -358,7 +356,10 @@ fn build_overlay_toml(form: &Form) -> String { ); rule.insert("permission".into(), toml::Value::String("write".into())); let mut scope = toml::value::Table::new(); - scope.insert("allow".into(), toml::Value::Array(vec![toml::Value::Table(rule)])); + scope.insert( + "allow".into(), + toml::Value::Array(vec![toml::Value::Table(rule)]), + ); root.insert("scope".into(), toml::Value::Table(scope)); } @@ -382,7 +383,6 @@ fn resolve_pod_command() -> PathBuf { PathBuf::from("pod") } - struct StderrTail { lines: std::collections::VecDeque, } @@ -529,7 +529,9 @@ fn name_line(form: &Form) -> Line<'_> { Span::styled("name: ", Style::default().fg(Color::DarkGray)), Span::styled( form.name.as_str(), - Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD), + Style::default() + .fg(Color::Cyan) + .add_modifier(Modifier::BOLD), ), ]) } diff --git a/crates/tui/src/tool.rs b/crates/tui/src/tool.rs index ae2cc957..949a7e70 100644 --- a/crates/tui/src/tool.rs +++ b/crates/tui/src/tool.rs @@ -74,9 +74,12 @@ fn render_read_aggregate(blocks: &[Block], start: usize, mode: Mode) -> ToolRend }) .collect(); - let in_progress = group - .iter() - .any(|tc| !matches!(tc.state, ToolCallState::Done { .. } | ToolCallState::Error { .. } | ToolCallState::Incomplete)); + let in_progress = group.iter().any(|tc| { + !matches!( + tc.state, + ToolCallState::Done { .. } | ToolCallState::Error { .. } | ToolCallState::Incomplete + ) + }); let paths: Vec = group.iter().map(|tc| read_path(tc)).collect(); let count = paths.len(); @@ -89,9 +92,7 @@ fn render_read_aggregate(blocks: &[Block], start: usize, mode: Mode) -> ToolRend } else { format!("Read — {count} file{} read", plural(count)) }; - lines.push(Line::from(vec![ - Span::styled(header, tool_style), - ])); + lines.push(Line::from(vec![Span::styled(header, tool_style)])); if matches!(mode, Mode::Overview) { return ToolRenderOutput { @@ -169,17 +170,15 @@ fn render_write(cache: &FileCache, tc: &ToolCallBlock, mode: Mode) -> Vec Vec Vec> { +fn render_edit( + cache: &FileCache, + tc: &ToolCallBlock, + width: u16, + mode: Mode, +) -> Vec> { let args = parsed_args(tc); let path = args .as_ref() @@ -296,9 +300,7 @@ fn build_edit_diff(content: &str, old: &str, new: &str, width: u16) -> Vec Vec NORMAL_MAX_BODY, @@ -565,17 +565,13 @@ fn render_default(tc: &ToolCallBlock, mode: Mode) -> Vec> { } else { format!("{} — {suffix}", tc.name) }; - return vec![Line::from(vec![ - Span::styled(label, tool_style), - ])]; + return vec![Line::from(vec![Span::styled(label, tool_style)])]; } - let mut lines = vec![Line::from(vec![ - Span::styled( - format!("{} — {}", tc.name, state_suffix(&tc.state)), - tool_style, - ), - ])]; + let mut lines = vec![Line::from(vec![Span::styled( + format!("{} — {}", tc.name, state_suffix(&tc.state)), + tool_style, + )])]; let args_pretty = parsed_args(tc) .and_then(|v| serde_json::to_string_pretty(&v).ok()) @@ -589,7 +585,9 @@ fn render_default(tc: &ToolCallBlock, mode: Mode) -> Vec> { &mut lines, &args_pretty, arg_cap, - Style::default().fg(Color::DarkGray).add_modifier(Modifier::ITALIC), + Style::default() + .fg(Color::DarkGray) + .add_modifier(Modifier::ITALIC), ); let summary_source: String = match &tc.state { @@ -660,12 +658,7 @@ fn maybe_error_line(lines: &mut Vec>, state: &ToolCallState) { } } -fn emit_capped_lines( - out: &mut Vec>, - text: &str, - cap: usize, - style: Style, -) { +fn emit_capped_lines(out: &mut Vec>, text: &str, cap: usize, style: Style) { let all: Vec<&str> = text.lines().collect(); let shown = all.len().min(cap); for l in &all[..shown] { diff --git a/crates/tui/src/ui.rs b/crates/tui/src/ui.rs index f740a5a1..badbb3ea 100644 --- a/crates/tui/src/ui.rs +++ b/crates/tui/src/ui.rs @@ -22,7 +22,7 @@ use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; use protocol::{AlertLevel, Greeting, Segment}; -use crate::app::{App, fmt_tokens, alert_source_label}; +use crate::app::{App, alert_source_label, fmt_tokens}; use crate::block::{Block, CompactEvent}; /// Display density for the history view. @@ -64,10 +64,10 @@ pub fn draw(frame: &mut Frame, app: &mut App) { let input_height = input_area_height(&input_render, area.height); let chunks = Layout::vertical([ - Constraint::Min(0), // history view - Constraint::Length(1), // separator - Constraint::Length(1), // status - Constraint::Length(input_height), // input area + Constraint::Min(0), // history view + Constraint::Length(1), // separator + Constraint::Length(1), // status + Constraint::Length(input_height), // input area ]) .split(area); @@ -219,21 +219,20 @@ fn wrap_line_into(line: Line<'static>, width: u16, out: &mut Vec>) *pending_width = 0; }; - let push_row = |current: &mut Vec>, - row_width: &mut usize, - out: &mut Vec>| { - if fill_to_width && *row_width < w { - let pad = w - *row_width; - current.push(Span::styled(" ".repeat(pad), line_style)); - *row_width = w; - } - let mut l = Line::from(std::mem::take(current)).style(line_style); - if let Some(a) = alignment { - l = l.alignment(a); - } - out.push(l); - *row_width = 0; - }; + let push_row = + |current: &mut Vec>, row_width: &mut usize, out: &mut Vec>| { + if fill_to_width && *row_width < w { + let pad = w - *row_width; + current.push(Span::styled(" ".repeat(pad), line_style)); + *row_width = w; + } + let mut l = Line::from(std::mem::take(current)).style(line_style); + if let Some(a) = alignment { + l = l.alignment(a); + } + out.push(l); + *row_width = 0; + }; for span in line.spans { if !pending.is_empty() && span.style != pending_style { @@ -276,12 +275,7 @@ fn wrap_line_into(line: Line<'static>, width: u16, out: &mut Vec>) push_row(&mut current, &mut row_width, out); } -fn render_block_into( - lines: &mut Vec>, - block: &Block, - width: u16, - mode: Mode, -) { +fn render_block_into(lines: &mut Vec>, block: &Block, width: u16, mode: Mode) { match block { Block::Greeting(g) => match mode { Mode::Overview => { @@ -426,10 +420,7 @@ fn segment_display_text(seg: &Segment) -> String { match seg { Segment::Text { content } => content.replace('\n', " "), Segment::Paste { - id, - chars, - lines, - .. + id, chars, lines, .. } => format!("[Clipboard #{id} | {chars} chars, {lines} lines]"), Segment::FileRef { path } => format!("@{path}"), Segment::KnowledgeRef { slug } => format!("#{slug}"), @@ -554,16 +545,19 @@ fn render_compact(lines: &mut Vec>, evt: &CompactEvent, width: u16 let (text, kind) = match evt { CompactEvent::Start => ("[compact] starting".to_owned(), MessageKind::NoticeWarn), CompactEvent::Done { new_session_id } => { - let short = new_session_id.to_string().chars().take(8).collect::(); + let short = new_session_id + .to_string() + .chars() + .take(8) + .collect::(); ( format!("[compact] done (new session {short})"), MessageKind::NoticeWarn, ) } - CompactEvent::Failed { error } => ( - format!("[compact error] {error}"), - MessageKind::NoticeError, - ), + CompactEvent::Failed { error } => { + (format!("[compact error] {error}"), MessageKind::NoticeError) + } }; match mode { Mode::Overview => push_overview_line(lines, &text, width, kind, ""), @@ -772,4 +766,3 @@ pub fn kind_style(kind: MessageKind) -> Style { .add_modifier(Modifier::BOLD), } } -