feat: open isolated multirun sessions before setup completes
Open isolated Multi-Run sessions while worktree setup continues in the background Apply the faster flow to VS Code Agent Manager isolated runs Document the new Multi-Run and Agent Manager behavior
This commit is contained in:
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
- Chat: added `/handoff-review` to open a linked review session for the current workspace changes, with actions to send review feedback and implementation replies between the sessions.
|
||||||
|
- Worktrees: creating a single new worktree session now opens the session immediately while worktree setup continues in the background.
|
||||||
|
- Multi-Run: creating isolated runs now opens sessions immediately while worktree setup continues in the background.
|
||||||
|
- Sessions: chat folder assignments now stay in place after reloads.
|
||||||
|
|
||||||
## [1.12.3] - 2026-06-05
|
## [1.12.3] - 2026-06-05
|
||||||
|
|
||||||
- Windows/Startup: WSL OpenCode installs are no longer detected or launched; install OpenCode natively on Windows and configure `opencode.cmd` or `opencode.exe` instead.
|
- Windows/Startup: WSL OpenCode installs are no longer detected or launched; install OpenCode natively on Windows and configure `opencode.cmd` or `opencode.exe` instead.
|
||||||
|
|||||||
@@ -4,6 +4,24 @@ import type { Session } from '@opencode-ai/sdk/v2';
|
|||||||
const upsertedSessions: Session[] = [];
|
const upsertedSessions: Session[] = [];
|
||||||
const registeredDirectories: Array<{ sessionID: string; directory: string }> = [];
|
const registeredDirectories: Array<{ sessionID: string; directory: string }> = [];
|
||||||
const ensureChildCalls: Array<{ directory: string; bootstrap?: boolean }> = [];
|
const ensureChildCalls: Array<{ directory: string; bootstrap?: boolean }> = [];
|
||||||
|
const worktreeMetadataCalls: Array<{ sessionId: string; path: string }> = [];
|
||||||
|
const worktreeCreateCalls: Array<{ project: { id?: string; path: string }; args: Record<string, unknown>; options: unknown }> = [];
|
||||||
|
let isGitRepository = false;
|
||||||
|
const createWorktreeWithDefaultsMock = mock((project: { id?: string; path: string }, args: Record<string, unknown>, options: unknown) => {
|
||||||
|
worktreeCreateCalls.push({ project, args, options });
|
||||||
|
return Promise.resolve({
|
||||||
|
source: 'sdk',
|
||||||
|
name: 'fix-thing',
|
||||||
|
path: '/repo-worktrees/fix-thing',
|
||||||
|
projectDirectory: '/repo',
|
||||||
|
branch: 'fix-thing',
|
||||||
|
label: 'fix-thing',
|
||||||
|
worktreeRoot: '/repo-worktrees/fix-thing',
|
||||||
|
worktreeStatus: 'pending',
|
||||||
|
headState: 'branch',
|
||||||
|
worktreeSource: 'created-for-session',
|
||||||
|
});
|
||||||
|
});
|
||||||
const childState = {
|
const childState = {
|
||||||
session: [] as Session[],
|
session: [] as Session[],
|
||||||
sessionTotal: 0,
|
sessionTotal: 0,
|
||||||
@@ -16,6 +34,9 @@ mock.module('@/sync/session-ui-store', () => ({
|
|||||||
useSessionUIStore: {
|
useSessionUIStore: {
|
||||||
getState: () => ({
|
getState: () => ({
|
||||||
markSessionAsOpenChamberCreated: mock(() => undefined),
|
markSessionAsOpenChamberCreated: mock(() => undefined),
|
||||||
|
setWorktreeMetadata: (sessionId: string, metadata: { path: string }) => {
|
||||||
|
worktreeMetadataCalls.push({ sessionId, path: metadata.path });
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
@@ -41,11 +62,11 @@ mock.module('@/lib/opencode/client', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
mock.module('@/lib/gitApi', () => ({
|
mock.module('@/lib/gitApi', () => ({
|
||||||
checkIsGitRepository: mock(() => Promise.resolve(false)),
|
checkIsGitRepository: mock(() => Promise.resolve(isGitRepository)),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
mock.module('@/lib/worktrees/worktreeCreate', () => ({
|
mock.module('@/lib/worktrees/worktreeCreate', () => ({
|
||||||
createWorktreeWithDefaults: mock(),
|
createWorktreeWithDefaults: createWorktreeWithDefaultsMock,
|
||||||
resolveRootTrackingRemote: mock(() => Promise.resolve(null)),
|
resolveRootTrackingRemote: mock(() => Promise.resolve(null)),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -116,6 +137,9 @@ describe('useMultiRunStore', () => {
|
|||||||
upsertedSessions.length = 0;
|
upsertedSessions.length = 0;
|
||||||
registeredDirectories.length = 0;
|
registeredDirectories.length = 0;
|
||||||
ensureChildCalls.length = 0;
|
ensureChildCalls.length = 0;
|
||||||
|
worktreeMetadataCalls.length = 0;
|
||||||
|
worktreeCreateCalls.length = 0;
|
||||||
|
isGitRepository = false;
|
||||||
childState.session = [];
|
childState.session = [];
|
||||||
childState.sessionTotal = 0;
|
childState.sessionTotal = 0;
|
||||||
childState.limit = 5;
|
childState.limit = 5;
|
||||||
@@ -139,4 +163,25 @@ describe('useMultiRunStore', () => {
|
|||||||
expect(ensureChildCalls).toEqual([{ directory: '/repo', bootstrap: false }]);
|
expect(ensureChildCalls).toEqual([{ directory: '/repo', bootstrap: false }]);
|
||||||
expect(childState.session.map((session) => session.id)).toEqual(['ses_multirun']);
|
expect(childState.session.map((session) => session.id)).toEqual(['ses_multirun']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('uses fast background worktree creation for isolated runs', async () => {
|
||||||
|
isGitRepository = true;
|
||||||
|
|
||||||
|
const result = await useMultiRunStore.getState().createMultiRun({
|
||||||
|
name: 'Fix thing',
|
||||||
|
isolateRuns: true,
|
||||||
|
groups: [{
|
||||||
|
prompt: 'Fix it',
|
||||||
|
models: [{ providerID: 'anthropic', modelID: 'claude-sonnet-4-5' }],
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result?.sessionIds).toEqual(['ses_multirun']);
|
||||||
|
expect(worktreeCreateCalls.length).toBe(1);
|
||||||
|
expect(worktreeCreateCalls[0]?.project).toEqual({ id: 'project-1', path: '/repo' });
|
||||||
|
expect(worktreeCreateCalls[0]?.args.returnAfterDirectoryCreated).toBe(true);
|
||||||
|
expect(worktreeCreateCalls[0]?.options).toEqual({ resolvedRootTrackingRemote: null });
|
||||||
|
expect(registeredDirectories).toEqual([{ sessionID: 'ses_multirun', directory: '/repo-worktrees/fix-thing' }]);
|
||||||
|
expect(worktreeMetadataCalls).toEqual([{ sessionId: 'ses_multirun', path: '/repo-worktrees/fix-thing' }]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -233,6 +233,7 @@ export const useMultiRunStore = create<MultiRunStore>()(
|
|||||||
worktreeName: preferredName,
|
worktreeName: preferredName,
|
||||||
startRef: params.worktreeBaseBranch || 'HEAD',
|
startRef: params.worktreeBaseBranch || 'HEAD',
|
||||||
setupCommands: commandsToRun,
|
setupCommands: commandsToRun,
|
||||||
|
returnAfterDirectoryCreated: true,
|
||||||
}, {
|
}, {
|
||||||
resolvedRootTrackingRemote: rootTrackingRemote,
|
resolvedRootTrackingRemote: rootTrackingRemote,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
- Agent Manager: creating isolated runs now opens sessions immediately while worktree setup continues in the background.
|
||||||
|
- Sessions: chat folder assignments now stay in place after reloads.
|
||||||
|
|
||||||
## [1.12.3] - 2026-06-05
|
## [1.12.3] - 2026-06-05
|
||||||
|
|
||||||
- Startup: OpenCode health checks now work with OpenCode 1.15.x.
|
- Startup: OpenCode health checks now work with OpenCode 1.15.x.
|
||||||
|
|||||||
Reference in New Issue
Block a user