fix(workspaces): start task agents on remote hosts (#13412)
This commit is contained in:
parent
f3ea376b6b
commit
a3ca887a3c
|
|
@ -750,6 +750,7 @@ describe('useComposerState host-context boundaries', () => {
|
||||||
'const submitQuick = useCallback'
|
'const submitQuick = useCallback'
|
||||||
)
|
)
|
||||||
expect(fullSubmit).toContain('platform: selectedRepoAgentLaunchPlatform')
|
expect(fullSubmit).toContain('platform: selectedRepoAgentLaunchPlatform')
|
||||||
|
expect(fullSubmit).toContain('startupDraft: startupPlan.draftPrompt')
|
||||||
expect(fullSubmit).not.toContain('platform: CLIENT_PLATFORM')
|
expect(fullSubmit).not.toContain('platform: CLIENT_PLATFORM')
|
||||||
|
|
||||||
const quickSubmit = sourceBetween(
|
const quickSubmit = sourceBetween(
|
||||||
|
|
@ -771,6 +772,7 @@ describe('useComposerState host-context boundaries', () => {
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(activation).toContain('...(startupPlan && !backendSpawnedStartup')
|
expect(activation).toContain('...(startupPlan && !backendSpawnedStartup')
|
||||||
|
expect(activation).toContain('backendStartupTerminalSpawned: true')
|
||||||
expect(activation).toContain('command: startupPlan.launchCommand')
|
expect(activation).toContain('command: startupPlan.launchCommand')
|
||||||
expect(activation).toContain('launchAgent: tuiAgent')
|
expect(activation).toContain('launchAgent: tuiAgent')
|
||||||
// The removed activation-time fallback must not come back through this caller.
|
// The removed activation-time fallback must not come back through this caller.
|
||||||
|
|
|
||||||
|
|
@ -3849,7 +3849,10 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS
|
||||||
submitCompareBaseRef,
|
submitCompareBaseRef,
|
||||||
{
|
{
|
||||||
linkedWorkItem: toFolderWorkspaceLinkedTask(submitLinkedWorkItem),
|
linkedWorkItem: toFolderWorkspaceLinkedTask(submitLinkedWorkItem),
|
||||||
linkedTaskSourceContext: taskSourceContext
|
linkedTaskSourceContext: taskSourceContext,
|
||||||
|
...(!backendStartup && startupPlan?.draftPrompt
|
||||||
|
? { startupDraft: startupPlan.draftPrompt }
|
||||||
|
: {})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const worktree = result.worktree
|
const worktree = result.worktree
|
||||||
|
|
@ -3877,6 +3880,7 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS
|
||||||
setup: result.setup,
|
setup: result.setup,
|
||||||
defaultTabs: result.defaultTabs,
|
defaultTabs: result.defaultTabs,
|
||||||
issueCommand,
|
issueCommand,
|
||||||
|
...(backendSpawnedStartup ? { backendStartupTerminalSpawned: true } : {}),
|
||||||
...(startupPlan && !backendSpawnedStartup
|
...(startupPlan && !backendSpawnedStartup
|
||||||
? {
|
? {
|
||||||
startup: {
|
startup: {
|
||||||
|
|
|
||||||
|
|
@ -370,6 +370,38 @@ describe('ensureWorktreeHasInitialTerminal', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('does not create a fallback while a backend startup terminal awaits mirroring', () => {
|
||||||
|
useAppStore.setState({
|
||||||
|
getKnownWorktreeById: ((id: string) =>
|
||||||
|
id === 'wt-1'
|
||||||
|
? { id: 'wt-1' }
|
||||||
|
: undefined) as unknown as AppStoreState['getKnownWorktreeById']
|
||||||
|
} as Partial<AppStoreState>)
|
||||||
|
const store = createMockStore()
|
||||||
|
|
||||||
|
const result = ensureWorktreeHasInitialTerminal(
|
||||||
|
store,
|
||||||
|
'wt-1',
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
{ command: 'gh issue view 42' },
|
||||||
|
undefined,
|
||||||
|
{ backendStartupTerminalSpawned: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toBeNull()
|
||||||
|
expect(store.createTab).not.toHaveBeenCalled()
|
||||||
|
expect(store.setActiveTab).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
useAppStore.setState({
|
||||||
|
tabsByWorktree: { 'wt-1': [{ id: 'mirror-tab-1' }] }
|
||||||
|
} as unknown as Partial<AppStoreState>)
|
||||||
|
|
||||||
|
expect(useAppStore.getState().pendingIssueCommandSplitByTabId['mirror-tab-1']).toEqual({
|
||||||
|
command: 'gh issue view 42'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('creates a local initial terminal for explicitly local worktrees while a runtime is focused', () => {
|
it('creates a local initial terminal for explicitly local worktrees while a runtime is focused', () => {
|
||||||
useAppStore.setState((state) => ({
|
useAppStore.setState((state) => ({
|
||||||
settings: state.settings
|
settings: state.settings
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,11 @@ type WorktreeActivationStore = Partial<WorktreeRuntimeOwnerState> & {
|
||||||
settings?: Pick<GlobalSettings, 'experimentalNativeChat' | 'openAgentTabsInChatByDefault'> | null
|
settings?: Pick<GlobalSettings, 'experimentalNativeChat' | 'openAgentTabsInChatByDefault'> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InitialTerminalOptions = {
|
||||||
|
activateCreatedTabs?: boolean
|
||||||
|
backendStartupTerminalSpawned?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared activation sequence used by the worktree palette and add-repo/worktree dialogs.
|
* Shared activation sequence used by the worktree palette and add-repo/worktree dialogs.
|
||||||
* The caller passes only `worktreeId`; the helper derives `repoId` and returns early
|
* The caller passes only `worktreeId`; the helper derives `repoId` and returns early
|
||||||
|
|
@ -279,6 +284,7 @@ export function activateAndRevealWorktree(
|
||||||
notifyHostRuntime?: boolean
|
notifyHostRuntime?: boolean
|
||||||
revealInSidebar?: boolean
|
revealInSidebar?: boolean
|
||||||
executionHostId?: ExecutionHostId
|
executionHostId?: ExecutionHostId
|
||||||
|
backendStartupTerminalSpawned?: boolean
|
||||||
}
|
}
|
||||||
): ActivateAndRevealResult | false {
|
): ActivateAndRevealResult | false {
|
||||||
const state = useAppStore.getState()
|
const state = useAppStore.getState()
|
||||||
|
|
@ -339,7 +345,8 @@ export function activateAndRevealWorktree(
|
||||||
opts?.startup,
|
opts?.startup,
|
||||||
opts?.setup,
|
opts?.setup,
|
||||||
opts?.issueCommand,
|
opts?.issueCommand,
|
||||||
opts?.defaultTabs
|
opts?.defaultTabs,
|
||||||
|
opts?.backendStartupTerminalSpawned ? { backendStartupTerminalSpawned: true } : undefined
|
||||||
)
|
)
|
||||||
if (primaryTabId && opts?.initialCwd) {
|
if (primaryTabId && opts?.initialCwd) {
|
||||||
useAppStore.getState().queueTabInitialCwd(primaryTabId, opts.initialCwd)
|
useAppStore.getState().queueTabInitialCwd(primaryTabId, opts.initialCwd)
|
||||||
|
|
@ -371,7 +378,7 @@ export function activateAndRevealWorktree(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts?.notifyHostRuntime !== false) {
|
if (opts?.notifyHostRuntime !== false && !opts?.backendStartupTerminalSpawned) {
|
||||||
ensureWebRuntimeWorktreeTerminalAfterWake(worktreeId)
|
ensureWebRuntimeWorktreeTerminalAfterWake(worktreeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -432,7 +439,7 @@ export function ensureWorktreeHasInitialTerminal(
|
||||||
setup?: WorktreeSetupLaunch,
|
setup?: WorktreeSetupLaunch,
|
||||||
issueCommand?: IssueCommandLaunch,
|
issueCommand?: IssueCommandLaunch,
|
||||||
defaultTabs?: WorktreeDefaultTabsLaunch,
|
defaultTabs?: WorktreeDefaultTabsLaunch,
|
||||||
opts?: { activateCreatedTabs?: boolean }
|
opts?: InitialTerminalOptions
|
||||||
): string | null {
|
): string | null {
|
||||||
const { renderableTabCount } = store.reconcileWorktreeTabModel(worktreeId)
|
const { renderableTabCount } = store.reconcileWorktreeTabModel(worktreeId)
|
||||||
// Why: creating a terminal just because the legacy terminal slice is empty gives editor/browser-only worktrees an unexpected extra tab.
|
// Why: creating a terminal just because the legacy terminal slice is empty gives editor/browser-only worktrees an unexpected extra tab.
|
||||||
|
|
@ -459,8 +466,12 @@ export function ensureWorktreeHasInitialTerminal(
|
||||||
wrappedSetupCommandStr = sequenced.setupCommand
|
wrappedSetupCommandStr = sequenced.setupCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
// Why: web clients mirror the server's session tabs, so avoid spawning a duplicate host terminal before the mirror lands.
|
const backendStartupTerminalSpawned = opts?.backendStartupTerminalSpawned === true
|
||||||
if (isWebRuntimeSessionActive(getRuntimeEnvironmentIdForWorktree(ownerState, worktreeId))) {
|
// Why: explicit spawn evidence survives the new-worktree ownership race; active web sessions provide the same authority for later activations.
|
||||||
|
if (
|
||||||
|
backendStartupTerminalSpawned ||
|
||||||
|
isWebRuntimeSessionActive(getRuntimeEnvironmentIdForWorktree(ownerState, worktreeId))
|
||||||
|
) {
|
||||||
const existingTerminalTabId = store.tabsByWorktree[worktreeId]?.[0]?.id
|
const existingTerminalTabId = store.tabsByWorktree[worktreeId]?.[0]?.id
|
||||||
if (existingTerminalTabId && (setup || issueCommand)) {
|
if (existingTerminalTabId && (setup || issueCommand)) {
|
||||||
queueSetupAndIssueCommands(
|
queueSetupAndIssueCommands(
|
||||||
|
|
@ -474,6 +485,9 @@ export function ensureWorktreeHasInitialTerminal(
|
||||||
)
|
)
|
||||||
return existingTerminalTabId
|
return existingTerminalTabId
|
||||||
}
|
}
|
||||||
|
if (existingTerminalTabId && backendStartupTerminalSpawned) {
|
||||||
|
return existingTerminalTabId
|
||||||
|
}
|
||||||
if (setup || issueCommand) {
|
if (setup || issueCommand) {
|
||||||
// Why: runtime-owned worktrees mirror session tabs async, so hold commands for the first mirrored tab instead of dropping them.
|
// Why: runtime-owned worktrees mirror session tabs async, so hold commands for the first mirrored tab instead of dropping them.
|
||||||
queueHookCommandsForFirstWorktreeTab({
|
queueHookCommandsForFirstWorktreeTab({
|
||||||
|
|
@ -586,7 +600,7 @@ function applyDefaultTerminalTabs(
|
||||||
issueCommand: IssueCommandLaunch | undefined,
|
issueCommand: IssueCommandLaunch | undefined,
|
||||||
defaultTabs: WorktreeDefaultTabsLaunch | undefined,
|
defaultTabs: WorktreeDefaultTabsLaunch | undefined,
|
||||||
wrappedSetupCommandStr: string | undefined,
|
wrappedSetupCommandStr: string | undefined,
|
||||||
opts: { activateCreatedTabs?: boolean } | undefined
|
opts: InitialTerminalOptions | undefined
|
||||||
): string | null {
|
): string | null {
|
||||||
if (!defaultTabs || store.defaultTerminalTabsAppliedByWorktreeId[worktreeId]) {
|
if (!defaultTabs || store.defaultTerminalTabsAppliedByWorktreeId[worktreeId]) {
|
||||||
return null
|
return null
|
||||||
|
|
@ -675,7 +689,7 @@ function queueSetupAndIssueCommands(
|
||||||
setup: WorktreeSetupLaunch | undefined,
|
setup: WorktreeSetupLaunch | undefined,
|
||||||
issueCommand: IssueCommandLaunch | undefined,
|
issueCommand: IssueCommandLaunch | undefined,
|
||||||
wrappedSetupCommandStr: string | undefined,
|
wrappedSetupCommandStr: string | undefined,
|
||||||
opts: { activateCreatedTabs?: boolean } | undefined
|
opts: InitialTerminalOptions | undefined
|
||||||
): void {
|
): void {
|
||||||
// Why: setup launch location is user-configurable — 'new-tab' keeps setup output off the primary pane; splits keep it adjacent.
|
// Why: setup launch location is user-configurable — 'new-tab' keeps setup output off the primary pane; splits keep it adjacent.
|
||||||
if (setup) {
|
if (setup) {
|
||||||
|
|
|
||||||
|
|
@ -512,13 +512,14 @@ describe('staged background worktree creation', () => {
|
||||||
expect(store.setSidebarOpen).not.toHaveBeenCalled()
|
expect(store.setSidebarOpen).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('does not reveal a completed staged create after the user leaves the creation surface', async () => {
|
it('keeps a backend startup terminal in the background after the user leaves', async () => {
|
||||||
store.activeView = 'tasks'
|
store.activeView = 'tasks'
|
||||||
store.createWorktree.mockResolvedValueOnce({
|
store.createWorktree.mockResolvedValueOnce({
|
||||||
worktree: {
|
worktree: {
|
||||||
id: 'wt-1',
|
id: 'wt-1',
|
||||||
repoId: 'repo-1'
|
repoId: 'repo-1'
|
||||||
}
|
},
|
||||||
|
startupTerminal: { tabId: 'agent-tab', spawned: true }
|
||||||
})
|
})
|
||||||
|
|
||||||
const started = continueBackgroundWorktreeCreation('creation-1', makeRequest(), {
|
const started = continueBackgroundWorktreeCreation('creation-1', makeRequest(), {
|
||||||
|
|
@ -535,7 +536,7 @@ describe('staged background worktree creation', () => {
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
{ activateCreatedTabs: false }
|
{ activateCreatedTabs: false, backendStartupTerminalSpawned: true }
|
||||||
)
|
)
|
||||||
expect(queueWorkspaceActivationTerminalFocus).not.toHaveBeenCalled()
|
expect(queueWorkspaceActivationTerminalFocus).not.toHaveBeenCalled()
|
||||||
expect(store.removePendingWorktreeCreation).toHaveBeenCalledWith('creation-1', {
|
expect(store.removePendingWorktreeCreation).toHaveBeenCalledWith('creation-1', {
|
||||||
|
|
@ -543,8 +544,11 @@ describe('staged background worktree creation', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('reveals the completed workspace after the user switches to another workspace', async () => {
|
it('reveals a backend-owned startup after the user switches workspaces', async () => {
|
||||||
let resolveCreate!: (result: { worktree: { id: string; repoId: string } }) => void
|
let resolveCreate!: (result: {
|
||||||
|
worktree: { id: string; repoId: string }
|
||||||
|
startupTerminal: { tabId: string; spawned: true }
|
||||||
|
}) => void
|
||||||
store.createWorktree.mockReturnValueOnce(
|
store.createWorktree.mockReturnValueOnce(
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
resolveCreate = resolve
|
resolveCreate = resolve
|
||||||
|
|
@ -560,11 +564,15 @@ describe('staged background worktree creation', () => {
|
||||||
// Why: selecting a real workspace clears only the pending surface pointer;
|
// Why: selecting a real workspace clears only the pending surface pointer;
|
||||||
// completion should still finish the task-launch handoff once it is ready.
|
// completion should still finish the task-launch handoff once it is ready.
|
||||||
store.activePendingCreationId = null
|
store.activePendingCreationId = null
|
||||||
resolveCreate({ worktree: { id: 'wt-1', repoId: 'repo-1' } })
|
resolveCreate({
|
||||||
|
worktree: { id: 'wt-1', repoId: 'repo-1' },
|
||||||
|
startupTerminal: { tabId: 'agent-tab', spawned: true }
|
||||||
|
})
|
||||||
await flushAsyncWorktreeCreation()
|
await flushAsyncWorktreeCreation()
|
||||||
|
|
||||||
expect(activateAndRevealWorktree).toHaveBeenCalledWith('wt-1', {
|
expect(activateAndRevealWorktree).toHaveBeenCalledWith('wt-1', {
|
||||||
sidebarRevealBehavior: 'auto'
|
sidebarRevealBehavior: 'auto',
|
||||||
|
backendStartupTerminalSpawned: true
|
||||||
})
|
})
|
||||||
expect(ensureWorktreeHasInitialTerminal).not.toHaveBeenCalled()
|
expect(ensureWorktreeHasInitialTerminal).not.toHaveBeenCalled()
|
||||||
expect(store.removePendingWorktreeCreation).toHaveBeenCalledWith('creation-1', {
|
expect(store.removePendingWorktreeCreation).toHaveBeenCalledWith('creation-1', {
|
||||||
|
|
@ -731,6 +739,10 @@ describe('staged background worktree creation', () => {
|
||||||
expect(store.seedNativeChatLaunchDraft).toHaveBeenCalledWith(
|
expect(store.seedNativeChatLaunchDraft).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({ tabId: 'agent-tab' })
|
expect.objectContaining({ tabId: 'agent-tab' })
|
||||||
)
|
)
|
||||||
|
const createCall = store.createWorktree.mock.calls[0] as unknown[] | undefined
|
||||||
|
expect(createCall?.[25]).toEqual({
|
||||||
|
startupDraft: 'https://github.com/o/r/issues/12'
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,10 @@ async function executeWorktreeCreation(
|
||||||
: {}),
|
: {}),
|
||||||
...(preparedRequest.linkedTaskSourceContext !== undefined
|
...(preparedRequest.linkedTaskSourceContext !== undefined
|
||||||
? { linkedTaskSourceContext: preparedRequest.linkedTaskSourceContext }
|
? { linkedTaskSourceContext: preparedRequest.linkedTaskSourceContext }
|
||||||
|
: {}),
|
||||||
|
// Why: the remote host must own task-draft startup so its initial terminal is the agent, not an idle fallback shell.
|
||||||
|
...(!backendStartup && preparedRequest.agent && preparedRequest.launchDraftPrompt
|
||||||
|
? { startupDraft: preparedRequest.launchDraftPrompt }
|
||||||
: {})
|
: {})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -167,7 +171,6 @@ async function executeWorktreeCreation(
|
||||||
}
|
}
|
||||||
|
|
||||||
const worktree = result.worktree
|
const worktree = result.worktree
|
||||||
|
|
||||||
// Why: if the user dismissed/cancelled while the create was in flight, the entry
|
// Why: if the user dismissed/cancelled while the create was in flight, the entry
|
||||||
// is gone. Git already made the worktree on disk, but don't auto-provision (trust
|
// is gone. Git already made the worktree on disk, but don't auto-provision (trust
|
||||||
// write, terminal, agent, note) work they abandoned — it surfaces as a plain row
|
// write, terminal, agent, note) work they abandoned — it surfaces as a plain row
|
||||||
|
|
@ -210,7 +213,8 @@ async function executeWorktreeCreation(
|
||||||
...(result.setup ? { setup: result.setup } : {}),
|
...(result.setup ? { setup: result.setup } : {}),
|
||||||
...(result.defaultTabs ? { defaultTabs: result.defaultTabs } : {}),
|
...(result.defaultTabs ? { defaultTabs: result.defaultTabs } : {}),
|
||||||
...(startupOpt ? { startup: startupOpt } : {}),
|
...(startupOpt ? { startup: startupOpt } : {}),
|
||||||
...(preparedRequest.issueCommand ? { issueCommand: preparedRequest.issueCommand } : {})
|
...(preparedRequest.issueCommand ? { issueCommand: preparedRequest.issueCommand } : {}),
|
||||||
|
...(backendSpawned ? { backendStartupTerminalSpawned: true } : {})
|
||||||
})
|
})
|
||||||
primaryTabId = activation === false ? null : activation.primaryTabId
|
primaryTabId = activation === false ? null : activation.primaryTabId
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -224,7 +228,10 @@ async function executeWorktreeCreation(
|
||||||
result.setup,
|
result.setup,
|
||||||
preparedRequest.issueCommand,
|
preparedRequest.issueCommand,
|
||||||
result.defaultTabs,
|
result.defaultTabs,
|
||||||
{ activateCreatedTabs: false }
|
{
|
||||||
|
activateCreatedTabs: false,
|
||||||
|
...(backendSpawned ? { backendStartupTerminalSpawned: true } : {})
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,8 @@ export type WorktreeSlice = {
|
||||||
automationProvenanceRequest?: CreateWorktreeArgs['automationProvenanceRequest']
|
automationProvenanceRequest?: CreateWorktreeArgs['automationProvenanceRequest']
|
||||||
linkedWorkItem?: WorkspaceLinkedItem | null
|
linkedWorkItem?: WorkspaceLinkedItem | null
|
||||||
linkedTaskSourceContext?: TaskSourceContext | null
|
linkedTaskSourceContext?: TaskSourceContext | null
|
||||||
|
/** Lets the owning runtime launch and prefill a task agent without first creating an idle shell. */
|
||||||
|
startupDraft?: string
|
||||||
}
|
}
|
||||||
) => Promise<CreateWorktreeResult>
|
) => Promise<CreateWorktreeResult>
|
||||||
/** Register an in-flight background creation and make it the active surface. */
|
/** Register an in-flight background creation and make it the active surface. */
|
||||||
|
|
|
||||||
|
|
@ -5913,6 +5913,42 @@ describe('worktree remote runtime mutations', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('passes task startup drafts only to the owning remote runtime', async () => {
|
||||||
|
const store = createTestStore()
|
||||||
|
const wt = makeWorktree({
|
||||||
|
id: 'repo1::/path/task-draft',
|
||||||
|
repoId: 'repo1',
|
||||||
|
path: '/path/task-draft'
|
||||||
|
})
|
||||||
|
runtimeEnvironmentCall.mockResolvedValue({
|
||||||
|
id: 'rpc-create',
|
||||||
|
ok: true,
|
||||||
|
result: { worktree: wt },
|
||||||
|
_meta: { runtimeId: 'runtime-remote' }
|
||||||
|
})
|
||||||
|
store.setState({
|
||||||
|
settings: { activeRuntimeEnvironmentId: 'env-1' } as never,
|
||||||
|
worktreesByRepo: { repo1: [] }
|
||||||
|
} as Partial<AppState>)
|
||||||
|
const createWorktree = store.getState().createWorktree
|
||||||
|
const args: Parameters<typeof createWorktree> = ['repo1', 'task-draft', undefined, 'inherit']
|
||||||
|
args[10] = 'codex'
|
||||||
|
args[25] = { startupDraft: 'https://github.com/stablyai/orca/issues/12' }
|
||||||
|
|
||||||
|
await createWorktree(...args)
|
||||||
|
|
||||||
|
expect(runtimeEnvironmentCall).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
method: 'worktree.create',
|
||||||
|
params: expect.objectContaining({
|
||||||
|
createdWithAgent: 'codex',
|
||||||
|
startupDraft: 'https://github.com/stablyai/orca/issues/12'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(mockApi.worktrees.create).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
it('passes startup commands through local worktree creation IPC', async () => {
|
it('passes startup commands through local worktree creation IPC', async () => {
|
||||||
const store = createTestStore()
|
const store = createTestStore()
|
||||||
const wt = makeWorktree({
|
const wt = makeWorktree({
|
||||||
|
|
|
||||||
|
|
@ -3989,6 +3989,7 @@ export const createWorktreeSlice: StateCreator<AppState, [], [], WorktreeSlice>
|
||||||
const automationProvenanceRequest = options?.automationProvenanceRequest
|
const automationProvenanceRequest = options?.automationProvenanceRequest
|
||||||
const linkedWorkItem = options?.linkedWorkItem
|
const linkedWorkItem = options?.linkedWorkItem
|
||||||
const linkedTaskSourceContext = options?.linkedTaskSourceContext
|
const linkedTaskSourceContext = options?.linkedTaskSourceContext
|
||||||
|
const startupDraft = options?.startupDraft
|
||||||
try {
|
try {
|
||||||
for (let attempt = 0; attempt < CLIENT_WORKTREE_CREATE_MAX_ATTEMPTS; attempt += 1) {
|
for (let attempt = 0; attempt < CLIENT_WORKTREE_CREATE_MAX_ATTEMPTS; attempt += 1) {
|
||||||
const candidateName = getClientWorktreeCreateCandidate(name, attempt)
|
const candidateName = getClientWorktreeCreateCandidate(name, attempt)
|
||||||
|
|
@ -4095,6 +4096,7 @@ export const createWorktreeSlice: StateCreator<AppState, [], [], WorktreeSlice>
|
||||||
...(linkedGiteaPR !== undefined ? { linkedGiteaPR } : {}),
|
...(linkedGiteaPR !== undefined ? { linkedGiteaPR } : {}),
|
||||||
...(linkedWorkItem !== undefined ? { linkedWorkItem } : {}),
|
...(linkedWorkItem !== undefined ? { linkedWorkItem } : {}),
|
||||||
...(linkedTaskSourceContext !== undefined ? { linkedTaskSourceContext } : {}),
|
...(linkedTaskSourceContext !== undefined ? { linkedTaskSourceContext } : {}),
|
||||||
|
...(startupDraft ? { startupDraft } : {}),
|
||||||
...(automationProvenanceRequest ? { automationProvenanceRequest } : {}),
|
...(automationProvenanceRequest ? { automationProvenanceRequest } : {}),
|
||||||
...(startup
|
...(startup
|
||||||
? {
|
? {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue