diff --git a/static/app/views/settings/seer/seerAgentHooks.spec.tsx b/static/app/views/settings/seer/seerAgentHooks.spec.tsx
index 5a34dbf3596a6c..82691b24dd6765 100644
--- a/static/app/views/settings/seer/seerAgentHooks.spec.tsx
+++ b/static/app/views/settings/seer/seerAgentHooks.spec.tsx
@@ -13,7 +13,6 @@ import type {CodingAgentIntegration} from 'sentry/components/events/autofix/useA
import {ProjectsStore} from 'sentry/stores/projectsStore';
import {useQueryClient} from 'sentry/utils/queryClient';
import {
- useAgentOptions,
useBulkMutateCreatePr,
useMutateCreatePr,
useMutateSelectedAgent,
@@ -35,65 +34,31 @@ describe('seerAgentHooks', () => {
ProjectsStore.reset();
});
- describe('useAgentOptions', () => {
- it('returns Seer, integration options', () => {
- const integrations: CodingAgentIntegration[] = [
- {id: '42', name: 'Cursor', provider: 'cursor'},
- ];
-
- const {result} = renderHookWithProviders(useAgentOptions, {
- initialProps: {integrations},
- organization,
- });
-
- const options = result.current;
- expect(options).toHaveLength(3);
- expect(options[0]).toEqual({value: 'seer', label: expect.any(String)});
- expect(options[1]).toMatchObject({
- value: {id: '42', name: 'Cursor', provider: 'cursor'},
- label: 'Cursor',
- });
- expect(options[2]).toEqual({value: 'none', label: 'No Handoff'});
- });
-
- it('filters out integrations without id', () => {
- const integrations: CodingAgentIntegration[] = [
- {id: null, name: 'No Id', provider: 'other'},
- {id: '1', name: 'With Id', provider: 'cursor'},
- ];
-
- const {result} = renderHookWithProviders(useAgentOptions, {
- initialProps: {integrations},
- organization,
- });
-
- const options = result.current;
- expect(options).toHaveLength(3);
- expect(options[1]!.value).toMatchObject({id: '1', name: 'With Id'});
- });
- });
-
describe('useSelectedAgentFromProjectSettings', () => {
- it('returns "none" when project autofixAutomationTuning is off', () => {
- const p = ProjectFixture({...project, autofixAutomationTuning: 'off'});
-
+ it('returns "seer" when no automation_handoff integration_id', () => {
const {result} = renderHookWithProviders(useSelectedAgentFromProjectSettings, {
initialProps: {
- preference: {repositories: []},
- project: p,
+ preference: {
+ repositories: [],
+ },
integrations: [],
},
organization,
});
- expect(result.current).toBe('none');
+ expect(result.current).toBe('seer');
});
- it('returns "seer" when no automation_handoff integration_id', () => {
+ it('returns "seer" when no automation_handoff integration_id, ignoring autofixAutomationTuning', () => {
const {result} = renderHookWithProviders(useSelectedAgentFromProjectSettings, {
initialProps: {
- preference: {repositories: []},
- project,
+ preference: {
+ projectId: '1',
+ autofixAutomationTuning: 'off',
+ automatedRunStoppingPoint: 'code_changes',
+ automation_handoff: undefined,
+ repositories: [],
+ },
integrations: [],
},
organization,
@@ -117,7 +82,6 @@ describe('seerAgentHooks', () => {
integration_id: 99,
},
},
- project,
integrations,
},
organization,
@@ -128,7 +92,7 @@ describe('seerAgentHooks', () => {
});
describe('useSelectedAgentFromBulkSettings', () => {
- it('returns "none" when autofixAutomationTuning is off', () => {
+ it('returns "seer" when automationHandoff undefined, doesnt look at autofixAutomationTuning', () => {
const {result} = renderHookWithProviders(useSelectedAgentFromBulkSettings, {
initialProps: {
autofixSettings: {
@@ -143,7 +107,7 @@ describe('seerAgentHooks', () => {
organization,
});
- expect(result.current).toBe('none');
+ expect(result.current).toBe('seer');
});
it('returns "seer" when no automationHandoff integration_id', () => {
diff --git a/static/app/views/settings/seer/seerAgentHooks.tsx b/static/app/views/settings/seer/seerAgentHooks.tsx
index 770ed07b8e3f24..962b7b52941adf 100644
--- a/static/app/views/settings/seer/seerAgentHooks.tsx
+++ b/static/app/views/settings/seer/seerAgentHooks.tsx
@@ -23,39 +23,14 @@ import {fetchDataQuery, fetchMutation, useQueryClient} from 'sentry/utils/queryC
import {RequestError} from 'sentry/utils/requestError/requestError';
import {useOrganization} from 'sentry/utils/useOrganization';
-export function useAgentOptions({
- integrations,
-}: {
- integrations: CodingAgentIntegration[];
-}) {
- return useMemo(() => {
- return [
- {value: 'seer' as const, label: t('Seer Agent')},
- ...integrations
- .filter(integration => integration.id)
- .map(integration => ({
- value: integration,
- label: integration.name,
- })),
- {value: 'none' as const, label: t('No Handoff')},
- ];
- }, [integrations]);
-}
-
export function useSelectedAgentFromProjectSettings({
integrations,
preference,
- project,
}: {
integrations: CodingAgentIntegration[];
preference: ProjectSeerPreferences;
- project: Project;
}) {
return useMemo(() => {
- // If we have autofixAutomationTuning==OFF then 'none' is picked
- if (project.autofixAutomationTuning === 'off') {
- return 'none';
- }
// If we have nothing in preferences, then we have Seer
if (!preference?.automation_handoff?.integration_id) {
return 'seer';
@@ -65,11 +40,7 @@ export function useSelectedAgentFromProjectSettings({
integration =>
integration.id === String(preference.automation_handoff?.integration_id)
);
- }, [
- preference?.automation_handoff?.integration_id,
- project.autofixAutomationTuning,
- integrations,
- ]);
+ }, [preference.automation_handoff?.integration_id, integrations]);
}
export function useSelectedAgentFromBulkSettings({
@@ -80,10 +51,6 @@ export function useSelectedAgentFromBulkSettings({
integrations: CodingAgentIntegration[];
}) {
return useMemo(() => {
- // If we have autofixAutomationTuning==OFF then 'none' is picked
- if (autofixSettings.autofixAutomationTuning === 'off') {
- return 'none';
- }
// If we have nothing in preferences, then we have Seer
if (!autofixSettings?.automationHandoff?.integration_id) {
return 'seer';
@@ -93,11 +60,7 @@ export function useSelectedAgentFromBulkSettings({
integration =>
integration.id === String(autofixSettings.automationHandoff?.integration_id)
);
- }, [
- autofixSettings.automationHandoff?.integration_id,
- autofixSettings.autofixAutomationTuning,
- integrations,
- ]);
+ }, [autofixSettings.automationHandoff?.integration_id, integrations]);
}
function useApplyOptimisticUpdate({project}: {project: Project}) {
diff --git a/static/gsApp/views/seerAutomation/components/projectDetails/autofixAgent.tsx b/static/gsApp/views/seerAutomation/components/projectDetails/autofixAgent.tsx
index aae2ff52378e62..958b32849a1050 100644
--- a/static/gsApp/views/seerAutomation/components/projectDetails/autofixAgent.tsx
+++ b/static/gsApp/views/seerAutomation/components/projectDetails/autofixAgent.tsx
@@ -20,13 +20,13 @@ import {t, tct} from 'sentry/locale';
import type {Project} from 'sentry/types/project';
import {useMutation, useQuery, useQueryClient} from 'sentry/utils/queryClient';
import {useOrganization} from 'sentry/utils/useOrganization';
+import {useFetchAgentOptions} from 'sentry/views/settings/seer/overview/utils/seerPreferredAgent';
import {
getProjectStoppingPointMutationOptions,
getProjectStoppingPointValue,
useFetchStoppingPointOptions,
} from 'sentry/views/settings/seer/overview/utils/seerStoppingPoint';
import {
- useAgentOptions,
useMutateSelectedAgent,
useSelectedAgentFromProjectSettings,
} from 'sentry/views/settings/seer/seerAgentHooks';
@@ -44,14 +44,11 @@ function AgentSpecificFields({
integration,
...props
}: Props & {
- integration: 'seer' | 'none' | CodingAgentIntegration;
+ integration: 'seer' | CodingAgentIntegration;
}) {
if (integration === 'seer') {
return ;
}
- if (integration === 'none') {
- return null;
- }
if (integration.provider === 'cursor' || integration.provider === 'claude_code') {
return ;
}
@@ -68,10 +65,9 @@ export function AutofixAgent({canWrite, preference, project}: Props) {
...organizationIntegrationsCodingAgents(organization),
select: data => data.json.integrations ?? [],
});
- const options = useAgentOptions({integrations: integrations ?? []});
+ const options = useFetchAgentOptions({organization});
const selected = useSelectedAgentFromProjectSettings({
preference,
- project,
integrations: integrations ?? [],
});
const mutateSelectedAgent = useMutateSelectedAgent({project});
@@ -106,37 +102,29 @@ export function AutofixAgent({canWrite, preference, project}: Props) {
),
}
)}
- options={options}
+ options={options.data ?? []}
value={selected}
- onChange={(integration: 'seer' | 'none' | CodingAgentIntegration) => {
+ onChange={(integration: 'seer' | CodingAgentIntegration) => {
mutateSelectedAgent(integration, {
onSuccess: () =>
addSuccessMessage(
- integration === 'none'
- ? t('Removed coding agent')
- : tct('Started using [name] as coding agent', {
- name: (
-
- {integration === 'seer'
- ? t('Seer Agent')
- : integration.name}
-
- ),
- })
+ tct('Started using [name] as coding agent', {
+ name: (
+
+ {integration === 'seer' ? t('Seer Agent') : integration.name}
+
+ ),
+ })
),
onError: () =>
addErrorMessage(
- integration === 'none'
- ? t('Failed to update coding agent')
- : tct('Failed to set [name] as coding agent', {
- name: (
-
- {integration === 'seer'
- ? t('Seer Agent')
- : integration.name}
-
- ),
- })
+ tct('Failed to set [name] as coding agent', {
+ name: (
+
+ {integration === 'seer' ? t('Seer Agent') : integration.name}
+
+ ),
+ })
),
});
}}
@@ -150,7 +138,7 @@ export function AutofixAgent({canWrite, preference, project}: Props) {
/>
) : null}
- {selected && selected !== 'none' ? (
+ {selected ? (
{
const lowerCase = searchTerm?.toLowerCase() ?? '';
@@ -216,6 +224,7 @@ export function SeerProjectTable() {
integrations={integrations ?? []}
isPendingIntegrations={isPendingIntegrations}
project={project}
+ agentOptions={agentOptions}
/>
))
)}
diff --git a/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTableHeader.tsx b/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTableHeader.tsx
index b6db2925f8f820..8887ec8e89faa6 100644
--- a/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTableHeader.tsx
+++ b/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTableHeader.tsx
@@ -34,7 +34,7 @@ interface Props {
const COLUMNS = [
{title: t('Project'), key: 'project', sortKey: 'project'},
- {title: t('Autofix Handoff'), key: 'fixes'},
+ {title: t('Agent'), key: 'fixes', sortKey: 'agent'},
{
title: (
@@ -48,7 +48,7 @@ const COLUMNS = [
),
key: 'pr_creation',
},
- {title: t('Repos'), key: 'repos'},
+ {title: t('Repos'), key: 'repos', sortKey: 'repo_count'},
];
function getMutationCallbacks(count: number) {
diff --git a/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTableRow.tsx b/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTableRow.tsx
index 73c0b6404fd40c..47aca85c1a3b1b 100644
--- a/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTableRow.tsx
+++ b/static/gsApp/views/seerAutomation/components/projectTable/seerProjectTableRow.tsx
@@ -20,8 +20,8 @@ import {t, tct} from 'sentry/locale';
import type {Project} from 'sentry/types/project';
import {useListItemCheckboxContext} from 'sentry/utils/list/useListItemCheckboxState';
import {useOrganization} from 'sentry/utils/useOrganization';
+import type {useFetchAgentOptions} from 'sentry/views/settings/seer/overview/utils/seerPreferredAgent';
import {
- useAgentOptions,
useMutateCreatePr,
useMutateSelectedAgent,
useSelectedAgentFromBulkSettings,
@@ -30,6 +30,7 @@ import {
import {useCanWriteSettings} from 'getsentry/views/seerAutomation/components/useCanWriteSettings';
interface Props {
+ agentOptions: ReturnType;
autofixSettings: undefined | AutofixAutomationSettings;
integrations: CodingAgentIntegration[];
isPendingIntegrations: boolean;
@@ -37,16 +38,16 @@ interface Props {
}
export function SeerProjectTableRow({
+ agentOptions,
autofixSettings,
+ integrations,
isPendingIntegrations,
project,
- integrations,
}: Props) {
const organization = useOrganization();
const canWrite = useCanWriteSettings();
const {isSelected, toggleSelected} = useListItemCheckboxContext();
- const options = useAgentOptions({integrations: integrations ?? []});
const autofixAgent = useSelectedAgentFromBulkSettings({
autofixSettings: autofixSettings ?? {
autofixAutomationTuning: 'off',
@@ -94,28 +95,24 @@ export function SeerProjectTableRow({
size="xs"
disabled={!canWrite}
name="autofixAgent"
- options={options}
+ options={agentOptions.data ?? []}
value={autofixAgent}
- onChange={(option: ReturnType[number]) => {
+ onChange={option => {
mutateSelectedAgent(option.value, {
onSuccess: () => {
addSuccessMessage(
- option.value === 'none'
- ? t('Removed autofix agent from %s', project.name)
- : tct('Started using [name] for [project]', {
- name: {option.label},
- project: project.name,
- })
+ tct('Selected [name] for [project]', {
+ name: {option.label},
+ project: project.name,
+ })
);
},
onError: () =>
addErrorMessage(
- option.value === 'none'
- ? t('Failed to update autofix agent')
- : tct('Failed to set [name] for [project]', {
- name: {option.label},
- project: project.name,
- })
+ tct('Failed to set [name] for [project]', {
+ name: {option.label},
+ project: project.name,
+ })
),
});
}}
@@ -125,53 +122,49 @@ export function SeerProjectTableRow({
{autofixSettings ? (
- autofixAgent === 'none' ? (
- {'\u2014'}
- ) : (
-
- {organization.enableSeerCoding === false && autofixAgent === 'seer' ? (
-
- ),
- }
- )}
- size="xs"
- />
- ) : null}
-
- {
- const value = e.target.checked;
- mutateCreatePr(autofixAgent, value, {
- onSuccess: () =>
- addSuccessMessage(
- value
- ? t('Enabled auto create pull requests for %s', project.name)
- : t('Disabled auto create pull requests for %s', project.name)
- ),
- onError: () =>
- addErrorMessage(
- t(
- 'Failed to update auto create pull requests setting for %s',
- project.name
- )
- ),
- });
- }}
+
+ {organization.enableSeerCoding === false && autofixAgent === 'seer' ? (
+
+ ),
+ }
+ )}
+ size="xs"
/>
-
- )
+ ) : null}
+
+ {
+ const value = e.target.checked;
+ mutateCreatePr(autofixAgent, value, {
+ onSuccess: () =>
+ addSuccessMessage(
+ value
+ ? t('Enabled auto create pull requests for %s', project.name)
+ : t('Disabled auto create pull requests for %s', project.name)
+ ),
+ onError: () =>
+ addErrorMessage(
+ t(
+ 'Failed to update auto create pull requests setting for %s',
+ project.name
+ )
+ ),
+ });
+ }}
+ />
+
) : (
)}