Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/crates/core/src/agentic/agents/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,11 @@ impl AgentRegistry {
description,
tools,
prompt,
if review { true } else { readonly.unwrap_or(old.readonly) },
if review {
true
} else {
readonly.unwrap_or(old.readonly)
},
old.path.clone(),
old.kind,
);
Expand Down
16 changes: 8 additions & 8 deletions src/crates/core/src/agentic/coordination/coordinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
//! Top-level component that integrates all subsystems and provides a unified interface

use super::{scheduler::DialogSubmissionPolicy, turn_outcome::TurnOutcome};
use crate::agentic::WorkspaceBinding;
use crate::agentic::agents::get_agent_registry;
use crate::agentic::core::{
Message, MessageContent, ProcessingPhase, PromptEnvelope, Session, SessionConfig, SessionKind,
SessionState, SessionSummary, TurnStats, has_prompt_markup,
has_prompt_markup, Message, MessageContent, ProcessingPhase, PromptEnvelope, Session,
SessionConfig, SessionKind, SessionState, SessionSummary, TurnStats,
};
use crate::agentic::events::{
AgenticEvent, EventPriority, EventQueue, EventRouter, EventSubscriber,
Expand All @@ -17,8 +16,9 @@ use crate::agentic::fork::{ForkContextSnapshot, ForkExecutionRequest, ForkExecut
use crate::agentic::image_analysis::ImageContextData;
use crate::agentic::round_preempt::DialogRoundPreemptSource;
use crate::agentic::session::SessionManager;
use crate::agentic::tools::ToolRuntimeRestrictions;
use crate::agentic::tools::pipeline::{SubagentParentInfo, ToolPipeline};
use crate::agentic::tools::ToolRuntimeRestrictions;
use crate::agentic::WorkspaceBinding;
use crate::service::bootstrap::{
ensure_workspace_persona_files_for_prompt, is_workspace_bootstrap_pending,
};
Expand All @@ -29,8 +29,8 @@ use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::sync::OnceLock;
use tokio::sync::{OwnedSemaphorePermit, RwLock, Semaphore, mpsc, watch};
use tokio::time::{Duration, Instant, sleep};
use tokio::sync::{mpsc, watch, OwnedSemaphorePermit, RwLock, Semaphore};
use tokio::time::{sleep, Duration, Instant};
use tokio_util::sync::CancellationToken;

const MANUAL_COMPACTION_COMMAND: &str = "/compact";
Expand Down Expand Up @@ -2231,8 +2231,8 @@ Update the persona files and delete BOOTSTRAP.md as soon as bootstrap is complet
};

// Create dynamic deadline via watch channel so it can be adjusted at runtime.
let initial_deadline = timeout_seconds
.map(|seconds| Instant::now() + Duration::from_secs(seconds));
let initial_deadline =
timeout_seconds.map(|seconds| Instant::now() + Duration::from_secs(seconds));
let (deadline_tx, mut deadline_rx) = watch::channel(initial_deadline);

// Check cancel token (before creating session)
Expand Down
3 changes: 2 additions & 1 deletion src/crates/core/src/agentic/execution/stream_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,8 @@ impl StreamProcessor {
for tool_call in tool_calls {
trace!(
"Cleaning up tool: {} ({})",
tool_call.tool_name, tool_call.tool_id
tool_call.tool_name,
tool_call.tool_id
);

let tool_event = if is_user_cancellation {
Expand Down
2 changes: 1 addition & 1 deletion src/crates/core/src/agentic/execution/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

use crate::agentic::core::Message;
use crate::agentic::round_preempt::DialogRoundPreemptSource;
use crate::agentic::tools::ToolRuntimeRestrictions;
use crate::agentic::tools::pipeline::SubagentParentInfo;
use crate::agentic::tools::ToolRuntimeRestrictions;
use crate::agentic::workspace::WorkspaceServices;
use crate::agentic::WorkspaceBinding;
use serde_json::Value;
Expand Down
3 changes: 2 additions & 1 deletion src/crates/core/src/agentic/tools/framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ impl ToolUseContext {
}

pub fn enforce_tool_runtime_restrictions(&self, tool_name: &str) -> BitFunResult<()> {
self.runtime_tool_restrictions.ensure_tool_allowed(tool_name)
self.runtime_tool_restrictions
.ensure_tool_allowed(tool_name)
}

pub fn enforce_path_operation(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::agentic::tools::framework::{
Tool, ToolRenderOptions, ToolResult, ToolUseContext, ValidationResult,
};
use crate::agentic::tools::ToolPathOperation;
use crate::agentic::tools::workspace_paths::is_bitfun_runtime_uri;
use crate::agentic::tools::ToolPathOperation;
use crate::util::errors::{BitFunError, BitFunResult};
use async_trait::async_trait;
use log::debug;
Expand Down
4 changes: 2 additions & 2 deletions src/crates/core/src/agentic/tools/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ pub mod image_context;
pub mod implementations;
pub mod input_validator;
pub mod pipeline;
pub mod restrictions;
pub mod registry;
pub mod restrictions;
pub mod user_input_manager;
pub mod workspace_paths;

pub use framework::{Tool, ToolResult, ToolUseContext, ValidationResult};
pub use image_context::{ImageContextData, ImageContextProvider, ImageContextProviderRef};
pub use input_validator::InputValidator;
pub use pipeline::*;
pub use restrictions::{ToolPathOperation, ToolPathPolicy, ToolRuntimeRestrictions};
pub use registry::{
create_tool_registry, get_all_registered_tool_names, get_all_registered_tools, get_all_tools,
get_readonly_registered_tool_names, get_readonly_tools,
};
pub use restrictions::{ToolPathOperation, ToolPathPolicy, ToolRuntimeRestrictions};
8 changes: 3 additions & 5 deletions src/crates/core/src/agentic/tools/restrictions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,7 @@ mod tests {
#[test]
fn denied_tool_names_override_allow_list() {
let restrictions = ToolRuntimeRestrictions {
allowed_tool_names: ["Write", "Edit"]
.into_iter()
.map(str::to_string)
.collect(),
allowed_tool_names: ["Write", "Edit"].into_iter().map(str::to_string).collect(),
denied_tool_names: ["Write"].into_iter().map(str::to_string).collect(),
path_policy: ToolPathPolicy::default(),
};
Expand All @@ -221,7 +218,8 @@ mod tests {

#[test]
fn local_path_containment_handles_missing_children() {
let root = std::env::temp_dir().join(format!("bitfun-restrictions-{}", uuid::Uuid::new_v4()));
let root =
std::env::temp_dir().join(format!("bitfun-restrictions-{}", uuid::Uuid::new_v4()));
std::fs::create_dir_all(root.join("allowed")).expect("create temp root");

let allowed_child = root.join("allowed").join("nested").join("file.txt");
Expand Down
62 changes: 60 additions & 2 deletions src/crates/core/src/service/snapshot/snapshot_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ impl FileSnapshotSystem {
compressed_content: match optimized_content {
OptimizedContent::Raw(data) => data,
OptimizedContent::Compressed(data) => data,
OptimizedContent::Reference(_) => unreachable!(),
OptimizedContent::Reference(_) => Vec::new(),
},
timestamp: SystemTime::now(),
metadata,
Expand Down Expand Up @@ -435,7 +435,8 @@ impl FileSnapshotSystem {
fn optimize_content(&self, content: &[u8]) -> OptimizedContent {
if self.dedup_enabled {
let hash = self.calculate_content_hash(content);
if self.hash_to_path.contains_key(&hash) {
let content_path = self.get_content_path(&hash);
if self.hash_to_path.contains_key(&hash) && content_path.exists() {
return OptimizedContent::Reference(hash);
}
}
Expand Down Expand Up @@ -837,3 +838,60 @@ impl FileSnapshotSystem {
self.get_baseline_snapshot_id(file_path).await.is_some()
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::service::workspace_runtime::{WorkspaceRuntimeContext, WorkspaceRuntimeTarget};

fn test_runtime_context() -> WorkspaceRuntimeContext {
let runtime_root =
std::env::temp_dir().join(format!("bitfun_snapshot_test_{}", Uuid::new_v4()));
WorkspaceRuntimeContext::new(
WorkspaceRuntimeTarget::LocalWorkspace {
workspace_root: runtime_root.join("workspace"),
},
runtime_root,
)
}

fn create_runtime_dirs(context: &WorkspaceRuntimeContext) {
for directory in context.required_directories() {
fs::create_dir_all(directory).expect("create runtime directory");
}
}

#[tokio::test]
async fn create_snapshot_reuses_empty_baseline_content_without_panicking() {
let context = test_runtime_context();
create_runtime_dirs(&context);

let file_path = context.runtime_root.join("workspace").join("empty.txt");
fs::create_dir_all(file_path.parent().expect("file has parent")).expect("create parent");

let mut snapshot_system = FileSnapshotSystem::new(context.clone());
snapshot_system
.initialize()
.await
.expect("initialize snapshots");
snapshot_system
.create_empty_baseline(&file_path)
.await
.expect("create empty baseline");

fs::write(&file_path, []).expect("write empty file");

let snapshot_id = snapshot_system
.create_snapshot(&file_path)
.await
.expect("create snapshot");
let restored = snapshot_system
.restore_snapshot_content(&snapshot_id)
.await
.expect("restore snapshot content");

assert!(restored.is_empty());

fs::remove_dir_all(&context.runtime_root).expect("cleanup runtime root");
}
}
5 changes: 1 addition & 4 deletions src/crates/core/tests/stream_processor_anthropic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,7 @@ async fn anthropic_extended_thinking_sse_produces_reasoning_and_text() {
result.usage.as_ref().map(|usage| usage.total_token_count),
Some(25)
);
assert_eq!(
result.thinking_signature.as_deref(),
Some("sig_abc123")
);
assert_eq!(result.thinking_signature.as_deref(), Some("sig_abc123"));

let thinking_chunks: Vec<(&str, bool)> = output
.events
Expand Down
Loading