mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
fix(onboarding): start the default agent in the first folder added after skipping onboarding (#32701)
This commit is contained in:
@@ -2,7 +2,6 @@ import { describe, expect, it } from 'vitest'
|
||||
import { getDefaultSettings } from '../../../../shared/constants'
|
||||
import { getDefaultOnboardingState } from '../../../../shared/onboarding-defaults'
|
||||
import {
|
||||
buildDismissedOnboardingFolderAgentStartup,
|
||||
buildOnboardingFolderAgentStartup,
|
||||
shouldSeedFolderAgentAfterDismissedOnboarding
|
||||
} from '@/lib/onboarding-folder-agent-startup'
|
||||
@@ -111,34 +110,4 @@ describe('buildOnboardingFolderAgentStartup', () => {
|
||||
)
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('builds the skipped-onboarding folder startup from the persisted default agent', () => {
|
||||
expect(
|
||||
buildDismissedOnboardingFolderAgentStartup(
|
||||
{
|
||||
...getDefaultSettings('/tmp/orca-workspaces'),
|
||||
defaultTuiAgent: 'codex',
|
||||
agentCmdOverrides: { codex: 'echo onboarding-folder-agent' }
|
||||
},
|
||||
{ ...getDefaultOnboardingState(), outcome: 'dismissed' },
|
||||
false,
|
||||
HOST
|
||||
)
|
||||
).toEqual({
|
||||
command: "echo onboarding-folder-agent '--dangerously-bypass-approvals-and-sandbox'",
|
||||
env: {},
|
||||
launchAgent: 'codex',
|
||||
launchConfig: {
|
||||
agentCommand: "echo onboarding-folder-agent '--dangerously-bypass-approvals-and-sandbox'",
|
||||
agentArgs: '--dangerously-bypass-approvals-and-sandbox',
|
||||
agentEnv: {}
|
||||
},
|
||||
sessionOptions: undefined,
|
||||
telemetry: {
|
||||
agent_kind: 'codex',
|
||||
launch_source: 'onboarding',
|
||||
request_kind: 'new'
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -95,7 +95,7 @@ const NonGitFolderDialog = React.memo(function NonGitFolderDialog() {
|
||||
})
|
||||
const state = useAppStore.getState()
|
||||
const hadProjectBeforeAdd = stateBeforeAdd.repos.length > 0
|
||||
await markOnboardingProjectAdded('addedFolder')
|
||||
const onboardingBeforeAdd = await markOnboardingProjectAdded('addedFolder')
|
||||
await state.fetchWorktrees(repo.id, ownerOptions)
|
||||
// Why: mirror the local non-git folder flow — without this the
|
||||
// dialog closes and the UI shows no visible change, making the
|
||||
@@ -105,13 +105,12 @@ const NonGitFolderDialog = React.memo(function NonGitFolderDialog() {
|
||||
(worktree) => worktree.hostId === ownerOptions.executionHostId
|
||||
)
|
||||
if (folderWorktree) {
|
||||
const onboarding = await window.api.onboarding.get().catch(() => null)
|
||||
// Why: SSH users can hit this dialog from Add Project after
|
||||
// dismissing onboarding, bypassing the local addNonGitFolder path.
|
||||
const launch = resolveDismissedOnboardingFolderAgentLaunch({
|
||||
worktreeId: folderWorktree.id,
|
||||
store: useAppStore.getState(),
|
||||
onboarding,
|
||||
onboarding: onboardingBeforeAdd,
|
||||
hasExistingProject: hadProjectBeforeAdd,
|
||||
executionHostId: ownerOptions.executionHostId ?? connectionId
|
||||
})
|
||||
|
||||
@@ -84,15 +84,3 @@ export function shouldSeedFolderAgentAfterDismissedOnboarding(
|
||||
!onboarding.checklist.addedFolder
|
||||
)
|
||||
}
|
||||
|
||||
export function buildDismissedOnboardingFolderAgentStartup(
|
||||
settings: GlobalSettings | null,
|
||||
onboarding: OnboardingState | null,
|
||||
hasExistingProject: boolean,
|
||||
host: OnboardingStartupHost
|
||||
): OnboardingFolderAgentStartup | undefined {
|
||||
if (!shouldSeedFolderAgentAfterDismissedOnboarding(onboarding, hasExistingProject)) {
|
||||
return undefined
|
||||
}
|
||||
return buildOnboardingFolderAgentStartup(settings, host)
|
||||
}
|
||||
|
||||
@@ -3,15 +3,16 @@ import type { OnboardingState } from '../../../shared/onboarding-state-types'
|
||||
|
||||
export type OnboardingProjectChecklistItem = 'addedRepo' | 'addedFolder'
|
||||
|
||||
/** Returns the onboarding state read before marking, so callers can tell a first add from a later one. */
|
||||
export async function markOnboardingProjectAdded(
|
||||
item: OnboardingProjectChecklistItem
|
||||
): Promise<void> {
|
||||
): Promise<OnboardingState | null> {
|
||||
if (typeof window === 'undefined' || !window.api?.onboarding) {
|
||||
return
|
||||
return null
|
||||
}
|
||||
const onboarding = await window.api.onboarding.get().catch(() => null)
|
||||
if (!onboarding || onboarding.checklist[item]) {
|
||||
return
|
||||
return onboarding
|
||||
}
|
||||
|
||||
const checklist: Partial<OnboardingState['checklist']> = {}
|
||||
@@ -20,11 +21,12 @@ export async function markOnboardingProjectAdded(
|
||||
await window.api.onboarding.update({ checklist })
|
||||
} catch (err) {
|
||||
console.warn('[onboarding] Failed to update project checklist item:', err)
|
||||
return
|
||||
return onboarding
|
||||
}
|
||||
|
||||
track('activation_checklist_item_completed', {
|
||||
item,
|
||||
time_since_completed_ms: 0
|
||||
})
|
||||
return onboarding
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ export function createRepoAddActions(
|
||||
if (!repo) {
|
||||
return null
|
||||
}
|
||||
await markOnboardingProjectAdded('addedFolder')
|
||||
const onboardingBeforeAdd = await markOnboardingProjectAdded('addedFolder')
|
||||
// Why: focus the new folder so the add is visible; lazy-import worktree-activation to avoid a circular module load (it imports the store root).
|
||||
const executionHostId =
|
||||
options?.runtimeEnvironmentId === undefined
|
||||
@@ -186,7 +186,6 @@ export function createRepoAddActions(
|
||||
(worktree) => executionHostId === undefined || worktree.hostId === executionHostId
|
||||
)
|
||||
if (folderWorktree) {
|
||||
const onboarding = await window.api.onboarding.get().catch(() => null)
|
||||
// Why: lazy-import to avoid a circular module load (the launch graph imports the store root).
|
||||
const {
|
||||
resolveDismissedOnboardingFolderAgentLaunch,
|
||||
@@ -196,7 +195,7 @@ export function createRepoAddActions(
|
||||
const launch = resolveDismissedOnboardingFolderAgentLaunch({
|
||||
worktreeId: folderWorktree.id,
|
||||
store: get(),
|
||||
onboarding,
|
||||
onboarding: onboardingBeforeAdd,
|
||||
hasExistingProject: hadProjectBeforeAdd,
|
||||
executionHostId: executionHostId ?? LOCAL_EXECUTION_HOST_ID
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { getDefaultSettings } from '../../../../shared/constants'
|
||||
import { getDefaultOnboardingState } from '../../../../shared/onboarding-defaults'
|
||||
import type { OnboardingState } from '../../../../shared/onboarding-state-types'
|
||||
import { useAppStore } from '@/store'
|
||||
import {
|
||||
resolveDismissedOnboardingFolderAgentLaunch,
|
||||
@@ -30,17 +31,19 @@ afterEach(() => vi.restoreAllMocks())
|
||||
const reposAdd = vi.fn()
|
||||
const worktreesList = vi.fn()
|
||||
const onboardingGet = vi.fn()
|
||||
const onboardingUpdate = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
reposAdd.mockReset()
|
||||
worktreesList.mockReset()
|
||||
onboardingGet.mockReset()
|
||||
onboardingUpdate.mockReset()
|
||||
worktreeActivation.activateAndRevealWorktree.mockReset()
|
||||
vi.stubGlobal('window', {
|
||||
api: {
|
||||
repos: { add: reposAdd },
|
||||
worktrees: { list: worktreesList },
|
||||
onboarding: { get: onboardingGet }
|
||||
onboarding: { get: onboardingGet, update: onboardingUpdate }
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -57,7 +60,14 @@ describe('repo slice skipped-onboarding folder startup', () => {
|
||||
worktreesList.mockImplementation(({ repoId }: { repoId: string }) => [
|
||||
makeWorktree({ id: `${repoId}::/folder`, repoId })
|
||||
])
|
||||
onboardingGet.mockResolvedValue({ ...getDefaultOnboardingState(), outcome: 'dismissed' })
|
||||
let onboarding: OnboardingState = { ...getDefaultOnboardingState(), outcome: 'dismissed' }
|
||||
onboardingGet.mockImplementation(async () => onboarding)
|
||||
onboardingUpdate.mockImplementation(
|
||||
async ({ checklist }: { checklist: Partial<OnboardingState['checklist']> }) => {
|
||||
onboarding = { ...onboarding, checklist: { ...onboarding.checklist, ...checklist } }
|
||||
return onboarding
|
||||
}
|
||||
)
|
||||
const store = createTestStore()
|
||||
store.setState({
|
||||
settings: {
|
||||
|
||||
Reference in New Issue
Block a user