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:
iris-ai committed 2026-09-20 17:06:13 -04:00
1 parent bd9596d782
commit 3b309766d7
8 files changed
+201 -101

No files matched your search

+16
View File
@@ -471,6 +471,22 @@ written, and the fold uses that same predicate to decide a reply is settled.
`generate` now fails the turn for both -- a reply that stops early is not a
reply, and the transcript keeps whatever arrived before it.
- **A path is stored as it was typed, and `~` is expanded where it is used.**
`~/repos/x` and `/home/someone/repos/x` are a path and a snapshot of where it
pointed, and the snapshot is what breaks when an account is renamed or the
value is read on another machine -- so nothing at the boundary rewrites one
in either direction (`machines::tidy` used to expand and `shorten_home` used
to contract; both are gone). Expansion belongs to the machine the path is on:
`ssh::quote_path` and `files::PATH_PRELUDE` for a remote one,
`ssh::expand_home` for one here. The exception that proves it is
**`llama-server`'s tools**, which take the working directory as an
`x-tool-cwd` header and `chdir` to it with no shell in the way: a `~` arrives
there as a directory of that name and *every* tool using one answers "failed
to spawn process\n[exit code: -1]", which on the phone looks like a session
whose tools are all broken. `files::resolve_blocking` is what the llama
driver resolves it with at launch, on the machine that will serve the
session.
- **A transcript outlives the enum.** Removing `Event::TaskNote` hours after
adding it made every transcript that had recorded one unreadable, so
`launch` failed for those sessions and `SessionManager::new` skipped them —