From 30b8663a7485aa634521a6f3d68feb4e27fc82c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Tue, 21 Apr 2026 09:49:46 +0200 Subject: [PATCH] chore: remove IS_AI_ENABLED feature flag (#19916) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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) --- .../src/metadata/generated/schema.graphql | 1 - .../src/metadata/generated/schema.ts | 3 +- .../src/generated-admin/graphql.ts | 1 - .../src/generated-metadata/graphql.ts | 1 - .../ai/components/AgentChatProvider.tsx | 10 +--- .../components/AgentChatProviderContent.tsx | 18 +++---- .../hooks/useCommandMenuHotKeys.ts | 10 +--- .../MainNavigationDrawerTabsRow.tsx | 7 --- .../components/MobileNavigationBar.tsx | 25 ++++------ .../hooks/useSettingsNavigationItems.tsx | 10 +--- .../components/SettingsRoleAssignment.tsx | 49 ++++++++----------- .../useActionRolePermissionFlagConfig.ts | 13 +---- .../useSettingsRolePermissionFlagConfig.ts | 12 +---- .../components/SettingsRoleApplicability.tsx | 17 ++----- .../SidePanelTopBarRightCornerIcon.tsx | 5 +- .../SidePanelWorkflowSelectAction.tsx | 22 +++------ .../feature-flag-gated-front-component.ts | 3 +- ...nditional-availability-expressions.test.ts | 4 +- .../constants/public-feature-flag.const.ts | 8 --- .../__tests__/feature-flag.service.spec.ts | 16 +++--- .../validates/feature-flag.validate.spec.ts | 2 +- .../workflow-version-step.resolver.ts | 17 ------- .../workflow/workflow-api.module.ts | 2 - .../ai/ai-agent/agent.resolver.ts | 16 +----- .../ai/ai-agent/ai-agent.module.ts | 2 - .../ai/ai-chat/ai-chat.module.ts | 5 -- .../agent-chat-subscription.resolver.ts | 3 -- .../ai-chat/resolvers/agent-chat.resolver.ts | 19 +------ .../metadata-modules/role/role.resolver.ts | 4 -- .../workspace-entity-manager.spec.ts | 2 - .../core/utils/seed-feature-flags.util.ts | 5 -- .../twenty-shared/src/types/FeatureFlagKey.ts | 1 - 32 files changed, 73 insertions(+), 240 deletions(-) diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql index e9fd2d2c78a..d2f59a3ab03 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql @@ -1618,7 +1618,6 @@ type FeatureFlag { enum FeatureFlagKey { IS_UNIQUE_INDEXES_ENABLED IS_JSON_FILTER_ENABLED - IS_AI_ENABLED IS_COMMAND_MENU_ITEM_ENABLED IS_MARKETPLACE_SETTING_TAB_VISIBLE IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts index cbe1bad3145..c7b92134bdf 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts @@ -1326,7 +1326,7 @@ export interface FeatureFlag { __typename: 'FeatureFlag' } -export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_AI_ENABLED' | 'IS_COMMAND_MENU_ITEM_ENABLED' | 'IS_MARKETPLACE_SETTING_TAB_VISIBLE' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_CONNECTED_ACCOUNT_MIGRATED' | 'IS_RICH_TEXT_V1_MIGRATED' | 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED' | 'IS_DATASOURCE_MIGRATED' +export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_COMMAND_MENU_ITEM_ENABLED' | 'IS_MARKETPLACE_SETTING_TAB_VISIBLE' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_CONNECTED_ACCOUNT_MIGRATED' | 'IS_RICH_TEXT_V1_MIGRATED' | 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED' | 'IS_DATASOURCE_MIGRATED' export interface WorkspaceUrls { customUrl?: Scalars['String'] @@ -8563,7 +8563,6 @@ export const enumLogicFunctionExecutionStatus = { export const enumFeatureFlagKey = { IS_UNIQUE_INDEXES_ENABLED: 'IS_UNIQUE_INDEXES_ENABLED' as const, IS_JSON_FILTER_ENABLED: 'IS_JSON_FILTER_ENABLED' as const, - IS_AI_ENABLED: 'IS_AI_ENABLED' as const, IS_COMMAND_MENU_ITEM_ENABLED: 'IS_COMMAND_MENU_ITEM_ENABLED' as const, IS_MARKETPLACE_SETTING_TAB_VISIBLE: 'IS_MARKETPLACE_SETTING_TAB_VISIBLE' as const, IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED: 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' as const, diff --git a/packages/twenty-front/src/generated-admin/graphql.ts b/packages/twenty-front/src/generated-admin/graphql.ts index 4228a0020d7..8645ee535f1 100644 --- a/packages/twenty-front/src/generated-admin/graphql.ts +++ b/packages/twenty-front/src/generated-admin/graphql.ts @@ -231,7 +231,6 @@ export type FeatureFlag = { }; export enum FeatureFlagKey { - IS_AI_ENABLED = 'IS_AI_ENABLED', IS_COMMAND_MENU_ITEM_ENABLED = 'IS_COMMAND_MENU_ITEM_ENABLED', IS_CONNECTED_ACCOUNT_MIGRATED = 'IS_CONNECTED_ACCOUNT_MIGRATED', IS_DATASOURCE_MIGRATED = 'IS_DATASOURCE_MIGRATED', diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index ae36a34dc22..1c9303b4c0c 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -1634,7 +1634,6 @@ export type FeatureFlag = { }; export enum FeatureFlagKey { - IS_AI_ENABLED = 'IS_AI_ENABLED', IS_COMMAND_MENU_ITEM_ENABLED = 'IS_COMMAND_MENU_ITEM_ENABLED', IS_CONNECTED_ACCOUNT_MIGRATED = 'IS_CONNECTED_ACCOUNT_MIGRATED', IS_DATASOURCE_MIGRATED = 'IS_DATASOURCE_MIGRATED', diff --git a/packages/twenty-front/src/modules/ai/components/AgentChatProvider.tsx b/packages/twenty-front/src/modules/ai/components/AgentChatProvider.tsx index 6b54afa2927..3cdc705b0c1 100644 --- a/packages/twenty-front/src/modules/ai/components/AgentChatProvider.tsx +++ b/packages/twenty-front/src/modules/ai/components/AgentChatProvider.tsx @@ -1,17 +1,9 @@ import { AgentChatProviderContent } from '@/ai/components/AgentChatProviderContent'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; -import { FeatureFlagKey } from '~/generated-metadata/graphql'; export const AgentChatProvider = ({ children, }: { children: React.ReactNode; }) => { - const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED); - - return ( - - {children} - - ); + return {children}; }; diff --git a/packages/twenty-front/src/modules/ai/components/AgentChatProviderContent.tsx b/packages/twenty-front/src/modules/ai/components/AgentChatProviderContent.tsx index 5f3f8edcfc6..df09313e271 100644 --- a/packages/twenty-front/src/modules/ai/components/AgentChatProviderContent.tsx +++ b/packages/twenty-front/src/modules/ai/components/AgentChatProviderContent.tsx @@ -10,26 +10,20 @@ import { Suspense } from 'react'; export const AgentChatProviderContent = ({ children, - isAiEnabled, }: { children: React.ReactNode; - isAiEnabled: boolean; }) => { return ( - {isAiEnabled && ( - <> - - - - - - - - )} + + + + + + {children} diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHotKeys.ts b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHotKeys.ts index 071668ae9ba..6ed3d30893f 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHotKeys.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHotKeys.ts @@ -9,11 +9,9 @@ import { sidePanelSearchState } from '@/side-panel/states/sidePanelSearchState'; import { useGlobalHotkeys } from '@/ui/utilities/hotkey/hooks/useGlobalHotkeys'; import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { isNonEmptyString } from '@sniptt/guards'; import { Key } from 'ts-key-enum'; import { SidePanelPages } from 'twenty-shared/types'; -import { FeatureFlagKey } from '~/generated-metadata/graphql'; export const useCommandMenuHotKeys = () => { const { toggleSidePanelMenu } = useSidePanelMenu(); @@ -31,8 +29,6 @@ export const useCommandMenuHotKeys = () => { const sidePanelPage = useAtomStateValue(sidePanelPageState); - const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED); - useGlobalHotkeys({ keys: ['ctrl+k', 'meta+k'], callback: () => { @@ -58,12 +54,10 @@ export const useCommandMenuHotKeys = () => { useGlobalHotkeys({ keys: ['@'], callback: () => { - if (isAiEnabled) { - openAskAiPage({ resetNavigationStack: true }); - } + openAskAiPage({ resetNavigationStack: true }); }, containsModifier: false, - dependencies: [openAskAiPage, isAiEnabled], + dependencies: [openAskAiPage], options: { ignoreModifiers: true, }, diff --git a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerTabsRow.tsx b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerTabsRow.tsx index f9d8c2056b5..0c53d35d0c4 100644 --- a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerTabsRow.tsx +++ b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerTabsRow.tsx @@ -22,8 +22,6 @@ import { import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; -import { FeatureFlagKey } from '~/generated-metadata/graphql'; const StyledRow = styled.div<{ isExpanded: boolean }>` align-items: center; @@ -141,15 +139,10 @@ export const MainNavigationDrawerTabsRow = () => { const [navigationDrawerActiveTab, setNavigationDrawerActiveTab] = useAtomState(navigationDrawerActiveTabState); const { switchToNewChat } = useSwitchToNewAiChat(); - const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED); const setIsNavigationDrawerExpanded = useSetAtomState( isNavigationDrawerExpandedState, ); - if (!isAiEnabled) { - return null; - } - const isExpanded = isNavigationDrawerExpanded || isMobile; const handleTabClick = (tab: NavigationDrawerActiveTab) => () => { diff --git a/packages/twenty-front/src/modules/navigation/components/MobileNavigationBar.tsx b/packages/twenty-front/src/modules/navigation/components/MobileNavigationBar.tsx index 2be908b6142..d07205b6ec5 100644 --- a/packages/twenty-front/src/modules/navigation/components/MobileNavigationBar.tsx +++ b/packages/twenty-front/src/modules/navigation/components/MobileNavigationBar.tsx @@ -12,7 +12,6 @@ import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNaviga import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState'; import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useNavigate } from 'react-router-dom'; import { type IconComponent, @@ -21,7 +20,6 @@ import { IconSearch, } from 'twenty-ui/display'; import { NavigationBar } from 'twenty-ui/navigation'; -import { FeatureFlagKey } from '~/generated-metadata/graphql'; type NavigationBarItemName = 'main' | 'search' | 'newAiChat'; @@ -37,7 +35,6 @@ export const MobileNavigationBar = () => { const [currentMobileNavigationDrawer, setCurrentMobileNavigationDrawer] = useAtomState(currentMobileNavigationDrawerState); const { switchToNewChat } = useSwitchToNewAiChat(); - const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED); const { alphaSortedActiveNonSystemObjectMetadataItems } = useFilteredObjectMetadataItems(); @@ -92,19 +89,15 @@ export const MobileNavigationBar = () => { openRecordsSearchPage(); }, }, - ...(isAiEnabled - ? [ - { - name: 'newAiChat' as const, - Icon: IconMessageCirclePlus, - onClick: () => { - setIsNavigationDrawerExpanded(false); - closeSidePanelMenu(); - switchToNewChat(); - }, - }, - ] - : []), + { + name: 'newAiChat' as const, + Icon: IconMessageCirclePlus, + onClick: () => { + setIsNavigationDrawerExpanded(false); + closeSidePanelMenu(); + switchToNewChat(); + }, + }, ]; return ; diff --git a/packages/twenty-front/src/modules/settings/hooks/useSettingsNavigationItems.tsx b/packages/twenty-front/src/modules/settings/hooks/useSettingsNavigationItems.tsx index 9cc8fd63379..8395b1256cc 100644 --- a/packages/twenty-front/src/modules/settings/hooks/useSettingsNavigationItems.tsx +++ b/packages/twenty-front/src/modules/settings/hooks/useSettingsNavigationItems.tsx @@ -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', }, { diff --git a/packages/twenty-front/src/modules/settings/roles/role-assignment/components/SettingsRoleAssignment.tsx b/packages/twenty-front/src/modules/settings/roles/role-assignment/components/SettingsRoleAssignment.tsx index c7afd4aebdf..ca6c90c95d9 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-assignment/components/SettingsRoleAssignment.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-assignment/components/SettingsRoleAssignment.tsx @@ -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') && ( - - ), - )} + {Object.keys(ROLE_TARGET_CONFIG).map((roleTargetType) => ( + + ))} {selectedRoleTarget && ( { - 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, ]); }; diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/permission-flags/hooks/useSettingsRolePermissionFlagConfig.ts b/packages/twenty-front/src/modules/settings/roles/role-permissions/permission-flags/hooks/useSettingsRolePermissionFlagConfig.ts index 10719ecca44..ce46ad9612a 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/permission-flags/hooks/useSettingsRolePermissionFlagConfig.ts +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/permission-flags/hooks/useSettingsRolePermissionFlagConfig.ts @@ -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, ]); }; diff --git a/packages/twenty-front/src/modules/settings/roles/role-settings/components/SettingsRoleApplicability.tsx b/packages/twenty-front/src/modules/settings/roles/role-settings/components/SettingsRoleApplicability.tsx index ac117fd602a..5feae59b60c 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-settings/components/SettingsRoleApplicability.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-settings/components/SettingsRoleApplicability.tsx @@ -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 (
diff --git a/packages/twenty-front/src/modules/side-panel/components/SidePanelTopBarRightCornerIcon.tsx b/packages/twenty-front/src/modules/side-panel/components/SidePanelTopBarRightCornerIcon.tsx index 4c71c5d199a..3c8d9e50d30 100644 --- a/packages/twenty-front/src/modules/side-panel/components/SidePanelTopBarRightCornerIcon.tsx +++ b/packages/twenty-front/src/modules/side-panel/components/SidePanelTopBarRightCornerIcon.tsx @@ -4,14 +4,12 @@ import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState'; import { sidePanelSearchObjectFilterState } from '@/side-panel/states/sidePanelSearchObjectFilterState'; import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { styled } from '@linaria/react'; import { t } from '@lingui/core/macro'; import { SidePanelPages } from 'twenty-shared/types'; import { IconEdit } from 'twenty-ui/display'; import { IconButton } from 'twenty-ui/input'; import { useIsMobile } from 'twenty-ui/utilities'; -import { FeatureFlagKey } from '~/generated-metadata/graphql'; import { themeCssVariables } from 'twenty-ui/theme-constants'; const StyledIconButtonContainer = styled.div` @@ -20,7 +18,6 @@ const StyledIconButtonContainer = styled.div` export const SidePanelTopBarRightCornerIcon = () => { const isMobile = useIsMobile(); - const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED); const sidePanelPage = useAtomStateValue(sidePanelPageState); const { switchToNewChat } = useSwitchToNewAiChat(); const [sidePanelSearchObjectFilter, setSidePanelSearchObjectFilter] = @@ -42,7 +39,7 @@ export const SidePanelTopBarRightCornerIcon = () => { SidePanelPages.ViewPreviousAiChats, ].includes(sidePanelPage); - if (isMobile || !isAiEnabled || !isOnAskAiPage) { + if (isMobile || !isOnAskAiPage) { return null; } diff --git a/packages/twenty-front/src/modules/side-panel/pages/workflow/action/components/SidePanelWorkflowSelectAction.tsx b/packages/twenty-front/src/modules/side-panel/pages/workflow/action/components/SidePanelWorkflowSelectAction.tsx index a349c33d288..699ed3cf9a3 100644 --- a/packages/twenty-front/src/modules/side-panel/pages/workflow/action/components/SidePanelWorkflowSelectAction.tsx +++ b/packages/twenty-front/src/modules/side-panel/pages/workflow/action/components/SidePanelWorkflowSelectAction.tsx @@ -10,11 +10,9 @@ import { FLOW_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constan import { HUMAN_INPUT_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/HumanInputActions'; import { RECORD_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/RecordActions'; import { getActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIconColorOrThrow'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useLingui } from '@lingui/react/macro'; import { IconFunction } from 'twenty-ui/display'; import { MenuItem } from 'twenty-ui/navigation'; -import { FeatureFlagKey } from '~/generated-metadata/graphql'; export type WorkflowActionSelection = { type: WorkflowActionType; @@ -26,8 +24,6 @@ export const SidePanelWorkflowSelectAction = ({ }: { onActionSelected: (selection: WorkflowActionSelection) => void; }) => { - const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED); - const { t } = useLingui(); const logicFunctions = useAtomStateValue(logicFunctionsSelector); @@ -57,17 +53,13 @@ export const SidePanelWorkflowSelectAction = ({ onClick={handleActionClick} /> - {isAiEnabled && ( - <> - - {t`AI`} - - - - )} + + {t`AI`} + + {t`Flow`} diff --git a/packages/twenty-sdk/src/cli/utilities/build/common/conditional-availability/__tests__/__mocks__/feature-flag-gated-front-component.ts b/packages/twenty-sdk/src/cli/utilities/build/common/conditional-availability/__tests__/__mocks__/feature-flag-gated-front-component.ts index 1d68702b8ec..a9d694c9803 100644 --- a/packages/twenty-sdk/src/cli/utilities/build/common/conditional-availability/__tests__/__mocks__/feature-flag-gated-front-component.ts +++ b/packages/twenty-sdk/src/cli/utilities/build/common/conditional-availability/__tests__/__mocks__/feature-flag-gated-front-component.ts @@ -10,6 +10,7 @@ export default defineFrontComponent({ universalIdentifier: 'feature-flag-gated-cmd', label: 'Feature Flag Gated', conditionalAvailabilityExpression: - featureFlags.IS_AI_ENABLED && objectPermissions.canReadObjectRecords, + featureFlags.IS_JUNCTION_RELATIONS_ENABLED && + objectPermissions.canReadObjectRecords, }, }); diff --git a/packages/twenty-sdk/src/cli/utilities/build/common/conditional-availability/__tests__/transform-conditional-availability-expressions.test.ts b/packages/twenty-sdk/src/cli/utilities/build/common/conditional-availability/__tests__/transform-conditional-availability-expressions.test.ts index 73a4adddef9..7b8c14b3f27 100644 --- a/packages/twenty-sdk/src/cli/utilities/build/common/conditional-availability/__tests__/transform-conditional-availability-expressions.test.ts +++ b/packages/twenty-sdk/src/cli/utilities/build/common/conditional-availability/__tests__/transform-conditional-availability-expressions.test.ts @@ -274,7 +274,7 @@ describe('transformConditionalAvailabilityExpressionsForEsBuildPlugin', () => { describe('feature-flag-gated-front-component', () => { it('should allow when feature flag is enabled', () => { const context = buildMockCommandMenuContextApi({ - featureFlags: { IS_AI_ENABLED: true }, + featureFlags: { IS_JUNCTION_RELATIONS_ENABLED: true }, }); expect( @@ -287,7 +287,7 @@ describe('transformConditionalAvailabilityExpressionsForEsBuildPlugin', () => { it('should deny when feature flag is disabled', () => { const context = buildMockCommandMenuContextApi({ - featureFlags: { IS_AI_ENABLED: false }, + featureFlags: { IS_JUNCTION_RELATIONS_ENABLED: false }, }); expect( diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/constants/public-feature-flag.const.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/constants/public-feature-flag.const.ts index 05f62a5a596..15855ae52df 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/constants/public-feature-flag.const.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/constants/public-feature-flag.const.ts @@ -12,14 +12,6 @@ export type PublicFeatureFlag = { }; export const PUBLIC_FEATURE_FLAGS: PublicFeatureFlag[] = [ - { - key: FeatureFlagKey.IS_AI_ENABLED, - metadata: { - label: 'AI', - description: 'Enable AI-powered features including the agent chat', - imagePath: 'https://twenty.com/images/lab/is-ai-enabled.png', - }, - }, { key: FeatureFlagKey.IS_JUNCTION_RELATIONS_ENABLED, metadata: { diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/services/__tests__/feature-flag.service.spec.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/services/__tests__/feature-flag.service.spec.ts index a2da4b35c29..775dc0ec61f 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/services/__tests__/feature-flag.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/services/__tests__/feature-flag.service.spec.ts @@ -37,7 +37,7 @@ describe('FeatureFlagService', () => { }; const workspaceId = 'workspace-id'; - const featureFlag = FeatureFlagKey.IS_AI_ENABLED; + const featureFlag = FeatureFlagKey.IS_JUNCTION_RELATIONS_ENABLED; beforeEach(async () => { jest.clearAllMocks(); @@ -121,11 +121,11 @@ describe('FeatureFlagService', () => { // Prepare mockWorkspaceCacheService.getOrRecompute.mockResolvedValue({ featureFlagsMap: { - [FeatureFlagKey.IS_AI_ENABLED]: false, + [FeatureFlagKey.IS_JUNCTION_RELATIONS_ENABLED]: false, }, }); const mockFeatureFlags = [ - { key: FeatureFlagKey.IS_AI_ENABLED, value: false }, + { key: FeatureFlagKey.IS_JUNCTION_RELATIONS_ENABLED, value: false }, ]; // Act @@ -144,7 +144,11 @@ describe('FeatureFlagService', () => { it('should return a map of feature flags for a workspace', async () => { // Prepare const mockFeatureFlags = [ - { key: FeatureFlagKey.IS_AI_ENABLED, value: false, workspaceId }, + { + key: FeatureFlagKey.IS_JUNCTION_RELATIONS_ENABLED, + value: false, + workspaceId, + }, ]; mockFeatureFlagRepository.find.mockResolvedValue(mockFeatureFlags); @@ -154,7 +158,7 @@ describe('FeatureFlagService', () => { // Assert expect(result).toEqual({ - [FeatureFlagKey.IS_AI_ENABLED]: false, + [FeatureFlagKey.IS_JUNCTION_RELATIONS_ENABLED]: false, }); }); }); @@ -162,7 +166,7 @@ describe('FeatureFlagService', () => { describe('enableFeatureFlags', () => { it('should enable multiple feature flags for a workspace', async () => { // Prepare - const keys = [FeatureFlagKey.IS_AI_ENABLED]; + const keys = [FeatureFlagKey.IS_JUNCTION_RELATIONS_ENABLED]; mockFeatureFlagRepository.upsert.mockResolvedValue({}); mockWorkspaceCacheService.invalidateAndRecompute.mockResolvedValue( diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/validates/feature-flag.validate.spec.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/validates/feature-flag.validate.spec.ts index 38740b15053..cf6523a6d60 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/validates/feature-flag.validate.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/validates/feature-flag.validate.spec.ts @@ -8,7 +8,7 @@ describe('featureFlagValidator', () => { it('should not throw error if featureFlagKey is valid', () => { expect(() => featureFlagValidator.assertIsFeatureFlagKey( - 'IS_AI_ENABLED', + 'IS_JUNCTION_RELATIONS_ENABLED', new UnknownException('Error', 'Error', { userFriendlyMessage: msg`Error`, }), diff --git a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts index 878521f0cd4..0f00efefc25 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts @@ -2,10 +2,8 @@ import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation } from '@nestjs/graphql'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { FeatureFlagKey } from 'twenty-shared/types'; import { CoreResolver } from 'src/engine/api/graphql/graphql-config/decorators/core-resolver.decorator'; -import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { HttpTool } from 'src/engine/core-modules/tool/tools/http-tool/http-tool'; @@ -27,7 +25,6 @@ import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter'; import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service'; -import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service'; import { WorkflowRunnerWorkspaceService } from 'src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service'; @@ -49,7 +46,6 @@ export class WorkflowVersionStepResolver { private readonly workflowRunnerWorkspaceService: WorkflowRunnerWorkspaceService, private readonly workflowRunWorkspaceService: WorkflowRunWorkspaceService, private readonly httpTool: HttpTool, - private readonly featureFlagService: FeatureFlagService, ) {} @Mutation(() => WorkflowVersionStepChangesDTO) @@ -58,19 +54,6 @@ export class WorkflowVersionStepResolver { @Args('input') input: CreateWorkflowVersionStepInput, ): Promise { - if (input.stepType === WorkflowActionType.AI_AGENT) { - const isAiEnabled = await this.featureFlagService.isFeatureEnabled( - FeatureFlagKey.IS_AI_ENABLED, - workspaceId, - ); - - if (!isAiEnabled) { - throw new Error( - 'AI features are not available in your current workspace. Please contact support to enable them.', - ); - } - } - return this.workflowVersionStepWorkspaceService.createWorkflowVersionStep({ workspaceId, input, diff --git a/packages/twenty-server/src/engine/core-modules/workflow/workflow-api.module.ts b/packages/twenty-server/src/engine/core-modules/workflow/workflow-api.module.ts index 1ce80b1da66..cded9b9b4f0 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/workflow-api.module.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/workflow-api.module.ts @@ -1,7 +1,6 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; import { ToolModule } from 'src/engine/core-modules/tool/tool.module'; import { WorkflowTriggerController } from 'src/engine/core-modules/workflow/controllers/workflow-trigger.controller'; import { WorkflowBuilderResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-builder.resolver'; @@ -24,7 +23,6 @@ import { WorkflowTriggerModule } from 'src/modules/workflow/workflow-trigger/wor @Module({ imports: [ TypeOrmModule.forFeature([WorkspaceEntity]), - FeatureFlagModule, WorkflowTriggerModule, WorkflowBuilderModule, WorkflowCommonModule, diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/agent.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/agent.resolver.ts index 805ad35a6cf..08e46fb2dac 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/agent.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/agent.resolver.ts @@ -3,15 +3,10 @@ import { Args, Mutation, Query } from '@nestjs/graphql'; import { PermissionFlagType } from 'twenty-shared/constants'; import { isNonEmptyString } from '@sniptt/guards'; -import { FeatureFlagKey } from 'twenty-shared/types'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; -import { - FeatureFlagGuard, - RequireFeatureFlag, -} from 'src/engine/guards/feature-flag.guard'; import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { fromFlatAgentWithRoleIdToAgentDto } from 'src/engine/metadata-modules/flat-agent/utils/from-agent-entity-to-agent-dto.util'; @@ -26,11 +21,7 @@ import { CreateAgentInput } from './dtos/create-agent.input'; import { UpdateAgentInput } from './dtos/update-agent.input'; import { AiGraphqlApiExceptionInterceptor } from 'src/engine/metadata-modules/ai/interceptors/ai-graphql-api-exception.interceptor'; -@UseGuards( - WorkspaceAuthGuard, - FeatureFlagGuard, - SettingsPermissionGuard(PermissionFlagType.AI), -) +@UseGuards(WorkspaceAuthGuard, SettingsPermissionGuard(PermissionFlagType.AI)) @UseInterceptors( WorkspaceMigrationGraphqlApiExceptionInterceptor, AiGraphqlApiExceptionInterceptor, @@ -43,7 +34,6 @@ export class AgentResolver { ) {} @Query(() => [AgentDTO]) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) async findManyAgents( @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, ): Promise { @@ -54,7 +44,6 @@ export class AgentResolver { } @Query(() => AgentDTO) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) async findOneAgent( @Args('input') { id }: AgentIdInput, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, @@ -68,7 +57,6 @@ export class AgentResolver { } @Mutation(() => AgentDTO) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) @UseGuards(SettingsPermissionGuard(PermissionFlagType.AI_SETTINGS)) async createOneAgent( @Args('input') input: CreateAgentInput, @@ -90,7 +78,6 @@ export class AgentResolver { } @Mutation(() => AgentDTO) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) @UseGuards(SettingsPermissionGuard(PermissionFlagType.AI_SETTINGS)) async updateOneAgent( @Args('input') input: UpdateAgentInput, @@ -112,7 +99,6 @@ export class AgentResolver { } @Mutation(() => AgentDTO) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) @UseGuards(SettingsPermissionGuard(PermissionFlagType.AI_SETTINGS)) async deleteOneAgent( @Args('input') { id }: AgentIdInput, diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/ai-agent.module.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/ai-agent.module.ts index f76aa214daa..37c938bddf4 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/ai-agent.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/ai-agent.module.ts @@ -3,7 +3,6 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { ApplicationModule } from 'src/engine/core-modules/application/application.module'; import { AuditModule } from 'src/engine/core-modules/audit/audit.module'; -import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; import { FileModule } from 'src/engine/core-modules/file/file.module'; import { ThrottlerModule } from 'src/engine/core-modules/throttler/throttler.module'; import { AiAgentRoleModule } from 'src/engine/metadata-modules/ai/ai-agent-role/ai-agent-role.module'; @@ -31,7 +30,6 @@ import { AgentEntity } from './entities/agent.entity'; AiAgentRoleModule, ThrottlerModule, AuditModule, - FeatureFlagModule, FileModule, ObjectMetadataModule, PermissionsModule, diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/ai-chat.module.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/ai-chat.module.ts index 590c3494306..d3ee6498c1f 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/ai-chat.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/ai-chat.module.ts @@ -12,7 +12,6 @@ import { PermissionFlagType } from 'twenty-shared/constants'; import { TokenModule } from 'src/engine/core-modules/auth/token/token.module'; import { BillingModule } from 'src/engine/core-modules/billing/billing.module'; import { WorkspaceDomainsModule } from 'src/engine/core-modules/domain/workspace-domains/workspace-domains.module'; -import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity'; import { FileModule } from 'src/engine/core-modules/file/file.module'; import { ThrottlerModule } from 'src/engine/core-modules/throttler/throttler.module'; @@ -20,7 +19,6 @@ import { ToolProviderModule } from 'src/engine/core-modules/tool-provider/tool-p import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { FeatureFlagGuard } from 'src/engine/guards/feature-flag.guard'; import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { AiAgentExecutionModule } from 'src/engine/metadata-modules/ai/ai-agent-execution/ai-agent-execution.module'; @@ -59,7 +57,6 @@ import { SystemPromptBuilderService } from './services/system-prompt-builder.ser NestjsQueryGraphQLModule.forFeature({ imports: [ NestjsQueryTypeOrmModule.forFeature([AgentChatThreadEntity]), - FeatureFlagModule, PermissionsModule, ], resolvers: [ @@ -79,7 +76,6 @@ import { SystemPromptBuilderService } from './services/system-prompt-builder.ser delete: { disabled: true }, guards: [ WorkspaceAuthGuard, - FeatureFlagGuard, SettingsPermissionGuard(PermissionFlagType.AI), ], }, @@ -88,7 +84,6 @@ import { SystemPromptBuilderService } from './services/system-prompt-builder.ser AiAgentExecutionModule, BillingModule, ThrottlerModule, - FeatureFlagModule, FileModule, PermissionsModule, SkillModule, diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/resolvers/agent-chat-subscription.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/resolvers/agent-chat-subscription.resolver.ts index b6c2de8cd4c..f42cfa5a7db 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/resolvers/agent-chat-subscription.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/resolvers/agent-chat-subscription.resolver.ts @@ -11,7 +11,6 @@ import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/ import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthUserWorkspaceId } from 'src/engine/decorators/auth/auth-user-workspace-id.decorator'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; -import { RequireFeatureFlag } from 'src/engine/guards/feature-flag.guard'; import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; @@ -23,7 +22,6 @@ import { AiGraphqlApiExceptionInterceptor } from 'src/engine/metadata-modules/ai import { AgentChatEventDTO } from 'src/engine/metadata-modules/ai/ai-chat/dtos/agent-chat-event.dto'; import { AgentChatThreadEntity } from 'src/engine/metadata-modules/ai/ai-chat/entities/agent-chat-thread.entity'; import { SubscriptionService } from 'src/engine/subscriptions/subscription.service'; -import { FeatureFlagKey } from 'twenty-shared/types'; @MetadataResolver() @UseGuards(WorkspaceAuthGuard, UserAuthGuard) @@ -43,7 +41,6 @@ export class AgentChatSubscriptionResolver { return payload.onAgentChatEvent.threadId === variables.threadId; }, }) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) @UseGuards(SettingsPermissionGuard(PermissionFlagType.AI)) async onAgentChatEvent( @Args('threadId', { type: () => UUIDScalarType }) threadId: string, diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/resolvers/agent-chat.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/resolvers/agent-chat.resolver.ts index 4fdccd0e665..5623f3fe954 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/resolvers/agent-chat.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/resolvers/agent-chat.resolver.ts @@ -10,7 +10,6 @@ import { import { InjectRepository } from '@nestjs/typeorm'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { FeatureFlagKey } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { Repository } from 'typeorm'; import GraphQLJSON from 'graphql-type-json'; @@ -29,10 +28,6 @@ import { toDisplayCredits } from 'src/engine/core-modules/usage/utils/to-display import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthUserWorkspaceId } from 'src/engine/decorators/auth/auth-user-workspace-id.decorator'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; -import { - FeatureFlagGuard, - RequireFeatureFlag, -} from 'src/engine/guards/feature-flag.guard'; import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { @@ -54,11 +49,7 @@ import { getCancelChannel } from 'src/engine/metadata-modules/ai/ai-chat/utils/g import { SystemPromptBuilderService } from 'src/engine/metadata-modules/ai/ai-chat/services/system-prompt-builder.service'; import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service'; -@UseGuards( - WorkspaceAuthGuard, - FeatureFlagGuard, - SettingsPermissionGuard(PermissionFlagType.AI), -) +@UseGuards(WorkspaceAuthGuard, SettingsPermissionGuard(PermissionFlagType.AI)) @UseInterceptors(AiGraphqlApiExceptionInterceptor) @MetadataResolver(() => AgentChatThreadDTO) export class AgentChatResolver { @@ -76,7 +67,6 @@ export class AgentChatResolver { ) {} @Query(() => AgentChatThreadDTO) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) async chatThread( @Args('id', { type: () => UUIDScalarType }) id: string, @AuthUserWorkspaceId() userWorkspaceId: string, @@ -85,7 +75,6 @@ export class AgentChatResolver { } @Query(() => [AgentMessageDTO]) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) async chatMessages( @Args('threadId', { type: () => UUIDScalarType }) threadId: string, @AuthUserWorkspaceId() userWorkspaceId: string, @@ -97,7 +86,6 @@ export class AgentChatResolver { } @Query(() => ChatStreamCatchupChunksDTO) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) async chatStreamCatchupChunks( @Args('threadId', { type: () => UUIDScalarType }) threadId: string, @AuthUserWorkspaceId() userWorkspaceId: string, @@ -108,7 +96,6 @@ export class AgentChatResolver { } @Mutation(() => AgentChatThreadDTO) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) async createChatThread( @AuthUserWorkspaceId() userWorkspaceId: string, @AuthWorkspace() workspace: WorkspaceEntity, @@ -120,7 +107,6 @@ export class AgentChatResolver { } @Mutation(() => SendChatMessageResultDTO) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) async sendChatMessage( @Args('threadId', { type: () => UUIDScalarType }) threadId: string, @Args('text') text: string, @@ -210,7 +196,6 @@ export class AgentChatResolver { } @Mutation(() => Boolean) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) async stopAgentChatStream( @Args('threadId', { type: () => UUIDScalarType }) threadId: string, @AuthUserWorkspaceId() userWorkspaceId: string, @@ -236,7 +221,6 @@ export class AgentChatResolver { } @Mutation(() => Boolean) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) async deleteQueuedChatMessage( @Args('messageId', { type: () => UUIDScalarType }) messageId: string, @AuthUserWorkspaceId() userWorkspaceId: string, @@ -276,7 +260,6 @@ export class AgentChatResolver { } @Query(() => AiSystemPromptPreviewDTO) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) async getAiSystemPromptPreview( @AuthWorkspace() workspace: WorkspaceEntity, @AuthUserWorkspaceId() userWorkspaceId: string, diff --git a/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts index 5001249b82f..0b4f756c8d3 100644 --- a/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts @@ -8,7 +8,6 @@ import { Args, Mutation, Parent, Query, ResolveField } from '@nestjs/graphql'; import { msg } from '@lingui/core/macro'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { FeatureFlagKey } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; @@ -22,7 +21,6 @@ import { WorkspaceMemberDTO } from 'src/engine/core-modules/user/dtos/workspace- import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspaceMemberId } from 'src/engine/decorators/auth/auth-workspace-member-id.decorator'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; -import { RequireFeatureFlag } from 'src/engine/guards/feature-flag.guard'; import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; @@ -270,7 +268,6 @@ export class RoleResolver { } @Mutation(() => Boolean) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) async assignRoleToAgent( @Args('agentId', { type: () => UUIDScalarType }) agentId: string, @Args('roleId', { type: () => UUIDScalarType }) roleId: string, @@ -286,7 +283,6 @@ export class RoleResolver { } @Mutation(() => Boolean) - @RequireFeatureFlag(FeatureFlagKey.IS_AI_ENABLED) async removeRoleFromAgent( @Args('agentId', { type: () => UUIDScalarType }) agentId: string, @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, diff --git a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts index 2e7f86dd9a8..c5aaf59af44 100644 --- a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts @@ -232,7 +232,6 @@ describe('WorkspaceEntityManager', () => { featureFlagsMap: { IS_UNIQUE_INDEXES_ENABLED: false, IS_JSON_FILTER_ENABLED: false, - IS_AI_ENABLED: false, IS_MARKETPLACE_SETTING_TAB_VISIBLE: false, IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED: false, IS_PUBLIC_DOMAIN_ENABLED: false, @@ -262,7 +261,6 @@ describe('WorkspaceEntityManager', () => { featureFlagMap: { IS_UNIQUE_INDEXES_ENABLED: false, IS_JSON_FILTER_ENABLED: false, - IS_AI_ENABLED: false, IS_PUBLIC_DOMAIN_ENABLED: false, IS_EMAILING_DOMAIN_ENABLED: false, }, diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts index 77e672b8be6..a4f9015feae 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts @@ -25,11 +25,6 @@ export const seedFeatureFlags = async ({ workspaceId: workspaceId, value: false, }, - { - key: FeatureFlagKey.IS_AI_ENABLED, - workspaceId: workspaceId, - value: true, - }, { key: FeatureFlagKey.IS_PUBLIC_DOMAIN_ENABLED, workspaceId: workspaceId, diff --git a/packages/twenty-shared/src/types/FeatureFlagKey.ts b/packages/twenty-shared/src/types/FeatureFlagKey.ts index 89a5ae618ef..07db2629922 100644 --- a/packages/twenty-shared/src/types/FeatureFlagKey.ts +++ b/packages/twenty-shared/src/types/FeatureFlagKey.ts @@ -1,7 +1,6 @@ export enum FeatureFlagKey { IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED', IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED', - IS_AI_ENABLED = 'IS_AI_ENABLED', IS_COMMAND_MENU_ITEM_ENABLED = 'IS_COMMAND_MENU_ITEM_ENABLED', IS_MARKETPLACE_SETTING_TAB_VISIBLE = 'IS_MARKETPLACE_SETTING_TAB_VISIBLE', IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED = 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED',