Skip to content

feat(sub-agents): one kind of sub-agent, with an optional workspace that copies the whole project - #281

Draft
daniel-kurzynski wants to merge 9 commits into
mainfrom
feat/sub-agent-workspaces
Draft

daniel-kurzynski wants to merge 9 commits into
mainfrom
feat/sub-agent-workspaces

Conversation

@daniel-kurzynski

Copy link
Copy Markdown
Collaborator

Stacked on #280. Every sub-agent can now write. Sub-agents that change files alongside others can work in a full copy of the project of their own.

One kind of sub-agent

  • The read_only / default mode split is gone. Every sub-agent has the editing tools and the session's MCP tools; an old mode field in stored input is ignored.
  • Every spawn_agent call of a turn runs in parallel. By default sub-agents share the parent's tree; fs_explorer now locks each path and replaces files atomically, so concurrent edits are not lost.

Workspaces

  • With worktree: true (the main agent decides per call), a sub-agent in a git project works in a workspace of its own. It's a git worktree on a branch of its own, filled with a copy-on-write clone of everything in the parent's tree: uncommitted work, build outputs, installed dependencies and local configuration. So the sub-agent can build and test right away, without a setup step.
  • .git is not copied: the worktree shares the parent's repository, so its branch is visible there and the merge back needs no fetch. Worktrees nested in the project are left out.
  • The changes are measured against a snapshot of the copy itself. Edits made to the parent's tree while it was being copied are not taken for the sub-agent's.
  • When the sub-agent finishes, its changes (content and executable bit) are merged into the parent's working tree all-or-nothing. On a conflict nothing is applied and the changes stay on a branch the parent is told about. Files ignored by git are not merged back.
  • Clones use clonefile on macOS (APFS) and reflinks on Linux (btrfs, XFS). Where the file system can't clone files, the workspace is a checkout of the snapshot without ignored files, as before.
  • A sub-agent's working directory is its workspace, not the parent's tree.

Limits

  • Files copied from the parent can still point at its checkout through absolute paths (for example a virtualenv's scripts or editable installs).
  • Submodules are not set up in the copy.

Tests

  • Workspace: ignored files come along but are not merged back, the copy starts clean in git status, nested worktrees are skipped, two workspaces editing one file both land, a conflict keeps a branch, abandoning keeps partial changes.
  • Clone: everything but .git and skipped paths is copied, links stay links, and the copy is independent of the original.
  • Sub-agents share the tree unless asked for a workspace, and work in their workspace, not the parent's tree.

Docs: docs/sub-agents-feature.md.

@daniel-kurzynski
daniel-kurzynski marked this pull request as draft October 27, 2026 13:39
Base automatically changed from feat/sub-agents to main October 27, 2026 17:24
@daniel-kurzynski
daniel-kurzynski force-pushed the feat/sub-agent-workspaces branch from a575fa4 to 2d13b3c Compare October 28, 2026 10:20
MCP tools were tagged only for the main agent's scopes, so sub-agents
never saw them. Tag them for the sub-agent default scopes too; sub-agents
share the parent's registry and session config, so the session's MCP
on/off switches and :mcp: resources carry over.
Several agents can work in one tree at once, each with its own Explorer.
Their read-modify-write sequences could interleave and silently drop an
edit, and std::fs::write let readers see a truncated file.

Writers now hold a process-wide per-path lock across read, apply, format
and read-back, and files are replaced via a sibling temp file and rename
(written through symlinks, keeping permissions).
In a git project a writing sub-agent now starts in a worktree created from
a snapshot of the parent's tree (uncommitted and untracked work included,
index and HEAD untouched). When it finishes, its changes are three-way
merged into the parent's working tree, all or nothing, under the per-path
write locks. On a conflict nothing is applied; the changes stay on a
branch and the parent is told how to resolve them. A cancelled or failed
sub-agent applies nothing and keeps partial changes on a branch.

Writing sub-agents can therefore run in parallel and in the background.
Outside git they still take turns in the shared tree.
With every sub-agent working in its own worktree (git projects) or under
the per-file write locks (elsewhere), the read-only/default split is no
longer needed for safety. spawn_agent loses its mode parameter (an old
`mode` in saved sessions is ignored), SubAgentMode and the
SubAgentReadOnly scope go, every sub-agent gets the full tool scope and
may run in the background, and MCP tools are offered to sub-agents.
The merge compared content only, so a sub-agent's chmod alone was
dropped, and a content merge took the sub-agent's mode even when only the
parent had changed it. Compare content and executable bit, and let the
side that changed the bit win. A path deleted here but changed by the
sub-agent is now reported as such instead of as overlapping edits.
Every sub-agent in a git project got its own worktree, including ones
that only research: a full checkout per sub-agent, and builds there start
without any build outputs. spawn_agent now takes worktree: true for
sub-agents that change files alongside others; by default a sub-agent
shares the parent's tree, guarded by the per-file write locks. Whether a
sub-agent writes cannot be told from its tools (MCP tools), so the main
agent decides per call. The runner gets SubAgentOptions instead of a bare
require_file_references flag.
…'s tree

A sub-agent given a worktree only registered it as an extra project; its
session config still named the parent's project, so the system prompt
offered the parent's tree as the initial project and writing sub-agents
changed the user's checkout while their worktree stayed empty.

The sub-agent's directory is now its worktree path, so it becomes the
initial project; init_path stays the parent's for per-project tool choices.
A sub-agent's worktree was a fresh checkout of a snapshot: build outputs,
installed dependencies and local configuration were missing, so it had
to set them up before it could build or test.

The worktree is now created without a checkout and filled with a
copy-on-write clone of everything in the parent's tree, ignored files
included (clonefile on macOS, reflinks on Linux). .git is not copied: the
worktree shares the repository, so its branch and the merge back work as
before. Worktrees nested in the project are left out. The commit the
changes are measured against is a snapshot of the copy itself, so edits
made to the parent's tree while copying are not taken for the
sub-agent's. Where the file system cannot clone files, the worktree is a
checkout of the snapshot as before.
@daniel-kurzynski
daniel-kurzynski force-pushed the feat/sub-agent-workspaces branch from 2d13b3c to 78eaec9 Compare November 4, 2026 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant