Skip to content

feat(agent-launch): create a folder workspace and start its agent in one host operation - #26083

Merged
brennanb2025 merged 13 commits into
mainfrom
brennanb2025/agent-launch-create-folder-workspace
Oct 9, 2026
Merged

brennanb2025 merged 13 commits into
mainfrom
brennanb2025/agent-launch-create-folder-workspace

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 6 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​416 $\color{#cf222e}{\Huge{\mathbf{−}}}$​4 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​412
Prod 21 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​198 $\color{#cf222e}{\Huge{\mathbf{−}}}$​40 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​158

ELI5

Orca's host has one request, agent.launch, that can make a new git worktree and start an agent in it in one step. It couldn't do that for a folder workspace (a plain folder inside a project group, not a git worktree). Starting an agent in a new folder workspace took two separate requests, and the window, not the host, decided chat vs terminal.

This PR lets agent.launch create a folder workspace and start its agent in one host request. No button in the app sends it yet; the folder composer moves onto it in the next PR.

What Changed

The problem. We are moving every "new workspace + agent" entry point onto one host operation, so the host decides chat vs terminal, where the agent runs, and records what it did (step 3 of the launch unification; earlier parts #26052 and #26060). agent.launch only knew two targets: an existing workspace, or a new git worktree. A new folder workspace had no target, so the folder composer could not move onto it.

User-facing change. None yet: no client sends the new target in this PR. When the composer moves onto it, a new folder workspace with an agent will be created and started by the host in one step, exactly like starting an agent in an existing folder workspace.

The mechanism.

  1. New target { kind: 'create-folder-workspace', create }, where create is the existing folderWorkspace.create request, checked by the same schema.
  2. The host creates, then launches like any folder workspace. It creates the folder through the runtime's existing folder create (which checks the folder and records who created it, with createdWithAgent = the launch's agent), then resolves the new workspace exactly as an existing-folder target is resolved, so its id (folder:<id>) and SSH connection match. From there chat vs terminal and the surface are built exactly as for an existing folder workspace.
  3. Clean refusals before anything is stored. A missing folder, a missing project group, or an SSH host that is offline is refused before the store is written. A replay of the same request returns that same failure instead of "outcome unknown", and the error names the full requested path.
  4. Same permission as creating a folder. The new target needs folderWorkspace.create's permission, checked before anything happens, so a phone with mobile pairing is refused (agent_launch_target_forbidden) while its existing-workspace launches keep working.
  5. Whose screen moves. A paired client (a desktop client of a remote Orca server) that creates a folder workspace gets the new tab selected for itself and leaves the host's window where it was, the same as when it launches into an existing workspace.
  6. Replay. The whole create request is part of the replay fingerprint: the same request replays without creating a second workspace; a changed request under the same operation id conflicts.
  7. Negotiated. Hosts advertise agent.launch.create-folder-workspace.v1; an older host rejects the unknown target kind, so a client checks this first.
  8. Workspace kind is derived from the target (folder here) instead of every create counting as a git worktree; no new stored field.

Why

  • A target, not a second launcher. It's the same operation as a new git worktree (create, then start the agent). A separate folder launcher would duplicate folder checks, agent placement and the chat-vs-terminal decision.
  • Reuse the existing folder create and existing-target resolution, so the new target can't drift from folderWorkspace.create or from an existing-folder launch.
  • A capability, because clients and hosts update independently; without it a new client talking to an older host would get "invalid params" instead of a workspace.

Linked Issue

Part of the new-workspace launch unification (step 3a). Earlier parts: #26052, #26060.

Visual Proof

Host-only change: no app control sends the new target, so there is no before/after in the app. These are hidden test-build screenshots taken while sending the new request directly, at head 7283f36ed9d, with a stand-in agent (no real agent ran).

What Screenshot
New folder workspace + terminal agent: the workspace appears under its project group and the agent runs in that folder row1
Paired client creates a folder workspace: host window before before
...and after: host stays on its workspace, the new one is only added to the sidebar after
SSH host: the agent runs in the remote folder ssh
Managed Orca server over SSH: launched on the server's runtime, terminal in the remote folder orcad

Testing

  • Unit/integration: agent-launch-folder-workspace-create.test.ts (new, through the real agent.launch handler with SQLite), plus executor, caller-selection, project-group controller and params tests. At 7283f36ed9d: tc:node, tc:web, tc:cli, all repo static checks, and the six named launch test files (91/91) pass; CI passes.

  • Live, on macOS hidden test builds driven by runtime requests (details in the review comment): new folder + terminal agent, chat default, replay and conflict, missing folder / missing group refusals and their replays, an older host refusing the unknown target, phone refusal, existing and new-worktree launches unchanged, the paired-client screen rule, an SSH host (agent runs remotely; offline refusal and replay), and a managed Orca server over SSH (desktop-side request refused cleanly; request to the server's runtime creates and launches there; replay makes no second folder). Windows was not run for this head.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

Review

Reviewed in rounds, including an independent design comparison, and a final readiness review on the tree merged with current main. Findings, fixes and deferred items are in the review comment.

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • Before the composer uses this target: if the folder is created but the agent then fails to start, the launch answers "outcome unknown" without naming the workspace, and a new attempt makes another folder. Creating a new git worktree has the same gap today. It must get the "workspace kept, agent didn't start" answer (feat(agent-launch): name the kept workspace when a new workspace's agent provably never started #26060's mechanism) before the composer sends this target. A plain SSH host with no Node on it triggers it reliably.
  • SSH: a folder workspace in an SSH project group keeps its connection and its agent runs on that host. For a host converted to a managed Orca server, the folder lives on the server, so the request must go to the server's runtime; the desktop refuses it cleanly.
  • Folder workspaces: this PR is about them; git worktrees are unchanged.
  • Mixed versions: capability-gated; older clients never send it, older hosts never advertise it.

Author: @BrennanKB5

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

agent.launch can now create a folder workspace and start its agent there,
the same operation it already runs for a new git worktree. The host
creates the folder workspace (metadata only, no startup terminal), resolves
it the way an existing target is resolved, then settles the mode and builds
the surface as for any existing folder workspace.

Hosts advertise agent.launch.create-folder-workspace.v1 so a client offers
the target only to a host that accepts it.
…ke their create method

A create-folder-workspace launch could do three things its two-call
equivalent never did:

- A phone could create folder workspaces. The mobile allowlist admits
  agent.launch but not folderWorkspace.create, and is checked by method
  name only. A create target now needs the authorization of the create
  method it stands for, read from the same allowlist, and a phone is
  refused with agent_launch_target_forbidden before admission, so no
  ledger row and no create. create-worktree maps to worktree.create,
  which phones already may call, so nothing changes for it.
- A create refused before anything was stored (group gone, folder
  missing, SSH host unreachable) replayed as
  agent_session_operation_unknown, and a retry under the same operation
  id was refused until the row expired. runtime.createFolderWorkspace now
  raises those refusals as FolderWorkspaceCreateRefusedError, with the
  same message text, and the launch records it as failed for any target.
  A failure after the create still replays unknown.
- A paired caller's folder create activated the new chat on the host
  window and did not select the caller's own tab. Only create-worktree,
  whose host activation runs setup, keeps the host navigation now; a
  folder create navigates like an existing workspace.

Tests cover the phone refusal, replay of a success, a refused create
settling failed and replaying that failure, an after-create failure
staying unknown, an SSH connection reaching the agent's view mode, paired
navigation, and an older host refusing the target at parse.
A folder create refused before anything was stored is recorded as the
launch's failure, and a retry under the same operation id answers from
that record. The record went through the 128-character cap every launch
failure code gets, and folder refusals carry the folder's absolute path,
so a retry could show the user a path cut short: a different folder.

The refusal is now recorded whole when it fits a path as long as Linux's
PATH_MAX after its code. Past that, which no folder a stat accepts
reaches, only the code before the path is kept, so the replay degrades to
the bare code instead of a partial path.

Also: the controller test asserts the exact refusal message, the
create-folder-workspace capability comment says a phone needs its own
gate, and the forbidden-target code says why it is not `forbidden`.
… an assertion

The changed-code quality gate refuses the `as never` cast on the stub
store. It is now a RuntimeStore whose unreached members throw.
…ntime project

The operationId replay tests open the real SQLite record store, so the
file belongs with the other ledger-backed agent.launch tests.
# Conflicts:
#	config/scripts/vitest-sqlite-runtime-files.mjs
#	src/main/runtime/rpc/methods/agent-launch-caller-selection.ts
#	src/main/runtime/rpc/methods/agent-launch-worktree-creation.ts
…off the host window

The folder-create test now asserts surfaceOwner: false on the terminal a
paired caller's launch creates, rather than matching any options. The
caller-selection header now says only an in-process worktree create keeps
worktree.create's navigation.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review and QA summary (head 7283f36ed9d)

The problem this PR solves. Starting an agent in a new folder workspace took two separate host requests (create, then launch), and the window rather than the host chose chat vs terminal. New git worktrees already had a one-step host launch. This PR adds the folder-workspace target to that same host launch. It is the host half only: no app button sends it yet; the folder composer moves onto it in the next PR.

User-facing change. None in the app today. Once the composer uses it, a new folder workspace with an agent is created and started by the host in one step, behaving exactly like starting an agent in an existing folder workspace, locally or over SSH. A phone can't use it (it can't create folder workspaces today either), and a remote desktop client that uses it doesn't move the host's window.

What was fixed during review

  • Clean failures before anything is saved: a missing folder or missing project group used to replay as "outcome unknown"; now it replays the original failure, with the full requested path.
  • Phones: the new target was reachable from a phone pairing even though phones can't create folder workspaces; it is now refused before anything happens, and existing-workspace launches from phones still work.
  • Whose screen moves: a remote desktop client creating a folder workspace moved the host window; it now selects the new tab for itself and leaves the host where it was.
  • Final sync with main: one merge of current main (59b9bde). Main's "structured chat by default" change (Default experimental native chat to structured chat #22933) removed the per-connection chat/terminal view choice, so one new test's expectation was out of date; fixed in the test only.

Deferred (not blocking this host-only step)

  • Must land before the composer uses this target: if the folder is created but the agent then fails to start, the launch answers "outcome unknown" without naming the workspace, and a retry with a new id makes a second folder. Creating a new git worktree has the same gap on main. Live QA hit this reliably on a plain SSH host with no Node installed (the folder was kept, the answer was "unknown"). The fix is the "workspace kept, agent didn't start" answer from feat(agent-launch): name the kept workspace when a new workspace's agent provably never started #26060, extended to this target by whichever PR lands second.
  • A request sent to the desktop for a group whose SSH host was converted to a managed Orca server is refused as "folder unavailable". It is clean and saves nothing, but the wording reads like "host offline". The composer will send these requests to the server instead.
  • Small cleanups: one test name now claims more than it checks; the folder launch does one workspace lookup it no longer needs (a main leftover); the target's permission check reads the phone allowlist rather than main's newer per-method permission table (equivalent today); an unused helper name is now ambiguous.
  • Composer follow-up obligations: always send an operation id, carry the creator identity, reveal the new folder, and don't pass a workspace-root working directory that would force terminal mode.

Verified

  • tc:node, tc:web, tc:cli, all repo static checks, and the six launch test files (91/91) pass on the merged head. CI passes: 18 passed, 19 skipped. The first CI attempt had one failure, a browser cookie-import timing test this PR doesn't touch; it passed on rerun and locally.
  • Fresh readiness review of the final diff against current main: no new P0–P3 issues. Every main change that touches the same files (chat default, SSH permission checks, workspace links, structured chat for other agents) behaves the same for the new target as for an existing folder.
  • Live QA on macOS hidden test builds, driven by direct host requests, with a stand-in agent (no real agent CLI ran) and isolated test profiles:
Check Result
New folder + terminal agent: workspace under its group, createdWithAgent: codex, agent started in that folder with the prompt Pass
Chat as the default: new-folder launch ends in the same state as the same launch into an existing folder Pass (parity)
Same request replayed: same result, no second folder; changed request, same id: conflict Pass
Missing folder / missing group: clean failure naming the path; replay returns the same failure Pass
Older host (main build): doesn't advertise the capability, refuses the target as invalid, nothing created Pass
Phone pairing: new target refused on both launch requests; existing-workspace launch still works Pass
Existing-workspace and new-worktree launches: unchanged result shape and behaviour Pass
Remote desktop client creates a folder workspace: host window stays on its workspace and tab; the client's own tab list has the new tab active. The local CLI and existing-workspace launches were also checked as controls Pass
SSH host (throwaway Linux container, connected the older way without a managed server): agent runs in the remote folder; host offline gives a clean "path unavailable" failure, and the replay matches Pass
Managed Orca server over SSH: the desktop-side request is refused cleanly and saves nothing. The same request to the server's runtime creates the folder on the server and opens the terminal in that remote folder; replay makes no second folder Pass
SSH with chat default on: the old main bug (empty chat box while the terminal ran) no longer reproduces; the launch opens the terminal Pass

Screenshots are in the PR description.

Not verified

  • A real agent answering in the chat or terminal (stand-in only); a healthy structured chat session (the stand-in never starts one, so chat was checked only for parity).
  • Managed-server offline refusal via the server route; Windows and Linux desktop hosts for this head; the phone app UI (the phone check was protocol-level only).

Unexpected things seen (none caused by this PR)

  • Folder workspaces in a project group created without a parent folder don't show in the sidebar. App-created groups have one; groups created through a host request may not.
  • With chat as the default and an agent whose chat never starts, the request answers "outcome unknown" after about 16 seconds and leaves an empty terminal tab. This happens identically when launching into an existing folder.

@brennanb2025
brennanb2025 marked this pull request as ready for review October 9, 2026 01:13
@coderabbitai

coderabbitai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8f84199d-a475-412f-bc36-213be5417d25

📥 Commits

Reviewing files that changed from the base of the PR and between 40d9ca1 and 5f71920.


📒 Files selected for processing (4)
  • config/scripts/vitest-sqlite-runtime-files.mjs
  • src/main/agent-launch/agent-launch-executor.test.ts
  • src/main/agent-launch/agent-launch-executor.ts
  • src/main/agent-launch/agent-launch-surface-factories.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.



📝 Walkthrough

Walkthrough

Folder-workspace creation is now an agent-launch target. The launch contract, runtime capability, and mobile-target authorization support the target. The executor creates the workspace before starting a terminal agent or structured session. The RPC path resolves the workspace scope and connection, and typed creation refusals are preserved in replay outcomes. Paired-client launches select the created folder workspace.


Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 5f719

No merge-blocking issue is established for this change. The reported post-creation failure outcome should be addressed before a client adopts the new target.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 28.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 25 functions across 29 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: creating a folder workspace and starting its agent in one host operation.
Description check Passed The description is complete and directly matches the pull request. It covers the user impact, implementation, rationale, linked work, testing, visual proof, security and compatibility considerations, …
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.


  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR


🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Main's worktree.create caller (#26709) builds a legacy-host launch factory
with only createWorktree; type that policy's factory as worktree-only so it
needn't implement this PR's createFolderWorkspace, which only agent.launch uses.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

End-of-review sync with main (merge 40d9ca1c5fd, main at d7e985caa76, which includes #26709 and #26703)

  • Conflict: src/main/agent-launch/agent-launch-executor.ts. Kept this PR's folder-workspace create branch and its shared workspaceKindForLaunchTarget, and kept main's new prompt inputs (createLaunchPromptInputs) in place of the old startupPrompt line.
  • Fix the merge needed (found by the typecheck, no text conflict): Start worktree.create's agent through the launch executor #26709's worktree.create caller builds a launch that can only create a git worktree, so its factory has no createFolderWorkspace. That caller's factory is now typed as worktree-only (AgentLaunchWorktreeFactory), and agent.launch keeps the full factory. One type assertion in main's agent-launch-legacy-host-policy.test.ts was updated to match. Runtime behavior is unchanged for both worktree.create and folder creates.
  • Checks on the merged tree:
  • CI: green: 23 pass, 22 skipped, 0 failed.

Main's orchestration workers (#26724) and worktree.create (#26709) pass
worktree-only launch factories, so createFolderWorkspace is optional on the
factory; a folder create without it is refused before anything is created.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Second end-of-review sync with main (merge 5f71920aac2, main at 4eb6c0e5f72, which includes #26724)

@brennanb2025
brennanb2025 merged commit 39a01f9 into main Oct 9, 2026
56 of 58 checks passed
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