Keep a typed path as typed, and expand ~ where it is used
A llama session's tools all answered "failed to spawn process [exit code: -1]": `llama-server` takes the working directory as an `x-tool-cwd` header and `chdir`s to it with no shell in the way, so a `~/…` cwd named a directory of that name. `files::resolve_blocking` asks the machine that will serve the session what the path is, and the driver does that once at launch. The other half is the storing. `~` and `/home/someone` are a path and a snapshot of where it pointed, and it is the snapshot that breaks when an account is renamed -- so `machines::tidy` no longer expands one and `set_cwd` no longer contracts one (`shorten_home` is gone with it). The identity file is expanded at the point `ssh` is invoked instead. Spawn now asks the same question of a typed working directory that `set_cwd` already did: absolute or home-relative, and actually there on the machine that will run it. It accepted anything, so a typo became a session whose process could not start, reported later and pointing at nothing. Also: a provider written before `mcp_servers` existed adopts the defaults a probe would give it now, so a machine discovered before 2026-09-19 stops silently having no web search. Exercised end to end against a real llama session spawned with `cwd: "~/repos/ai-app/server"`: `exec_shell_command` with `pwd` answered `/home/bob/repos/ai-app/server`, exit 0.
This commit is contained in:
1 parent
bd9596d782
commit
3b309766d7
8 files changed
+201
-101
No files matched your search
@@ -378,9 +378,11 @@ struct Shared {
|
||||
/// are changeable while the session runs: they ride on the next request,
|
||||
/// so unlike the server's own flags there is nothing to reload.
|
||||
sampling: Mutex<serde_json::Map<String, Value>>,
|
||||
/// The session's working directory, which is where its tools act. `None`
|
||||
/// leaves that to `llama-server`, which is the honest answer rather than a
|
||||
/// guess at one.
|
||||
/// The session's working directory, which is where its tools act, resolved
|
||||
/// on the machine serving this session. Absolute, because it is handed to
|
||||
/// `llama-server` as a header it `chdir`s to with no shell to expand a
|
||||
/// `~`. `None` leaves the directory to `llama-server`, which is the honest
|
||||
/// answer rather than a guess at one.
|
||||
cwd: Option<String>,
|
||||
/// Set by [`Driver::interrupt`]; the streaming loop and the tool loop both
|
||||
/// check it, leaving what was produced in the transcript.
|
||||
@@ -474,16 +476,30 @@ impl LlamaDriver {
|
||||
|
||||
let sampling = sampling_from(&meta.params);
|
||||
|
||||
// Resolved on the machine that will serve this session, because it is
|
||||
// the one path here that reaches something with no shell in front of
|
||||
// it: `llama-server` `chdir`s to the header it is given. See
|
||||
// `files::resolve_blocking`. A failure is the launch's, for the same
|
||||
// reason a CLI session whose directory has gone fails at its own `cd`
|
||||
// -- tools running somewhere other than where they were told to is
|
||||
// worse than not starting.
|
||||
let cwd = meta
|
||||
.cwd
|
||||
.as_ref()
|
||||
.map(|path| {
|
||||
crate::files::resolve_blocking(transport, &path.to_string_lossy()).with_context(
|
||||
|| format!("this session's working directory, {}", path.display()),
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let driver = Self {
|
||||
shared: Arc::new(Shared {
|
||||
sink,
|
||||
transcript: transcript.to_path_buf(),
|
||||
session_dir: session_dir.to_path_buf(),
|
||||
sampling: Mutex::new(sampling),
|
||||
cwd: meta
|
||||
.cwd
|
||||
.as_ref()
|
||||
.map(|path| path.to_string_lossy().into_owned()),
|
||||
cwd,
|
||||
cancel: AtomicBool::new(false),
|
||||
turns: Mutex::new(Turns::default()),
|
||||
serving: Mutex::new(Serving::Loading),
|
||||
|
||||
@@ -172,6 +172,12 @@ impl Tools {
|
||||
/// say they use one. A session with no working directory sends none, and
|
||||
/// `llama-server` falls back to its own -- which is the honest outcome:
|
||||
/// this server has no better answer for where "here" is.
|
||||
///
|
||||
/// It must already be **absolute**: the header is passed to `chdir` with
|
||||
/// no shell in the way, so a leading `~` is a directory of that name and
|
||||
/// every tool using one fails with "failed to spawn process". The driver
|
||||
/// resolves it on the serving machine at launch; see
|
||||
/// `files::resolve_blocking`.
|
||||
pub fn execute(
|
||||
&self,
|
||||
name: &str,
|
||||
|
||||
Reference in new issue
Block a user