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
3 changes: 3 additions & 0 deletions crates/codegen/xai-grok-workspace/src/file_system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ pub use fs::{AsyncFileSystem, AsyncFsWrapper, FsError, bytes_to_string};
mod local_fs;
pub use local_fs::LocalFs;

mod runtime_fs;
pub use runtime_fs::RuntimeFs;

mod mock_fs;
pub use mock_fs::MockFs;

Expand Down
79 changes: 79 additions & 0 deletions crates/codegen/xai-grok-workspace/src/file_system/runtime_fs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//! Workspace filesystem adapter backed by the Simplicio Runtime.
//!
//! Project content must not be read, written, or deleted through the host
//! filesystem. The tools crate owns the Runtime/Agent handshake; this thin
//! adapter lets workspace session state use the same fail-closed boundary.

use std::path::{Path, PathBuf};

use crate::file_system::{AsyncFileSystem, FsError};
use xai_grok_tools::computer::types::AsyncFileSystem as ToolAsyncFileSystem;
use xai_grok_tools::computer::local::SimplicioRuntimeFs;

/// Workspace filesystem whose project effects are delegated to Runtime.
pub struct RuntimeFs {
root: PathBuf,
runtime: SimplicioRuntimeFs,
}

impl RuntimeFs {
pub fn new(root: PathBuf) -> Self {
Self {
runtime: SimplicioRuntimeFs::new(root.clone()),
root,
}
}

fn map_error(error: xai_grok_tools::computer::types::ComputerError) -> FsError {
FsError::Other(format!("Simplicio Runtime denied workspace operation: {error}"))
}

fn is_not_found(error: &xai_grok_tools::computer::types::ComputerError) -> bool {
let message = error.to_string().to_ascii_lowercase();
message.contains("not found") || message.contains("resource_not_found")
}
}

#[async_trait::async_trait]
impl AsyncFileSystem for RuntimeFs {
fn root(&self) -> &Path {
&self.root
}

async fn exists(&self, path: &Path) -> Result<bool, FsError> {
match self.runtime.stat_workspace(path).await {
Ok(_) => Ok(true),
Err(error) if Self::is_not_found(&error) => Ok(false),
Err(error) => Err(Self::map_error(error)),
}
}

async fn read_file(&self, path: &Path) -> Result<Vec<u8>, FsError> {
self.runtime
.read_file(path)
.await
.map_err(Self::map_error)
}

async fn try_read_file(&self, path: &Path) -> Result<Option<Vec<u8>>, FsError> {
match self.runtime.read_file(path).await {
Ok(bytes) => Ok(Some(bytes)),
Err(error) if Self::is_not_found(&error) => Ok(None),
Err(error) => Err(Self::map_error(error)),
}
}

async fn write_file(&self, path: &Path, data: &[u8]) -> Result<(), FsError> {
self.runtime
.write_file(path, data)
.await
.map_err(Self::map_error)
}

async fn delete_file(&self, path: &Path) -> Result<(), FsError> {
self.runtime
.delete_file(path)
.await
.map_err(Self::map_error)
}
}
11 changes: 9 additions & 2 deletions crates/codegen/xai-grok-workspace/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub(crate) mod swap_policy;
pub mod tool_config;
use crate::capability::CapabilityMode;
use crate::config::{MemoryConfig, SessionContextFactory};
use crate::file_system::{AsyncFsWrapper, LocalFs};
use crate::file_system::{AsyncFsWrapper, LocalFs, RuntimeFs};
use crate::hub::{HubConfig, HubHandle};
use crate::session::file_state::FileStateTracker;
use parking_lot::RwLock;
Expand Down Expand Up @@ -165,7 +165,14 @@ impl WorkspaceSession {
Some((handle, rx)) => (Some(handle), Some(rx)),
None => (None, None),
};
let async_fs = AsyncFsWrapper::new(Arc::new(LocalFs::new(cwd.clone())));
// Unit tests use LocalFs fixtures because they do not start the
// independent Agent host. Every production session uses RuntimeFs;
// project content therefore fails closed when Agent/Runtime is absent.
let async_fs = if cfg!(test) {
AsyncFsWrapper::new(Arc::new(LocalFs::new(cwd.clone())))
} else {
AsyncFsWrapper::new(Arc::new(RuntimeFs::new(cwd.clone())))
};
let file_state_tracker = Arc::new(FileStateTracker::new());
let checkpoint_store =
crate::session::checkpoint_store::CheckpointStore::new(&cwd, &session_id);
Expand Down
Loading