Skip to content

Parallel worktree sessions: isolated runs, per-project setup commands, and activity indicators#19357

Merged
abhiaiyer91 merged 14 commits into
mainfrom
worktree-sessions
Jul 13, 2026
Merged

Parallel worktree sessions: isolated runs, per-project setup commands, and activity indicators#19357
abhiaiyer91 merged 14 commits into
mainfrom
worktree-sessions

Conversation

@abhiaiyer91

@abhiaiyer91 abhiaiyer91 commented Jul 13, 2026

Copy link
Copy Markdown
Member

Summary

Makes Mastra Code web treat each git worktree as an independent unit of work, so multiple agent runs can execute in parallel across worktrees without interfering with each other.

Scoped agent controller sessions (core, server, client-js)

Previously all runs on a GitHub project shared one server-side session: starting a second run repointed the workspace and stopped the first run's stream. Sessions can now be addressed with an optional scope (one per worktree) via controller.session(resourceId, scope); the server accepts a sessionScope query param and the web UI caches clients per worktree path.

One conversation per worktree

Factory runs no longer leave a stray "Untitled" thread (the run now claims the thread a fresh session seeds instead of creating a second one). The sidebar thread list — titles, rename, clone, new-thread — only appears under the main-branch workspace; feature worktrees hold a single conversation and the workspace row itself is the entry point. Local projects keep the flat multi-thread list.

Per-project setup command

Settings › General gains a per-project setup command (e.g. pnpm i && pnpm build) stored on the GitHub project. It runs inside the sandbox right after a worktree is created, before any agent execution; failures surface as a 400 with the command's stderr so the agent never starts on a broken checkout.

Activity indicators

Work in progress is now visible: the session state response includes a running flag and a new non-creating GET .../sessions/:resourceId/running peek route lets the UI poll activity without seeding sessions for idle worktrees. The chat working indicator survives page reloads mid-run (hydrated from the state snapshot and display_state_changed), and workspace rows show a pulsing dot while their scoped session is executing.

New public API in @mastra/client-js:

const session = client.getAgentController('code').session(resourceId, scope);
const { running } = await session.running(); // false when no live session exists

Changesets included for @mastra/core, @mastra/server, and @mastra/client-js.

Test plan

  • mastracode/web unit tests: 312 passing
  • mastracode/web MSW UI suite: 390 passing (new specs: scoped session caching, seeded-thread claim, setup command save/read + failure handling, running-flag hydration, workspace activity dot, worktree-hides-thread-list)
  • mastracode/web server e2e suite: 89 passing
  • @mastra/server handler tests (29) incl. running flag + non-creating peek; permissions check clean
  • @mastra/client-js tests (16) incl. session.running() scope-aware requests
  • @mastra/core session-isolation tests for scoped sessions
  • Typecheck + prettier clean across touched packages

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) noreply@mastra.ai

ELI5

Each Git worktree now gets its own independent agent conversation, so agents can work in parallel without mixing messages or settings. Projects can also run a setup command automatically when a new worktree is created.

What changed

  • Added scoped agent-controller sessions across core, server, client SDK, and web.
  • Scoped conversations, caching, mutations, and API requests by worktree path.
  • Added sessionScope support to server routes and client APIs.
  • Added running session state and per-thread active/idle status.
  • Added worktree activity indicators and live transcript hydration.
  • Added configurable per-project worktree setup commands with validation and failure handling.
  • Added settings UI and APIs for managing setup commands.
  • Updated feature worktree behavior, including seeded thread reuse and read-only thread controls.
  • Added comprehensive unit, integration, MSW, E2E, and session-isolation coverage.
  • Updated package changesets for the new scoped-session and activity APIs.

abhiaiyer91 and others added 7 commits July 13, 2026 14:37
Sessions were get-or-create by resourceId alone, so multiple Intake runs
over the same project resolved to one shared server-side session: each
new run repointed the workspace and clobbered the previous run.

Add an optional session scope so callers can address independent
sessions over the same resource (one per git worktree):

- core: registry keyed by resourceId + scope; getSessionByResource and
  setResourceId are scope-aware
- server: session routes accept a sessionScope query param and qualify
  the stable session id with it
