mirror of
https://github.com/twentyhq/twenty
synced 2026-04-21 13:37:22 +00:00
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:
parent
69868a0ab6
commit
30b8663a74
32 changed files with 73 additions and 240 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<AgentChatProviderContent isAiEnabled={isAiEnabled}>
|
||||
{children}
|
||||
</AgentChatProviderContent>
|
||||
);
|
||||
return <AgentChatProviderContent>{children}</AgentChatProviderContent>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,26 +10,20 @@ import { Suspense } from 'react';
|
|||
|
||||
export const AgentChatProviderContent = ({
|
||||
children,
|
||||
isAiEnabled,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
isAiEnabled: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<AgentChatComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'agentChatComponentInstance' }}
|
||||
>
|
||||
{isAiEnabled && (
|
||||
<>
|
||||
<AgentChatThreadInitializationEffect />
|
||||
<AgentChatMessagesFetchEffect />
|
||||
<AgentChatStreamSubscriptionEffect />
|
||||
<AgentChatStreamingPartsDiffSyncEffect />
|
||||
<AgentChatSessionStartTimeEffect />
|
||||
<AgentChatStreamingAutoScrollEffect />
|
||||
</>
|
||||
)}
|
||||
<AgentChatThreadInitializationEffect />
|
||||
<AgentChatMessagesFetchEffect />
|
||||
<AgentChatStreamSubscriptionEffect />
|
||||
<AgentChatStreamingPartsDiffSyncEffect />
|
||||
<AgentChatSessionStartTimeEffect />
|
||||
<AgentChatStreamingAutoScrollEffect />
|
||||
{children}
|
||||
</AgentChatComponentInstanceContext.Provider>
|
||||
</Suspense>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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) => () => {
|
||||
|
|
|
|||
|
|
@ -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 <NavigationBar activeItemName={activeItemName} items={items} />;
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,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,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,
|
||||
]);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 && (
|
||||
<>
|
||||
<SidePanelWorkflowSelectStepTitle>
|
||||
{t`AI`}
|
||||
</SidePanelWorkflowSelectStepTitle>
|
||||
<WorkflowActionMenuItems
|
||||
actions={AI_ACTIONS}
|
||||
onClick={handleActionClick}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<SidePanelWorkflowSelectStepTitle>
|
||||
{t`AI`}
|
||||
</SidePanelWorkflowSelectStepTitle>
|
||||
<WorkflowActionMenuItems
|
||||
actions={AI_ACTIONS}
|
||||
onClick={handleActionClick}
|
||||
/>
|
||||
|
||||
<SidePanelWorkflowSelectStepTitle>
|
||||
{t`Flow`}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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`,
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -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<WorkflowVersionStepChangesDTO> {
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<AgentDTO[]> {
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Reference in a new issue