perf(worktree): prepare agent checkouts before composer submission

This commit is contained in:
Neil
2026-09-05 01:41:49 -07:00
parent 1f7392d2d7
commit da23e30e90
11 changed files with 178 additions and 19 deletions
@@ -185,11 +185,11 @@ function QuickTabBody({
}, [])
useEffect(() => {
if (!active || quickAgent !== null || createDisabled) {
if (!active || createDisabled) {
return
}
let cancelled = false
void prepareQuickWorkspace(() => cancelled)
void prepareQuickWorkspace(quickAgent, () => cancelled)
return () => {
cancelled = true
}
@@ -110,7 +110,7 @@ export type ComposerSubmitModel = {
) => QuickSubmitSource | null
resetForNextCreate: () => void
submit: () => Promise<void>
prepareQuickWorkspace: (isCancelled: () => boolean) => Promise<void>
prepareQuickWorkspace: (agent: TuiAgent | null, isCancelled: () => boolean) => Promise<void>
submitQuick: (agent: TuiAgent | null) => Promise<void>
submitFolderTarget: (requestedAgent: TuiAgent | null) => Promise<void>
}
@@ -81,9 +81,7 @@ export function useQuickCreationExecution(input: QuickCreationExecutionInput) {
if (
!isCancelled ||
(preparation &&
(!selectedRepoIsGit ||
requestedAgent !== null ||
(ephemeralVmsEnabled && selectedEphemeralVmRecipeId)))
(!selectedRepoIsGit || (ephemeralVmsEnabled && selectedEphemeralVmRecipeId)))
) {
return
}
@@ -205,7 +205,7 @@ export function useQuickSubmitAction(input: QuickSubmitActionInput) {
)
const prepareQuickWorkspace = useCallback(
(isCancelled: () => boolean) => submitQuick(null, { isCancelled }),
(agent: TuiAgent | null, isCancelled: () => boolean) => submitQuick(agent, { isCancelled }),
[submitQuick]
)
return { submitQuick, prepareQuickWorkspace }
+1 -1
View File
@@ -59,7 +59,7 @@ export type UseComposerStateResult = {
promptTextareaRef: RefObject<HTMLTextAreaElement | null>
nameInputRef: RefObject<HTMLInputElement | null>
submit: () => Promise<void>
prepareQuickWorkspace: (isCancelled: () => boolean) => Promise<void>
prepareQuickWorkspace: (agent: TuiAgent | null, isCancelled: () => boolean) => Promise<void>
submitQuick: (agent: TuiAgent | null) => Promise<void>
createDisabled: boolean
selectAddedProjectRepo: (repoId: string) => void
@@ -0,0 +1,67 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createRequestedWorktree } from './create-requested-worktree'
import { makeRequest } from './worktree-creation-request.test-fixture'
const { createWorktree } = vi.hoisted(() => ({ createWorktree: vi.fn() }))
vi.mock('@/store', () => ({
useAppStore: { getState: () => ({ createWorktree }) }
}))
vi.mock('@/lib/worktree-draft-startup-view-mode', () => ({
resolveBackendDraftStartup: (request: { startup?: unknown }) => request.startup
}))
beforeEach(() => vi.clearAllMocks())
describe('durable composer creation launch boundary', () => {
it.each(['command', 'draft'] as const)(
'withholds agent %s execution until Create while retaining workspace metadata',
async (delivery) => {
const request = makeRequest({
agent: 'codex',
startup: delivery === 'command' ? { command: 'codex', launchAgent: 'codex' } : undefined,
launchDraftPrompt: 'Investigate this task',
startupPlan: {
agent: 'codex',
launchCommand: 'codex',
expectedProcess: 'codex',
followupPrompt: null,
launchConfig: { agentArgs: '', agentEnv: {} }
}
})
const snapshot = structuredClone(request)
await createRequestedWorktree('reservation', request, true)
const args = createWorktree.mock.calls[0]
expect(args[10]).toBe('codex')
expect(args[16]).toBeUndefined()
expect(args[25]).not.toHaveProperty('startupDraft')
expect(request).toEqual(snapshot)
}
)
it('preserves ordinary backend agent launch', async () => {
const startup = { command: 'codex', launchAgent: 'codex' as const }
await createRequestedWorktree('submit', makeRequest({ agent: 'codex', startup }))
expect(createWorktree.mock.calls[0][16]).toEqual(startup)
})
it('preserves ordinary host-owned draft launch', async () => {
await createRequestedWorktree(
'submit',
makeRequest({ agent: 'codex', launchDraftPrompt: 'Unsent task' })
)
expect(createWorktree.mock.calls[0][25]).toMatchObject({ startupDraft: 'Unsent task' })
})
it('continues warming blank shells without selecting them', async () => {
await createRequestedWorktree(
'reservation',
makeRequest({ startup: { command: '', env: { PROJECT: 'fixture' } } }),
true
)
expect(createWorktree.mock.calls[0][16]).toEqual({
command: '',
env: { PROJECT: 'fixture' },
activate: false
})
})
})
@@ -12,8 +12,11 @@ export function createRequestedWorktree(
): Promise<CreateWorktreeResult> {
const provisionedRoot = getProvisionedRootCreateOptions(preparedRequest)
const structuredLaunch = preparedRequest.agentLaunchRoute === 'structured-native-chat'
const deferAgentLaunch = background && preparedRequest.agent !== null
const backendStartup =
provisionedRoot || structuredLaunch ? undefined : resolveBackendDraftStartup(preparedRequest)
provisionedRoot || structuredLaunch || deferAgentLaunch
? undefined
: resolveBackendDraftStartup(preparedRequest)
return useAppStore
.getState()
.createWorktree(
@@ -54,7 +57,8 @@ export function createRequestedWorktree(
? { linkedTaskSourceContext: preparedRequest.linkedTaskSourceContext }
: {}),
// Why: the remote host must own task-draft startup so its initial terminal is the agent, not an idle fallback shell.
...(!structuredLaunch &&
...(!deferAgentLaunch &&
!structuredLaunch &&
!backendStartup &&
preparedRequest.agent &&
preparedRequest.launchDraftPrompt
@@ -0,0 +1,69 @@
import { describe, expect, it, vi } from 'vitest'
import type { CreateWorktreeResult } from '../../../shared/worktree/create-types'
import { makeRequest } from './worktree-creation-request.test-fixture'
import { executeWorktreeCreation } from './worktree-creation-flow-execute'
import { activateAndRevealWorktree } from './worktree-activation'
const { store } = vi.hoisted(() => ({
store: {
createWorktree: vi.fn(),
pendingWorktreeCreations: { 'creation-1': {} },
activeView: 'terminal',
activePendingCreationId: 'creation-1'
}
}))
vi.mock('@/store', () => ({ useAppStore: { getState: () => store } }))
vi.mock('@/lib/browser-uuid', () => ({ createBrowserUuid: () => 'creation-1' }))
vi.mock('@/lib/worktree-activation', () => ({ activateAndRevealWorktree: vi.fn() }))
vi.mock('@/lib/worktree-initial-terminal-seeding', () => ({
ensureWorktreeHasInitialTerminal: vi.fn()
}))
vi.mock('@/lib/agent-trust-preflight', () => ({ preflightAgentTrust: vi.fn() }))
vi.mock('@/lib/ephemeral-vm-worktree-creation', () => ({
attachEphemeralVmRuntimeToWorkspace: vi.fn(),
cleanupEphemeralVmRuntimeForFailedCreate: vi.fn(),
prepareRequestForCreate: vi.fn()
}))
vi.mock('@/lib/worktree-creation-structured-session', () => ({
launchStructuredWorktreeSession: vi.fn()
}))
vi.mock('@/lib/worktree-creation-structured-recovery', () => ({
markStructuredWorktreeLaunchUnconfirmed: vi.fn()
}))
vi.mock('@/lib/worktree-creation-completion', () => ({ completeWorktreeCreation: vi.fn() }))
describe('retained agent activation', () => {
it('launches the selected agent from a retained checkout without creating again', async () => {
vi.mocked(activateAndRevealWorktree).mockReturnValueOnce({ primaryTabId: 'agent-tab' })
const launchConfig = { agentArgs: '--model selected', agentEnv: { PROJECT: 'fixture' } }
const request = makeRequest({
agent: 'codex',
launchDraftPrompt: 'Unsent task',
startupPlan: {
agent: 'codex',
launchCommand: 'codex --model selected',
expectedProcess: 'codex',
followupPrompt: null,
draftPrompt: 'Unsent task',
env: { PROJECT: 'fixture' },
launchConfig
}
})
await executeWorktreeCreation('creation-1', request, {
worktree: { id: 'wt-agent', repoId: 'repo-1' }
} as CreateWorktreeResult)
expect(store.createWorktree).not.toHaveBeenCalled()
expect(activateAndRevealWorktree).toHaveBeenCalledExactlyOnceWith('wt-agent', {
sidebarRevealBehavior: 'auto',
startup: {
command: 'codex --model selected',
launchAgent: 'codex',
env: { PROJECT: 'fixture' },
launchConfig,
launchToken: 'creation-1',
draftPrompt: 'Unsent task',
launchDraftText: 'Unsent task'
}
})
})
})
@@ -26,6 +26,28 @@ const result = {
} as CreateWorktreeResult
describe('retained composer worktree creation', () => {
it('retains an agent checkout and preserves the exact launch plan for Create', async () => {
const checkout = { worktree: result.worktree }
const create = vi.fn(async () => checkout)
const controller = createRetainedWorktreeCreation(create)
const agentRequest = request({
agent: 'codex',
startup: { command: 'codex', launchAgent: 'codex' },
startupPlan: {
agent: 'codex',
launchCommand: 'codex',
expectedProcess: 'codex',
followupPrompt: null,
launchConfig: { agentArgs: '', agentEnv: {} }
},
launchDraftPrompt: 'Keep this unsent'
})
expect(controller.start(agentRequest, 'owner')).toBe(true)
expect(await controller.take(agentRequest, 'owner')).toBe(checkout)
expect(create).toHaveBeenCalledExactlyOnceWith(agentRequest)
expect(controller.take(agentRequest, 'owner')).toBeNull()
})
it('retains a remote blank checkout whose terminal starts on activation', async () => {
const create = vi.fn(async () => ({ worktree: result.worktree }))
const controller = createRetainedWorktreeCreation(create)
@@ -3,12 +3,13 @@ import type { WorktreeCreationRequest } from './pending-worktree-creation'
export function canRetainComposerWorktree(request: WorktreeCreationRequest): boolean {
return (
request.agent === null &&
(request.startup === undefined || request.startup.command === '') &&
!request.startup?.launchAgent &&
!request.startup?.launchConfig &&
!request.startupPlan &&
!request.launchDraftPrompt &&
(request.agent !== null
? Boolean(request.startupPlan)
: (request.startup === undefined || request.startup.command === '') &&
!request.startup?.launchAgent &&
!request.startup?.launchConfig &&
!request.startupPlan &&
!request.launchDraftPrompt) &&
!request.issueCommand &&
!request.ephemeralVmRecipe &&
!request.ephemeralVmRuntimeId &&
+1 -3
View File
@@ -114,9 +114,7 @@ export type Worktree = {
* grant newly-created worktrees a short grace window at the top of Recent,
* immune to ambient PTY-bump reordering in other worktrees. */
createdAt?: number
/** Agent selected when Orca originally created the worktree. Used only to
* seed a replacement terminal if the user later reopens the worktree after
* closing every visible surface. */
/** Agent selected at creation; metadata alone never requests an agent launch. */
createdWithAgent?: TuiAgent
/** True while an auto-named workspace is waiting for the first agent message
* to drive the branch/title rename. */