fix: separate workspace root from cwd

This commit is contained in:
2026-06-12 01:03:33 +09:00
parent 23a5b53807
commit 7eff9301b9
14 changed files with 314 additions and 254 deletions
+3 -3
View File
@@ -76,7 +76,7 @@ pub(crate) struct BashParams {
pub(crate) struct BashTool {
/// Workspace root that every invocation starts in. Snapshot of
/// `ScopedFs::pwd()` at registration time; never mutated, since we
/// `ScopedFs::cwd()` at registration time; never mutated, since we
/// don't track `cd` across calls.
cwd: PathBuf,
/// Directory to spill long outputs into. Caller is expected to have
@@ -329,7 +329,7 @@ fn shell_single_quote(s: &str) -> String {
///
/// `output_dir` is where long outputs spill to; the caller is responsible
/// for arranging that the path is in the agent's readable scope. Every
/// invocation starts at `fs.pwd()` — the tool is intentionally stateless
/// invocation starts at `fs.cwd()` — the tool is intentionally stateless
/// w.r.t. the working directory.
pub fn bash_tool(fs: ScopedFs, output_dir: PathBuf) -> ToolDefinition {
Arc::new(move || {
@@ -339,7 +339,7 @@ pub fn bash_tool(fs: ScopedFs, output_dir: PathBuf) -> ToolDefinition {
.description(DESCRIPTION)
.input_schema(schema_value);
let tool: Arc<dyn Tool> = Arc::new(BashTool {
cwd: fs.pwd().to_path_buf(),
cwd: fs.cwd().to_path_buf(),
output_dir: output_dir.clone(),
spilled_outputs: std::sync::Mutex::new(Vec::new()),
});
+1 -1
View File
@@ -52,7 +52,7 @@ impl Tool for GlobTool {
let base = params
.path
.clone()
.unwrap_or_else(|| self.fs.pwd().to_path_buf());
.unwrap_or_else(|| self.fs.cwd().to_path_buf());
let pattern = params.pattern.clone();
let scope = self.fs.scope().clone();
+1 -1
View File
@@ -96,7 +96,7 @@ impl Tool for GrepTool {
"Grep"
);
let default_base = self.fs.pwd().to_path_buf();
let default_base = self.fs.cwd().to_path_buf();
let scope = self.fs.scope().clone();
let report = tokio::task::spawn_blocking(move || run_grep(default_base, params, &scope))
.await
+9 -9
View File
@@ -2,7 +2,7 @@
//!
//! `ScopedFs` is the write/read gate layered on top of a [`manifest::Scope`]
//! and a Pod's working directory. The scope decides which paths are
//! readable and writable; the pwd is carried alongside for convenience
//! readable and writable; the cwd is carried alongside for convenience
//! (Glob/Grep default their search base to it).
//!
//! `ScopedFs` is cheap to clone (`Arc` inside) and carries no per-session
@@ -20,7 +20,7 @@ use crate::error::ToolsError;
#[derive(Debug)]
struct ScopedFsInner {
scope: SharedScope,
pwd: PathBuf,
cwd: PathBuf,
}
/// Scope-aware filesystem handle. Clone-cheap (`Arc` inside).
@@ -60,20 +60,20 @@ pub struct SymlinkInfo {
}
impl ScopedFs {
/// Create a new [`ScopedFs`] wrapping `scope` and `pwd` in a fresh
/// Create a new [`ScopedFs`] wrapping `scope` and `cwd` in a fresh
/// [`SharedScope`]. Use [`ScopedFs::with_shared_scope`] when you
/// need the resulting `ScopedFs` to share scope state with another
/// holder of the `SharedScope` (typically the Pod).
pub fn new(scope: Scope, pwd: PathBuf) -> Self {
Self::with_shared_scope(SharedScope::new(scope), pwd)
pub fn new(scope: Scope, cwd: PathBuf) -> Self {
Self::with_shared_scope(SharedScope::new(scope), cwd)
}
/// Build a [`ScopedFs`] over an existing [`SharedScope`]. The
/// resulting handle and any future updates the caller pushes to
/// `scope` are observed by every clone of this `ScopedFs`.
pub fn with_shared_scope(scope: SharedScope, pwd: PathBuf) -> Self {
pub fn with_shared_scope(scope: SharedScope, cwd: PathBuf) -> Self {
Self {
inner: Arc::new(ScopedFsInner { scope, pwd }),
inner: Arc::new(ScopedFsInner { scope, cwd }),
}
}
@@ -93,8 +93,8 @@ impl ScopedFs {
/// The Pod's working directory. Glob/Grep default their search base
/// to this path when callers omit an explicit `path` parameter.
pub fn pwd(&self) -> &Path {
&self.inner.pwd
pub fn cwd(&self) -> &Path {
&self.inner.cwd
}
// =========================================================================