feat: start a new chat session in a managed worktree (web, iOS, Android)#100788
Conversation
Adds an additive worktree flag to sessions.create so any new chat can run in an isolated managed worktree of the agent's git workspace, with the branch checked out and .worktreeinclude provisioning applied. The session's spawnedCwd points at the matching subdirectory inside the worktree so chat runs, CLI, and the file browser execute there. agents.list gains workspaceGit (workspace or an ancestor is a git checkout) to gate the affordance; web sidebar, iOS, and Android expose a New-Chat-in-worktree action. Uses the method's operator.write scope, but the .openclaw/worktree-setup.sh step runs only for operator.admin callers since it executes repo code. Deleting the session, or leaving via a plain New Chat, clears the cwd and lossless-removes the worktree; idle GC treats recent session activity as worktree activity so an active session's checkout is never swept. Live-verified end-to-end on a real gateway; follow-up to #100535 (issue #100534).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7a419f5904
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| private var chatActionsMenu: some View { | ||
| Menu { | ||
| Button { | ||
| Task { await self.viewModel?.startNewSession() } |
There was a problem hiding this comment.
Disable New Chat while a response is pending
When a run is still pending, this new iOS menu path stays enabled and calls startNewSession directly. The pending-run guard in OpenClawChatViewModel.performSend only protects slash-command sends, and IOSGatewayChatTransport.CreateSessionParams does not send emitCommandHooks, so the gateway's parent-active check is skipped; tapping this during a response switches chats and leaves the active run attached to the old session.
Useful? React with 👍 / 👎.
| let current = path.resolve(start); | ||
| for (;;) { | ||
| if (existsSync(path.join(current, ".git"))) { |
There was a problem hiding this comment.
Resolve symlinked workspaces before Git preflight
For an agent workspace that is a symlink to a subdirectory inside a Git repo, path.resolve keeps the symlink path, so this ancestor walk never reaches the real repo's .git even though git -C <workspace> rev-parse --show-toplevel succeeds and ManagedWorktreeService.create would accept it. Because both the UI capability and sessions.create now rely on this helper, valid symlinked subdirectory workspaces incorrectly hide or reject the worktree chat action.
Useful? React with 👍 / 👎.
…aw#100788) Adds an additive worktree flag to sessions.create so any new chat can run in an isolated managed worktree of the agent's git workspace, with the branch checked out and .worktreeinclude provisioning applied. The session's spawnedCwd points at the matching subdirectory inside the worktree so chat runs, CLI, and the file browser execute there. agents.list gains workspaceGit (workspace or an ancestor is a git checkout) to gate the affordance; web sidebar, iOS, and Android expose a New-Chat-in-worktree action. Uses the method's operator.write scope, but the .openclaw/worktree-setup.sh step runs only for operator.admin callers since it executes repo code. Deleting the session, or leaving via a plain New Chat, clears the cwd and lossless-removes the worktree; idle GC treats recent session activity as worktree activity so an active session's checkout is never swept. Live-verified end-to-end on a real gateway; follow-up to openclaw#100535 (issue openclaw#100534).
…aw#100788) Adds an additive worktree flag to sessions.create so any new chat can run in an isolated managed worktree of the agent's git workspace, with the branch checked out and .worktreeinclude provisioning applied. The session's spawnedCwd points at the matching subdirectory inside the worktree so chat runs, CLI, and the file browser execute there. agents.list gains workspaceGit (workspace or an ancestor is a git checkout) to gate the affordance; web sidebar, iOS, and Android expose a New-Chat-in-worktree action. Uses the method's operator.write scope, but the .openclaw/worktree-setup.sh step runs only for operator.admin callers since it executes repo code. Deleting the session, or leaving via a plain New Chat, clears the cwd and lossless-removes the worktree; idle GC treats recent session activity as worktree activity so an active session's checkout is never swept. Live-verified end-to-end on a real gateway; follow-up to openclaw#100535 (issue openclaw#100534).
Related: #100534 — completes the session-level trigger deferred from #100535.
What Problem This Solves
Managed worktrees (#100535) could only be created by Workboard automations, the CLI, or the admin RPC. The everyday flow — "start this new chat in an isolated checkout", the composer toggle every major coding agent ships — did not exist:
sessions.createhad no workspace notion, so all chats ran directly in the agent's workspace and parallel sessions on one repo collided.Why This Change Was Made
Adds a
worktreeflag tosessions.create(additive; governed by the method's existingoperator.writescope — unlike the admin-gatedworktrees.*RPCs it never takes an arbitraryrepoRoot, only the agent's own configured workspace, which write-scope chat runs already mutate): the gateway materializes a managed worktree of the agent's workspace repo (ownerKind: "session", owner = the session key, idempotent per key), persists its path as the session'sspawnedCwd— which chat runs, CLI commands, and the session file browser already honor — and returns{ worktree: { id, path, branch } }. Agent summaries gainworkspaceGit(true when the workspace or an ancestor is a git checkout, matching the service's own repo discovery) so clients can gate the affordance. All three clients expose it: the web sidebar gets a split New-Chat action, iOS and Android add "New Chat in Worktree" menu actions, all gated onworkspaceGit. Deleting a session lossless-removes its worktree (snapshot-backed, dirty trees kept); idle GC treats recent session activity as worktree activity via an owner-activity hook, so a daily-used session's checkout can never be swept while only-abandoned ones expire.Non-goals: repo picker / branch-name input (agent workspace, auto names only), exposing raw
cwdto non-plugin clients, config surface (zero new keys).User Impact
Users can start any new chat in an isolated worktree — one button on web, a menu action on iOS/Android — when the agent's workspace is a git repo. The agent then works on its own
openclaw/<name>branch in its own checkout: parallel sessions stop stepping on each other and on the user's own edits. Cleanup is automatic and loss-proof: deleting the session removes a clean worktree (with a restore snapshot) and keeps a dirty one, and abandoned session worktrees age out via the existing GC with snapshots.New-chat worktree action (web sidebar, gated on
workspaceGit):Live-test state on the Worktrees page — three session worktrees active, one restorable after its session was deleted:
Evidence
Live end-to-end on a real gateway (isolated state dir, port 19801, real OpenAI model turn):
agents.listreportsworkspaceGit: true; sidebar shows the split action on a fresh page loadopenclaw/wt-*branch checked out,.worktreeincludecopied.env,.openclaw/worktree-setup.shran,spawnedCwdpersistedhello-from-worktree.txtinside the worktree; the source workspace stayed untouchedsessions.delete: dirty worktree kept (fail-safe); clean worktree removed withOpenClaw worktree snapshot: run-endpinned atrefs/openclaw/snapshots/<id>and branch deleted; appears as Restorable in the UI (second screenshot)Gates on Blacksmith Testbox (Linux, Node 24): oxlint 0/0 on touched paths; 14 focused test files green (worktrees service incl. owner-activity GC, sessions.create/delete lifecycle + cwd propagation into agent runs, maintenance hook wiring, protocol schema, workboard, UI e2e chat flow, navigation);
tsgo:prod+tsgo:test+tsgo:test:ui;ui:build; fullpnpm build;ui:i18n:check+native:i18n:check. Codex autoreview (gpt-5.5) iterated to clean — its findings (subdirectory-workspace preflight rejection, unused import) are fixed with a regression test.