mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 18:37:24 +00:00
fix(cloud): Orca Cloud picker keeps the local pick and surface; hook test
Review follow-up: Orca Cloud derives its agent without overwriting the user's pick (a local-only pick returns on the local host), drops the terminal-only surface from cloud picks, and tests the composer hook end to end.
This commit is contained in:
@@ -191,9 +191,12 @@ function QuickTabBody({
|
||||
entry?: AgentPickerEntry
|
||||
) => {
|
||||
setQuickAgentOverride(agent)
|
||||
setQuickAgentPick({ agent, surface, draft, entry, contextKey: observation.contextKey })
|
||||
// Orca Cloud is terminal-only; keeping its surface would pin the agent to a terminal locally.
|
||||
setQuickAgentPick(
|
||||
orcaCloud ? { agent } : { agent, surface, draft, entry, contextKey: observation.contextKey }
|
||||
)
|
||||
},
|
||||
[observation.contextKey]
|
||||
[observation.contextKey, orcaCloud]
|
||||
)
|
||||
|
||||
const handleCreate = useCallback(async (): Promise<void> => {
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { cleanup, renderHook } from '@testing-library/react'
|
||||
import { useState } from 'react'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { TuiAgent } from '../../../../shared/agent-launch/catalog/tui-agent'
|
||||
import type { GlobalSettings } from '../../../../shared/global-settings-types'
|
||||
import { ORCA_CLOUD_IMAGE_AGENTS } from '../../../../shared/hosted-sandbox/orca-cloud-image-agents'
|
||||
import type { EphemeralVmRecipeOption } from './new-workspace-composer-card-props'
|
||||
|
||||
vi.mock('@/store/index', () => ({ useAppStore: { getState: () => ({}) } }))
|
||||
vi.mock('@/lib/host-chat-program', () => ({ agentChoicePending: () => false }))
|
||||
// This desktop has aider and claude; the host offers exactly what it detected.
|
||||
vi.mock('@/lib/host-chat-program-acquisition', () => ({
|
||||
useAgentsOfferedForLaunch: (_workspace: unknown, detected: ReadonlySet<TuiAgent> | null) =>
|
||||
detected
|
||||
}))
|
||||
|
||||
import { useComposerQuickAgent } from './use-composer-quick-agent'
|
||||
|
||||
afterEach(cleanup)
|
||||
|
||||
const orcaCloudRecipe: EphemeralVmRecipeOption = {
|
||||
id: 'orca-cloud',
|
||||
name: 'Orca Cloud',
|
||||
create: 'orca-cloud',
|
||||
provider: 'orca-cloud'
|
||||
}
|
||||
const otherRecipe: EphemeralVmRecipeOption = { id: 'custom', name: 'Custom', create: './vm.sh' }
|
||||
|
||||
function renderComposerAgent(props: {
|
||||
recipe: EphemeralVmRecipeOption | null
|
||||
override?: TuiAgent | null
|
||||
settings?: Pick<GlobalSettings, 'defaultTuiAgent'>
|
||||
}) {
|
||||
return renderHook(
|
||||
({ recipe }: { recipe: EphemeralVmRecipeOption | null }) => {
|
||||
const [override, setOverride] = useState<TuiAgent | null | undefined>(props.override)
|
||||
return {
|
||||
override,
|
||||
...useComposerQuickAgent(
|
||||
{
|
||||
agentRows: null,
|
||||
detectedAgentIds: new Set<TuiAgent>(['aider', 'claude']),
|
||||
repoId: 'repo-1',
|
||||
selectedRepoIsGit: true,
|
||||
selectedRepoExecutionHostId: null,
|
||||
ephemeralVmRecipes: [orcaCloudRecipe, otherRecipe],
|
||||
selectedEphemeralVmRecipeId: recipe?.id ?? null
|
||||
},
|
||||
props.settings ?? null,
|
||||
override,
|
||||
setOverride
|
||||
)
|
||||
}
|
||||
},
|
||||
{ initialProps: { recipe: props.recipe } }
|
||||
)
|
||||
}
|
||||
|
||||
describe('useComposerQuickAgent', () => {
|
||||
it('offers only the image agents and an enabled terminal-only picker for Orca Cloud', () => {
|
||||
const { result } = renderComposerAgent({
|
||||
recipe: orcaCloudRecipe,
|
||||
settings: { defaultTuiAgent: 'aider' }
|
||||
})
|
||||
expect(result.current.orcaCloud).toBe(true)
|
||||
expect(result.current.quickAgent).toBe('claude')
|
||||
expect([...(result.current.chipAgentIds ?? [])]).toEqual([...ORCA_CLOUD_IMAGE_AGENTS])
|
||||
expect(result.current.orcaCloudModel?.state).toBe('settled')
|
||||
expect(result.current.orcaCloudModel?.rows.every((row) => row.kind === 'terminal')).toBe(true)
|
||||
})
|
||||
|
||||
it('never submits a local-only pick to Orca Cloud, and restores it on the local host', () => {
|
||||
const { result, rerender } = renderComposerAgent({ recipe: orcaCloudRecipe, override: 'aider' })
|
||||
expect(result.current.quickAgent).toBe('claude')
|
||||
// The pick itself is kept, so going back to this desktop restores it.
|
||||
expect(result.current.override).toBe('aider')
|
||||
rerender({ recipe: null })
|
||||
expect(result.current.orcaCloud).toBe(false)
|
||||
expect(result.current.quickAgent).toBe('aider')
|
||||
})
|
||||
|
||||
it('keeps a default the image ships', () => {
|
||||
const { result } = renderComposerAgent({
|
||||
recipe: orcaCloudRecipe,
|
||||
settings: { defaultTuiAgent: 'gemini' }
|
||||
})
|
||||
expect(result.current.quickAgent).toBe('gemini')
|
||||
})
|
||||
|
||||
it('leaves other VM recipes on this host inventory', () => {
|
||||
const { result } = renderComposerAgent({
|
||||
recipe: otherRecipe,
|
||||
settings: { defaultTuiAgent: 'aider' }
|
||||
})
|
||||
expect(result.current.orcaCloud).toBe(false)
|
||||
expect(result.current.orcaCloudModel).toBeNull()
|
||||
expect(result.current.quickAgent).toBe('aider')
|
||||
expect([...(result.current.chipAgentIds ?? [])].sort()).toEqual(['aider', 'claude'])
|
||||
})
|
||||
})
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
pickOrcaCloudAgent
|
||||
} from '@/lib/orca-cloud/orca-cloud-agent-picker'
|
||||
import type { TuiAgent } from '../../../../shared/agent-launch/catalog/tui-agent'
|
||||
import type { GlobalSettings } from '../../../../shared/global-settings-types'
|
||||
import type { NewTabAgentMenuSettings } from '../../../../shared/agent-launch/picker/agent-launch-picker-rows'
|
||||
import { isOrcaCloudSelection } from './run-target-options'
|
||||
import type { NewWorkspaceComposerCardProps } from './new-workspace-composer-card-props'
|
||||
|
||||
@@ -31,7 +31,7 @@ type ComposerQuickAgentProps = Pick<
|
||||
/** The composer's agent: the user's pick when the launch host can start it, else the default. */
|
||||
export function useComposerQuickAgent(
|
||||
cardProps: ComposerQuickAgentProps,
|
||||
settings: GlobalSettings | null | undefined,
|
||||
settings: NewTabAgentMenuSettings | null | undefined,
|
||||
quickAgentOverride: TuiAgent | null | undefined,
|
||||
setQuickAgentOverride: (agent: TuiAgent | null | undefined) => void
|
||||
) {
|
||||
@@ -69,8 +69,9 @@ export function useComposerQuickAgent(
|
||||
selectedChoiceUnresolved: Boolean(quickAgentOverride && choicePending(quickAgentOverride)),
|
||||
disabledTuiAgents: settings?.disabledTuiAgents
|
||||
})
|
||||
if (resolved.quickAgentOverride !== quickAgentOverride) {
|
||||
// Repair invalid picks (e.g. a local-only agent once Orca Cloud is chosen) before children render it.
|
||||
// Repair invalid picks before children render them. Orca Cloud only derives its agent, so a
|
||||
// local-only pick returns when the user switches back to a host that has it.
|
||||
if (!orcaCloud && resolved.quickAgentOverride !== quickAgentOverride) {
|
||||
setQuickAgentOverride(resolved.quickAgentOverride)
|
||||
}
|
||||
const quickAgent = resolved.quickAgent
|
||||
|
||||
Reference in New Issue
Block a user