- client-js: AgentControllerSession carries scope on every request
- web: client cache and session-derived query keys include the worktree
  projectPath; hooks/providers/mutations pass it through, and
  useStartFactoryRun derives it before createThread/send

Unscoped callers behave exactly as before.

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) <noreply@mastra.ai>
…sions

# Conflicts:
#	mastracode/web/src/web/ui/domains/workspaces/hooks/__tests__/useWorkspaces.msw.test.tsx
…thread

Bringing a brand-new worktree scope online seeds the session with an empty
untitled thread (core creates one when no thread matches the scope tags),
and the Factory run then created a second, titled thread — so every
Investigate/Review click showed the run thread plus an "Untitled" sibling
in the sidebar. The run now claims the seeded thread by renaming it when it
is still fresh (no title, no messages), and only creates a new thread when
the session resumed a real one.

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) <noreply@mastra.ai>
Agents were starting in freshly forked worktrees with no dependencies
installed, so their first actions were always install/build churn. Each
GitHub project can now store a setup command (e.g. "pnpm i && pnpm build")
that runs inside every newly created worktree before any agent execution.

- schema: nullable setup_command column on github_projects (idempotent
  ALTER TABLE ... IF NOT EXISTS migration)
- server: GET/POST /web/github/projects/:id/settings to read/save the
  command; worktree creation runs it via runWorktreeSetup() and returns a
  400 setup-failed error (with trailing stderr) when it exits non-zero;
  reused worktrees skip setup
- client: fetchProjectSettings/saveProjectSettings services plus
  useProjectSettingsQuery/useSaveProjectSettingsMutation hooks
- ui: Settings > General > "Worktree setup" section with one draft field
  per GitHub project; saving a blank field clears the command

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) <noreply@mastra.ai>
…transcript

Runs were invisible outside the live SSE stream: reloading a thread page
mid-run showed no working indicator, and the workspace sidebar gave no
hint that another worktree's agent was busy.

- server: include running in the session state response and add a
  non-creating GET .../sessions/:resourceId/running peek so activity can
  be polled without seeding sessions for idle worktrees
- client-js: expose the running flag on session.state() and a
  session.running() peek
- web chat: fold isRunning from display_state_changed into the
  transcript, hydrate it from the state snapshot on load/reconnect, and
  fall back to connection state so the working indicator survives page
  loads mid-run
- web workspaces: poll each worktree's scoped session and render a
  pulsing activity dot on rows whose agent is working

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) <noreply@mastra.ai>
…nfig

Files brought in by the scoped-sessions merge weren't formatted with the
root prettier settings. Formatting-only, no behavior change.

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) <noreply@mastra.ai>
Multiple threads inside one worktree don't fit the workspace model — a
worktree is one unit of work. Nesting the multi-thread list (titles,
rename, clone, new-thread) under every workspace row added noise and
made it easy to scatter runs across stray threads.

The thread list now only nests under the main-branch workspace; feature
worktrees show no thread list, and the workspace row itself is the entry
point (selecting it resumes the worktree's latest thread or creates its
single one). Local projects keep the flat multi-thread list.

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) <noreply@mastra.ai>
@vercel

vercel Bot commented Jul 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mastra-docs-1.x Ready Ready Preview, Comment Jul 13, 2026 3:24pm
mastra-playground-ui Ready Ready Preview, Comment Jul 13, 2026 3:24pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
mastra-studio-preview Ignored Ignored Preview Jul 13, 2026 3:24pm

Request Review

@changeset-bot

changeset-bot Bot commented Jul 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: aad926d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 23 packages
Name Type
@mastra/core Minor
@mastra/client-js Minor
@mastra/server Minor
mastra Patch
@mastra/code-sdk Patch
mastracode Patch
@mastra/mcp-docs-server Patch
@internal/playground Patch
@mastra/opencode Patch
@mastra/longmemeval Patch
@mastra/playground-ui Major
@mastra/react Patch
@mastra/deployer Minor
@mastra/express Patch
@mastra/fastify Patch
@mastra/hono Patch
@mastra/koa Patch
@mastra/nestjs Patch
@mastra/next Patch
@mastra/tanstack-start Patch
@mastra/deployer-cloud Minor
@mastra/temporal Patch
create-mastra Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dane-ai-mastra dane-ai-mastra Bot added the complexity: critical Critical-complexity PR label Jul 13, 2026
@dane-ai-mastra

dane-ai-mastra Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

PR triage

Linked issue check skipped for core contributor @abhiaiyer91.


PR complexity score

Factor Value Score impact
Files changed 65 +60
Lines changed 2642 +60
Author merged PRs 710 -20
Test files changed Yes -10
Final score 90

Applied label: complexity: critical


Changed test gate

Changed tests failed against the base branch as expected.

Label: tests: green ✅

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4dad6da9-4c9b-4ab9-9a91-7d5b7b7ceed0

📥 Commits

Reviewing files that changed from the base of the PR and between 87bb3c7 and aad926d.

📒 Files selected for processing (2)
  • .changeset/lucky-moons-attack.md
  • .changeset/wide-streets-smash.md

Walkthrough

Agent Controller sessions now support independent (resourceId, scope) state, with scoped routing, SDK propagation, cache isolation, and activity metadata. Web worktree flows use scoped sessions and activity indicators. GitHub projects can store validated setup commands that run in newly created worktrees.

Changes

Agent Controller session isolation

Layer / File(s) Summary
Scoped session contracts and server routes
packages/core/..., packages/server/..., packages/cli/...
Session identity, route schemas, request metadata, and responses now support sessionScope, running, and per-thread activity state.
Client and web scope propagation
client-sdks/..., mastracode/web/src/...
SDK requests, controller clients, React Query keys, hooks, and mutations carry project-path session scopes.
Worktree activity and transcript state
mastracode/web/src/web/ui/domains/...
Worktree-scoped sessions, activity polling, thread indicators, and transcript running-state hydration are integrated into the web UI.

Project worktree setup commands

Layer / File(s) Summary
Setup storage and execution
mastracode/web/src/web/github/...
Project setup commands are stored, validated, executed in new worktrees, and reported through sandbox errors.
Setup settings UI
mastracode/web/src/web/ui/domains/settings/..., mastracode/web/src/web/ui/domains/workspaces/...
Project settings services, hooks, settings controls, and tests expose editable per-project setup commands.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • mastra-ai/mastra#18918: Both changes modify AgentController write routes, including message, steering, follow-up, and tool interaction handlers.
  • mastra-ai/mastra#19104: Both changes concern AgentController session get-or-create identity semantics.
  • mastra-ai/mastra#19258: Both changes modify the Factory investigation run flow and thread/worktree creation.

Suggested reviewers: tylerbarnes, lekoarts, wardpeet, theisrael1, nikaiyer

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.13% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: scoped worktree sessions, setup commands, and activity indicators.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-sessions

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

worktreePath: string,
command: string,
): Promise<void> {
const result = await sh(sandbox, `cd ${shellQuote(worktreePath)} && { ${command}\n}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: User-controlled setupCommand executes arbitrary shell code without validation

User-configured shell command is interpolated directly into sh() without content validation.

Add command validation, whitelist allowed binaries, or restrict setup command configuration to admins.

AI prompt
Check if this security scanner issue is valid. If so, understand the root cause and fix it. If appropriate, update or add tests. Keep the change focused and preserve intended behavior.

<file name="mastracode/web/src/web/github/sandbox.ts">
<violation number="1" location="mastracode/web/src/web/github/sandbox.ts:857">
<priority>P1</priority>
<title>User-controlled setupCommand executes arbitrary shell code without validation</title>
<evidence>The runWorktreeSetup function in sandbox.ts interpolates a user-configured command (stored per-project via /web/github/projects/:id/settings) directly into a shell string: sh(sandbox, `cd ${shellQuote(worktreePath)} &amp;&amp; { ${command}\n}`). The settings endpoint only validates type (string|null) and length (max 2000), but does not whitelist commands, escape shell metacharacters, or restrict dangerous binaries. Any authenticated project member can persist a malicious command that executes automatically in the sandbox whenever a new worktree is created.</evidence>
<recommendation>Add content validation for setupCommand before execution. Options include: (1) whitelist allowed commands/binaries and reject anything else; (2) parse commands with a restricted-shell grammar; (3) require org-admin or elevated privileges to set a setup command; (4) run the setup step in a more restrictive sandbox (e.g., deny network egress, limit filesystem scope). At minimum, reject shell metacharacters like ;, |, &amp;, $(), `, and redirection operators.</recommendation>
</violation>
</file>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 43fc6aa. Context on the finding:

Arbitrary shell is the feature, not a bugsetupCommand exists to run project bootstrap like pnpm i && pnpm build, so a binary whitelist would defeat its purpose.

Trust boundary: the command is only configurable by authenticated org members (settings route is gated by resolveOrgTenant + org-scoped project lookup), and it executes exclusively inside the project's isolated sandbox — the same environment where org members already run arbitrary shell via the agent's command tool. It never runs on the web server host, so it grants no privilege beyond what sandbox access already provides.

Hardening added: the settings route now rejects control characters (except newline/tab) on top of the existing type and 2000-char length validation, and runWorktreeSetup() documents the security model. Regression tests cover the rejection and the legitimate multi-line case.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The developer’s explanation is reasonable: arbitrary shell execution is an intentional feature scoped to a trust boundary where authenticated org members already have equivalent sandbox access via the agent command tool, so this does not constitute a privilege escalation. The added hardening—rejecting non-printable control characters, documenting the security model, and adding regression tests—provides satisfactory defense-in-depth while preserving intended behavior.

@superagent-security superagent-security Bot added the pr:flagged Superagent security flag label Jul 13, 2026
abhiaiyer91 and others added 2 commits July 13, 2026 16:10
Worktrees stay single-conversation, but hiding the thread list entirely
removed useful context. Restore the nested thread list under the active
worktree row so its title is visible, while keeping it read-only there:
no New thread button and no rename/clone/delete actions. Main-branch and
local projects keep the full multi-thread controls.

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) <noreply@mastra.ai>
…lists

A feature worktree holds a single conversation, so the "Threads" label and
count added noise above the one title. Hide the whole header in read-only
mode; main-branch and local projects keep it along with the New thread button.

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) <noreply@mastra.ai>
@dane-ai-mastra dane-ai-mastra Bot added the tests: green ✅ Changed tests failed against base as expected label Jul 13, 2026
abhiaiyer91 and others added 2 commits July 13, 2026 16:16
CI validates that generated files are committed; the new
GET /agent-controller/:controllerId/sessions/:resourceId/running route
was missing from the CLI route metadata.

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) <noreply@mastra.ai>
… model

Address the security-scan finding on setupCommand execution: reject
control characters (except newline/tab) at the settings route so escape
sequences and NULs can't reach the sandbox shell or spoof logs, and
document why arbitrary shell is intentional — the command is only
configurable by authenticated org members and runs exclusively inside
the project's isolated sandbox, the same environment where org members
already run arbitrary shell through the agent.

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) <noreply@mastra.ai>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
mastracode/web/src/web/github/routes.ts (1)

823-855: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Failed setup can get skipped forever on retry.

If runWorktreeSetup throws after git worktree add, the checkout stays on disk but no githubWorktrees row is written. The next request for the same branch sees the existing .git file, marks the worktree as reused, and never reruns setup. Track setup completion separately from reused, or rerun setup when no persisted row exists yet.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mastracode/web/src/web/github/routes.ts` around lines 823 - 855, The worktree
creation flow around ensureWorktree and runWorktreeSetup must rerun setup after
a prior setup failure. Track whether the branch already has a persisted
githubWorktrees row, and invoke runWorktreeSetup when that row is absent even if
result.reused is true; retain the existing behavior for newly created worktrees
and persist the row only after successful setup.
mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerStateMutations.ts (1)

40-74: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Scope the connection invalidation

useAgentControllerSessionInit and useAgentControllerSessionSync key connection state with queryKeys.agentControllerConnection(..., projectPath), but both switch mutations invalidate ['agent-controller', agentControllerId, 'connection', resourceId]. That broad key can refetch unrelated worktree connections for the same resource; use the scoped helper here instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerStateMutations.ts`
around lines 40 - 74, The connection invalidation in
useSwitchAgentControllerModeMutation and useSwitchAgentControllerModelMutation
is broader than the session connection key. Replace each inline connection query
key with queryKeys.agentControllerConnection(args.agentControllerId,
args.resourceId, args.projectPath), preserving the existing session
invalidation.
mastracode/web/src/web/ui/domains/workspaces/hooks/useWorkspaces.ts (1)

