acpmux: a chat sees the folders in its additionalDirectories, on every harness and start

Merged
TE
3d ago
+430-8

Leo asked for chats that can see any folders and files, on any harness, with nothing required to start one:

"but like also being able to have chatgpt (and in this case, any harnesses) see whatever folders/files would be cool"
"and you can also just start with nothing"

This is the acpmux half for folders. A chat can now be started with ACP's additionalDirectories on session/new, and every start of its harness receives them.

  • Validation. The folders are checked: absolute paths to existing directories, made canonical, each listed once, never the chat's own folder, at most 64. Anything else is refused with -32602. WebSocket callers are still held to the web roots first by remote_guard.rs, which already checked this field.
  • Storage. They are kept in SessionMeta::additional_directories, so a respawn, a restart and a daemon restart all keep them. A fork, a side chat and a handoff target carry them too. A fork's own folder is dropped from its list, and the agent's session/fork is asked with them.
  • Chats moved in from elsewhere. A guarded move (transfer_guard.rs) checks each folder against the move's roots, the same way it checks the chat's cwd, and drops any folder outside them. A crafted bundle can never widen what the chat sees.
  • Claude Code. Gets --add-dir DIR for each folder, at every spawn.
  • ACP harnesses. OpenCode, pi, Gemini and the Codex bridge get the field in session/new and session/load (acp_params). The field is left out when the list is empty, so nothing changes for existing chats.
  • Session summary. Lists additionalDirectories for the pane.
  • Pool. A chat that has folders never takes a pooled (prewarmed) harness, because the pooled one started without them.
  • No project required. A chat started with nothing still gets them. It runs in its empty agent-home folder, the folder the app gives a chat with no project.

Not in this PR (follow-ups):

  • Adding folders to a running chat. A new _acpmux method, plus a respawn at the end of the turn through profile_respawn.rs.
  • The web side: the composer chip, and Sources + taking several folders and files on any machine.
  • Codex writableRoots in the cmux-codex-acp bridge. Codex can already read outside its workspace, but writing needs the bridge to map this field.
  • On a remote-origin (sandboxed) Claude chain, the folders are passed as --add-dir, but the sandbox profile (Bound::for_session) only opens the chat's own folder. Inside the sandbox they are not reachable until the bounds include them.
  • A harness that ignores ACP additionalDirectories gets no fallback yet. A context note listing the paths is a follow-up, as is sending the field only to harnesses that advertise sessionCapabilities.additionalDirectories. ACP agents ignore unknown params, so sending it today is harmless.

Changelog

none (no UI yet)

Tests

  • Red first: e3a19633bf4 adds tests/additional_directories.rs. It was not run on the fleet at that commit, because a lane VM's cmux-ci runs only at the PR head. Without the fix, the summary key and the --add-dir words are absent, so its assertions fail.
  • tests/remote_guard.rs: the unix socket now refuses a relative additionalDirectories. The test now expects that, as the review asked.
  • cmux-ci test -- cargo test -p acpmux --test additional_directories and the hub::additional_dirs unit test: results in a comment below.

Observed / Not run

See the test comment. Not run: a live Claude Code or Codex run with real folders.

Agent session: agent session

šŸ¤– Generated with Claude Code