chore: remove IS_AI_ENABLED feature flag (#19916)
## Summary - AI is now GA, so the public/lab `IS_AI_ENABLED` flag is removed from `FeatureFlagKey`, the public flag catalog, and the dev seeder. - Drops every backend `@RequireFeatureFlag(IS_AI_ENABLED)` guard (agent, agent chat, chat subscription, role-to-agent assignment, workflow AI step creation) and the now-unused `FeatureFlagModule`/`FeatureFlagGuard` wiring in the AI and workflow modules. - Removes frontend gating from settings nav, role permissions/assignment/applicability, command menu hotkeys, side panel, mobile/drawer nav, and the agent chat provider so AI UI is always on. Tests and generated GraphQL/SDK schemas updated accordingly. ## Test plan - [x] `npx nx typecheck twenty-shared` - [x] `npx nx typecheck twenty-server` - [x] `npx nx typecheck twenty-front` - [x] `npx nx lint:diff-with-main twenty-server` - [x] `npx nx lint:diff-with-main twenty-front` - [x] `npx jest --config=packages/twenty-server/jest.config.mjs feature-flag` - [x] `npx jest --config=packages/twenty-server/jest.config.mjs workspace-entity-manager` - [ ] Manual smoke test: AI features still accessible without any flag row in `featureFlag` 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,6 @@ import {
|
||||
type NavigationDrawerItemModifier,
|
||||
} from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import {
|
||||
@@ -39,10 +38,7 @@ import {
|
||||
IconUsers,
|
||||
IconWorld,
|
||||
} from 'twenty-ui/display';
|
||||
import {
|
||||
FeatureFlagKey,
|
||||
PermissionFlagType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { PermissionFlagType } from '~/generated-metadata/graphql';
|
||||
|
||||
export type SettingsNavigationSection = {
|
||||
label: string;
|
||||
@@ -74,7 +70,6 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
|
||||
const isAdminEnabled =
|
||||
(currentUser?.canImpersonate || currentUser?.canAccessFullAdminPanel) ??
|
||||
false;
|
||||
const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
|
||||
const isSupportChatConfigured =
|
||||
supportChat?.supportDriver === 'FRONT' &&
|
||||
isNonEmptyString(supportChat.supportFrontChatId);
|
||||
@@ -182,8 +177,7 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
|
||||
label: t`AI`,
|
||||
path: SettingsPath.AI,
|
||||
Icon: IconSparkles,
|
||||
isHidden:
|
||||
!isAiEnabled || !permissionMap[PermissionFlagType.WORKSPACE],
|
||||
isHidden: !permissionMap[PermissionFlagType.WORKSPACE],
|
||||
modifier: 'new',
|
||||
},
|
||||
{
|
||||
|
||||
+20
-29
@@ -13,14 +13,12 @@ import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState';
|
||||
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useState } from 'react';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { SettingsPath } from 'twenty-shared/types';
|
||||
import { useQuery } from '@apollo/client/react';
|
||||
import {
|
||||
type Agent,
|
||||
FeatureFlagKey,
|
||||
type ApiKeyForRole,
|
||||
FindManyAgentsDocument,
|
||||
GetApiKeysDocument,
|
||||
@@ -40,8 +38,6 @@ export const SettingsRoleAssignment = ({
|
||||
roleId,
|
||||
isCreateMode,
|
||||
}: SettingsRoleAssignmentProps) => {
|
||||
const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
|
||||
|
||||
const settingsDraftRole = useAtomFamilyStateValue(
|
||||
settingsDraftRoleFamilyState,
|
||||
roleId,
|
||||
@@ -59,9 +55,7 @@ export const SettingsRoleAssignment = ({
|
||||
const { addApiKeyToRoleAndUpdateState, updateApiKeyRoleDraftState } =
|
||||
useUpdateApiKeyRole(roleId);
|
||||
|
||||
const { data: agentsData } = useQuery(FindManyAgentsDocument, {
|
||||
skip: !isAiEnabled,
|
||||
});
|
||||
const { data: agentsData } = useQuery(FindManyAgentsDocument);
|
||||
const { data: apiKeysData } = useQuery(GetApiKeysDocument);
|
||||
|
||||
const { openModal, closeModal } = useModal();
|
||||
@@ -204,28 +198,25 @@ export const SettingsRoleAssignment = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{Object.keys(ROLE_TARGET_CONFIG).map(
|
||||
(roleTargetType) =>
|
||||
(isAiEnabled || roleTargetType !== 'agent') && (
|
||||
<RoleAssignmentSection
|
||||
key={roleTargetType}
|
||||
roleTargetType={roleTargetType as keyof typeof ROLE_TARGET_CONFIG}
|
||||
roleId={roleId}
|
||||
settingsDraftRole={settingsDraftRole}
|
||||
currentWorkspaceMember={
|
||||
roleTargetType === 'member'
|
||||
? currentWorkspaceMember || undefined
|
||||
: undefined
|
||||
}
|
||||
onSelect={handleSelectEntity}
|
||||
allWorkspaceMembersHaveThisRole={
|
||||
roleTargetType === 'member'
|
||||
? allWorkspaceMembersHaveThisRole
|
||||
: false
|
||||
}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
{Object.keys(ROLE_TARGET_CONFIG).map((roleTargetType) => (
|
||||
<RoleAssignmentSection
|
||||
key={roleTargetType}
|
||||
roleTargetType={roleTargetType as keyof typeof ROLE_TARGET_CONFIG}
|
||||
roleId={roleId}
|
||||
settingsDraftRole={settingsDraftRole}
|
||||
currentWorkspaceMember={
|
||||
roleTargetType === 'member'
|
||||
? currentWorkspaceMember || undefined
|
||||
: undefined
|
||||
}
|
||||
onSelect={handleSelectEntity}
|
||||
allWorkspaceMembersHaveThisRole={
|
||||
roleTargetType === 'member'
|
||||
? allWorkspaceMembersHaveThisRole
|
||||
: false
|
||||
}
|
||||
/>
|
||||
))}
|
||||
|
||||
{selectedRoleTarget && (
|
||||
<SettingsRoleAssignmentConfirmationModal
|
||||
|
||||
+1
-12
@@ -1,5 +1,4 @@
|
||||
import { type SettingsRolePermissionsSettingPermission } from '@/settings/roles/role-permissions/permission-flags/types/SettingsRolePermissionsSettingPermission';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
@@ -14,10 +13,7 @@ import {
|
||||
IconTable,
|
||||
IconUser,
|
||||
} from 'twenty-ui/display';
|
||||
import {
|
||||
FeatureFlagKey,
|
||||
PermissionFlagType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { PermissionFlagType } from '~/generated-metadata/graphql';
|
||||
|
||||
type UseActionRolePermissionFlagConfigParams = {
|
||||
assignmentCapabilities?: {
|
||||
@@ -30,8 +26,6 @@ type UseActionRolePermissionFlagConfigParams = {
|
||||
export const useActionRolePermissionFlagConfig = ({
|
||||
assignmentCapabilities,
|
||||
}: UseActionRolePermissionFlagConfigParams = {}): SettingsRolePermissionsSettingPermission[] => {
|
||||
const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
|
||||
|
||||
const {
|
||||
canBeAssignedToAgents = false,
|
||||
canBeAssignedToUsers = false,
|
||||
@@ -154,10 +148,6 @@ export const useActionRolePermissionFlagConfig = ({
|
||||
canBeAssignedToUsers && !canBeAssignedToAgents && !canBeAssignedToApiKeys;
|
||||
|
||||
return allPermissions.filter((permission) => {
|
||||
if (permission.key === PermissionFlagType.AI && !isAiEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasAssignmentCapabilities) {
|
||||
if (canBeAssignedOnlyToAgents && !permission.isRelevantForAgents) {
|
||||
return false;
|
||||
@@ -179,6 +169,5 @@ export const useActionRolePermissionFlagConfig = ({
|
||||
canBeAssignedToAgents,
|
||||
canBeAssignedToUsers,
|
||||
canBeAssignedToApiKeys,
|
||||
isAiEnabled,
|
||||
]);
|
||||
};
|
||||
|
||||
+1
-11
@@ -1,5 +1,4 @@
|
||||
import { type SettingsRolePermissionsSettingPermission } from '@/settings/roles/role-permissions/permission-flags/types/SettingsRolePermissionsSettingPermission';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
@@ -17,10 +16,7 @@ import {
|
||||
IconSpy,
|
||||
IconUsers,
|
||||
} from 'twenty-ui/display';
|
||||
import {
|
||||
FeatureFlagKey,
|
||||
PermissionFlagType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { PermissionFlagType } from '~/generated-metadata/graphql';
|
||||
|
||||
type UseSettingsRolePermissionFlagConfigParams = {
|
||||
assignmentCapabilities?: {
|
||||
@@ -33,8 +29,6 @@ type UseSettingsRolePermissionFlagConfigParams = {
|
||||
export const useSettingsRolePermissionFlagConfig = ({
|
||||
assignmentCapabilities,
|
||||
}: UseSettingsRolePermissionFlagConfigParams = {}): SettingsRolePermissionsSettingPermission[] => {
|
||||
const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
|
||||
|
||||
const {
|
||||
canBeAssignedToAgents = false,
|
||||
canBeAssignedToUsers = false,
|
||||
@@ -174,9 +168,6 @@ export const useSettingsRolePermissionFlagConfig = ({
|
||||
canBeAssignedToUsers && !canBeAssignedToAgents && !canBeAssignedToApiKeys;
|
||||
|
||||
return allPermissions.filter((permission) => {
|
||||
if (permission.key === PermissionFlagType.AI_SETTINGS && !isAiEnabled) {
|
||||
return false;
|
||||
}
|
||||
if (hasAssignmentCapabilities) {
|
||||
if (canBeAssignedOnlyToAgents && !permission.isRelevantForAgents) {
|
||||
return false;
|
||||
@@ -198,6 +189,5 @@ export const useSettingsRolePermissionFlagConfig = ({
|
||||
canBeAssignedToAgents,
|
||||
canBeAssignedToUsers,
|
||||
canBeAssignedToApiKeys,
|
||||
isAiEnabled,
|
||||
]);
|
||||
};
|
||||
|
||||
+5
-12
@@ -1,4 +1,3 @@
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
@@ -7,7 +6,6 @@ import { Checkbox } from 'twenty-ui/input';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { useContext } from 'react';
|
||||
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledCheckboxContainer = styled.div<{ disabled: boolean }>`
|
||||
align-items: center;
|
||||
@@ -51,7 +49,6 @@ export const SettingsRoleApplicability = ({
|
||||
isEditable,
|
||||
}: SettingsRoleApplicabilityProps) => {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
|
||||
|
||||
const options = [
|
||||
{
|
||||
@@ -64,15 +61,11 @@ export const SettingsRoleApplicability = ({
|
||||
label: t`Assignable to Agents`,
|
||||
Icon: IconRobot,
|
||||
},
|
||||
...(isAiEnabled
|
||||
? [
|
||||
{
|
||||
key: 'canBeAssignedToApiKeys' as const,
|
||||
label: t`Assignable to API Keys`,
|
||||
Icon: IconKey,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
key: 'canBeAssignedToApiKeys' as const,
|
||||
label: t`Assignable to API Keys`,
|
||||
Icon: IconKey,
|
||||
},
|
||||
];
|
||||
return (
|
||||
<Section>
|
||||
|
||||
Reference in New Issue
Block a user