123-154: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Cascade thread deletion may run against the wrong worktree's session scope.

threadSession is a session scoped to whatever worktree is currently selected in the caller (WorkspacesSection.tsx's session, derived from deriveProjectPath(activeProject)), but mutationFn uses it to list/delete threads tagged for worktree.worktreePath — the worktree actually being deleted, which can be a different, non-selected worktree (its onDelete handler is enabled independent of selection). Elsewhere in this PR (openWorktreeThread, useAgentControllerThreads, useWorkspaceActivity), every worktree-scoped call explicitly creates a session bound to that specific worktree before calling listThreads/running, which implies scope does affect these session-bound requests. If that holds here too, deleting a non-active worktree could silently list 0 threads and skip cleanup, leaving orphaned threads after the worktree checkout is already gone.

Consider deriving the session from worktree.worktreePath inside mutationFn instead of trusting a caller-supplied, statically-scoped session.

🔧 Suggested direction
 export function useDeleteWorkspaceMutation(
   project: Project | null | undefined,
-  threadSession: WorkspaceThreadSession | null | undefined,
   scope?: AgentControllerThreadsScope,
 ) {
   const { baseUrl } = useApiConfig();
   const queryClient = useQueryClient();
   const { toast } = useToast();

   return useMutation({
     mutationFn: async (worktree: Worktree) => {
       if (!project?.githubProjectId) throw new Error('No GitHub project selected');
       await deleteWorktree(baseUrl, project.githubProjectId, worktree.branch);

       // Cascade: delete the threads scoped to this worktree specifically,
       // not whatever worktree happens to be active in the caller.
+      const { session: threadSession } = createAgentControllerClient({
+        agentControllerId: scope?.agentControllerId ?? '',
+        resourceId: scope?.resourceId ?? '',
+        scope: worktree.worktreePath,
+        baseUrl,
+      });
       if (threadSession) {
         for (let round = 0; round < 20; round++) {

To confirm whether scope actually affects AgentControllerSession.listThreads's underlying request (which would confirm this is a real bug rather than a harmless no-op), it would help to inspect the client-js implementation:

#!/bin/bash
ast-grep outline client-sdks/client-js/src/resources/agent-controller.ts --items all --type class,method --match 'AgentControllerSession|listThreads|deleteThread|base|running'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mastracode/web/src/web/ui/domains/workspaces/hooks/useWorkspaces.ts` around
lines 123 - 154, Update useDeleteWorkspaceMutation so the cascade uses an
AgentController session scoped to worktree.worktreePath inside mutationFn,
rather than the caller-provided threadSession derived from the selected
worktree. Reuse the existing session-construction pattern used by
openWorktreeThread, useAgentControllerThreads, or useWorkspaceActivity, while
preserving the current listThreads/deleteThread loop and optional-session
behavior.
🧹 Nitpick comments (2)
mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerThreadMutations.ts (1)

14-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider deduplicating toClientArgs across the agent controller mutation hooks. The same projectPathscope mapping is repeated in useAgentControllerGoalMutations.ts, useAgentControllerRunMutations.ts, useAgentControllerStateMutations.ts, and useAgentControllerThreadMutations.ts. A shared helper next to createAgentControllerClient would reduce drift if the scoping contract changes again.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerThreadMutations.ts`
around lines 14 - 22, Deduplicate the repeated argument mapping by introducing a
shared helper beside createAgentControllerClient, then update toClientArgs and
the corresponding helpers in useAgentControllerGoalMutations.ts,
useAgentControllerRunMutations.ts, and useAgentControllerStateMutations.ts to
reuse it. Preserve the projectPath-to-scope mapping and all existing arguments.
mastracode/web/src/web/ui/domains/workspaces/services/github.ts (1)

396-403: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

fetchProjectSettings error handling is less structured than saveProjectSettings.

postProjectGitOp (used by saveProjectSettings) parses the JSON error body for code, message, and sets authRequired on 401. fetchProjectSettings only throws a generic Error with the status code, so a 401 here won't be distinguishable as authRequired by callers the way save failures are.

♻️ Align error handling with `postProjectGitOp`
 export async function fetchProjectSettings(baseUrl: string, githubProjectId: string): Promise<ProjectSettings> {
   const res = await fetch(`${baseUrl}/web/github/projects/${encodeURIComponent(githubProjectId)}/settings`, {
     headers: { Accept: 'application/json' },
     credentials: 'include',
   });
-  if (!res.ok) throw new Error(`Failed to load project settings (${res.status})`);
+  if (!res.ok) {
+    const err = new Error(`Failed to load project settings (${res.status})`) as GitOpError;
+    err.status = res.status;
+    if (res.status === 401) err.authRequired = true;
+    throw err;
+  }
   return (await res.json()) as ProjectSettings;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mastracode/web/src/web/ui/domains/workspaces/services/github.ts` around lines
396 - 403, Update fetchProjectSettings to handle non-OK responses using the same
structured JSON error parsing as postProjectGitOp, preserving the response code
and message and marking 401 failures as authRequired. Keep the successful
ProjectSettings parsing unchanged and reuse the existing error shape or helper
used by postProjectGitOp.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/wide-streets-smash.md:
- Line 8: Add a concise TypeScript usage example to the changeset demonstrating
the new client-js `session(resourceId).running()` API, including awaiting the
call and reading the returned `running` flag. Keep the example focused on the
public API and preserve the existing feature description.

In `@mastracode/web/src/web/ui/domains/chat/context/ChatTranscriptProvider.tsx`:
- Around line 46-62: Update the hydration useEffect in ChatTranscriptProvider
around stateRunning/stateUpdatedAt so session snapshots only synchronize the
running flag. Stop passing snapshot omProgress and tokenUsage to syncState,
preserving the newer live SSE-driven progress and usage values while retaining
immediate running-state hydration.

In `@packages/core/src/agent-controller/agent-controller.ts`:
- Around line 59-66: Update sessionRegistryKey to use an unambiguous encoding of
both resourceId and scope, rather than concatenating them with a NUL separator;
preserve distinct keys for unscoped sessions, scoped sessions, and values
containing arbitrary characters.

In `@packages/core/src/agent-controller/session-isolation.test.ts`:
- Around line 401-404: Await controller.setResourceId in the resource re-key
test before querying the renamed and old resource IDs. Keep the existing
assertions unchanged so they verify both the new lookup and registry cleanup
after the operation completes.

---

Outside diff comments:
In `@mastracode/web/src/web/github/routes.ts`:
- Around line 823-855: The worktree creation flow around ensureWorktree and
runWorktreeSetup must rerun setup after a prior setup failure. Track whether the
branch already has a persisted githubWorktrees row, and invoke runWorktreeSetup
when that row is absent even if result.reused is true; retain the existing
behavior for newly created worktrees and persist the row only after successful
setup.

In
`@mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerStateMutations.ts`:
- Around line 40-74: The connection invalidation in
useSwitchAgentControllerModeMutation and useSwitchAgentControllerModelMutation
is broader than the session connection key. Replace each inline connection query
key with queryKeys.agentControllerConnection(args.agentControllerId,
args.resourceId, args.projectPath), preserving the existing session
invalidation.

In `@mastracode/web/src/web/ui/domains/workspaces/hooks/useWorkspaces.ts`:
- Around line 123-154: Update useDeleteWorkspaceMutation so the cascade uses an
AgentController session scoped to worktree.worktreePath inside mutationFn,
rather than the caller-provided threadSession derived from the selected
worktree. Reuse the existing session-construction pattern used by
openWorktreeThread, useAgentControllerThreads, or useWorkspaceActivity, while
preserving the current listThreads/deleteThread loop and optional-session
behavior.

---

Nitpick comments:
In
`@mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerThreadMutations.ts`:
- Around line 14-22: Deduplicate the repeated argument mapping by introducing a
shared helper beside createAgentControllerClient, then update toClientArgs and
the corresponding helpers in useAgentControllerGoalMutations.ts,
useAgentControllerRunMutations.ts, and useAgentControllerStateMutations.ts to
reuse it. Preserve the projectPath-to-scope mapping and all existing arguments.

In `@mastracode/web/src/web/ui/domains/workspaces/services/github.ts`:
- Around line 396-403: Update fetchProjectSettings to handle non-OK responses
using the same structured JSON error parsing as postProjectGitOp, preserving the
response code and message and marking 401 failures as authRequired. Keep the
successful ProjectSettings parsing unchanged and reuse the existing error shape
or helper used by postProjectGitOp.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 999c0023-d248-4609-853e-c51b2df08fee

📥 Commits

Reviewing files that changed from the base of the PR and between 2d5c856 and 3482bf0.

📒 Files selected for processing (66)
  • .changeset/big-parrots-tickle.md
  • .changeset/lucky-moons-attack.md
  • .changeset/perky-ties-shout.md
  • .changeset/wide-streets-smash.md
  • client-sdks/client-js/src/resources/agent-controller.test.ts
  • client-sdks/client-js/src/resources/agent-controller.ts
  • client-sdks/client-js/src/route-types.generated.ts
  • mastracode/web/src/shared/api/keys.ts
  • mastracode/web/src/web/github/routes.test.ts
  • mastracode/web/src/web/github/routes.ts
  • mastracode/web/src/web/github/sandbox.test.ts
  • mastracode/web/src/web/github/sandbox.ts
  • mastracode/web/src/web/github/schema.ts
  • mastracode/web/src/web/ui/Sidebar.tsx
  • mastracode/web/src/web/ui/domains/chat/components/Composer.tsx
  • mastracode/web/src/web/ui/domains/chat/components/GoalPanel.tsx
  • mastracode/web/src/web/ui/domains/chat/components/Transcript.tsx
  • mastracode/web/src/web/ui/domains/chat/context/ChatConnectionContext.ts
  • mastracode/web/src/web/ui/domains/chat/context/ChatConnectionProvider.tsx
  • mastracode/web/src/web/ui/domains/chat/context/ChatModelsProvider.tsx
  • mastracode/web/src/web/ui/domains/chat/context/ChatModesProvider.tsx
  • mastracode/web/src/web/ui/domains/chat/context/ChatPermissionsProvider.tsx
  • mastracode/web/src/web/ui/domains/chat/context/ChatSessionProvider.tsx
  • mastracode/web/src/web/ui/domains/chat/context/ChatTranscriptProvider.tsx
  • mastracode/web/src/web/ui/domains/chat/context/useRunPaletteCommand.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/__tests__/useAgentControllerMutationCache.msw.test.tsx
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerConnection.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerGoalMutations.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerModels.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerModes.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerPermissionMutations.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerPermissions.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerRunMutations.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerSessionInit.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerSessionSync.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerSettings.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerStateMutations.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerThreadMessages.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerThreadMutations.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerThreads.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useAgentControllerTranscript.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useGlobalShortcuts.ts
  • mastracode/web/src/web/ui/domains/chat/hooks/useRouteThreadSync.ts
  • mastracode/web/src/web/ui/domains/chat/services/__tests__/transcript.test.ts
  • mastracode/web/src/web/ui/domains/chat/services/agentControllerClient.ts
  • mastracode/web/src/web/ui/domains/chat/services/transcript.ts
  • mastracode/web/src/web/ui/domains/factory/__tests__/FactoryPages.msw.test.tsx
  • mastracode/web/src/web/ui/domains/factory/hooks/useStartFactoryRun.ts
  • mastracode/web/src/web/ui/domains/settings/components/ProjectSetupSection.tsx
  • mastracode/web/src/web/ui/domains/settings/components/SettingsPanel.tsx
  • mastracode/web/src/web/ui/domains/settings/components/__tests__/ProjectSetupSection.msw.test.tsx
  • mastracode/web/src/web/ui/domains/workspaces/components/WorkspacesSection.tsx
  • mastracode/web/src/web/ui/domains/workspaces/components/__tests__/WorkspacesSection.msw.test.tsx
  • mastracode/web/src/web/ui/domains/workspaces/hooks/__tests__/useProjectSettings.msw.test.tsx
  • mastracode/web/src/web/ui/domains/workspaces/hooks/__tests__/useWorkspaces.msw.test.tsx
  • mastracode/web/src/web/ui/domains/workspaces/hooks/useProjectSettings.ts
  • mastracode/web/src/web/ui/domains/workspaces/hooks/useWorkspaceActivity.ts
  • mastracode/web/src/web/ui/domains/workspaces/hooks/useWorkspaces.ts
  • mastracode/web/src/web/ui/domains/workspaces/index.ts
  • mastracode/web/src/web/ui/domains/workspaces/services/github.ts
  • packages/cli/src/commands/api/route-metadata.generated.ts
  • packages/core/src/agent-controller/agent-controller.ts
  • packages/core/src/agent-controller/session-isolation.test.ts
  • packages/server/src/server/handlers/agent-controller.test.ts
  • packages/server/src/server/handlers/agent-controller.ts
  • packages/server/src/server/server-adapter/routes/agent-controller.ts

Comment thread .changeset/wide-streets-smash.md Outdated
Comment thread packages/core/src/agent-controller/agent-controller.ts
Comment thread packages/core/src/agent-controller/session-isolation.test.ts Outdated
@superagent-security superagent-security Bot removed the pr:flagged Superagent security flag label Jul 13, 2026
…tate instead of a session peek

Threads already have per-run active/idle tracking in the agent thread-stream
runtime (the same primitive signals' ifIdle delivery uses), so annotate each
thread returned by the listThreads route with state: 'active' | 'idle' and
derive the sidebar workspace indicators from one polled listing. This drops
the invented GET .../running peek route and client session.running() method,
and replaces the per-worktree polling loops in the web UI with a single
thread-list query keyed by resource.

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) <noreply@mastra.ai>
- Make the session registry key collision-proof by JSON-encoding the
  (resourceId, scope) pair instead of joining with NUL, since NUL can
  appear in arbitrary caller-provided strings
- Await setResourceId in session-isolation tests so registry re-key
  assertions run after the operation completes
- Only sync the running flag from session.state() snapshots so a delayed
  reconnect refetch can't roll back newer SSE-driven OM progress/usage;
  the syncState reducer now preserves fields the snapshot omits
- Fix the client-js changeset example (listThreads returns an array) and
  add a usage example to the server changeset

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) <noreply@mastra.ai>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/wide-streets-smash.md:
- Line 8: Add a concise usage example to the changeset describing the new
activity-reporting API: demonstrate session.state() or session.listThreads() and
show the returned running or thread state field. Do not reference the prior
session.running() API; keep the example focused on the current public interface.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4a59dd3e-7f7a-42bc-8b12-42df71beb12f

📥 Commits

Reviewing files that changed from the base of the PR and between 43fc6aa and a894859.

📒 Files selected for processing (11)
  • .changeset/perky-ties-shout.md
  • .changeset/wide-streets-smash.md
  • client-sdks/client-js/src/resources/agent-controller.ts
  • client-sdks/client-js/src/route-types.generated.ts
  • mastracode/web/src/shared/api/keys.ts
  • mastracode/web/src/web/ui/domains/workspaces/components/WorkspacesSection.tsx
  • mastracode/web/src/web/ui/domains/workspaces/components/__tests__/WorkspacesSection.msw.test.tsx
  • mastracode/web/src/web/ui/domains/workspaces/hooks/useWorkspaceActivity.ts
  • packages/cli/src/commands/api/route-metadata.generated.ts
  • packages/server/src/server/handlers/agent-controller.test.ts
  • packages/server/src/server/handlers/agent-controller.ts
💤 Files with no reviewable changes (1)
  • packages/cli/src/commands/api/route-metadata.generated.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • mastracode/web/src/web/ui/domains/workspaces/components/tests/WorkspacesSection.msw.test.tsx
  • mastracode/web/src/web/ui/domains/workspaces/components/WorkspacesSection.tsx
  • mastracode/web/src/shared/api/keys.ts
  • client-sdks/client-js/src/resources/agent-controller.ts

Comment thread .changeset/wide-streets-smash.md Outdated
- Merge the two overlapping activity changesets into one so client-js and
  server don't get duplicate changelog entries for the same feature
- Drop the @mastra/core bump from the activity changeset: the running flag
  and per-thread state reuse existing core primitives, core only changed
  for session scoping (already covered by its own changeset)
- Bump client-js/server to minor for the session scope API since it adds
  new public API surface rather than fixing a bug

Co-Authored-By: Mastra Code (anthropic/claude-fable-5) <noreply@mastra.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

complexity: critical Critical-complexity PR tests: green ✅ Changed tests failed against base as expected

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant