Decouple Send Email node from workflows (#13322)

- Renamed `WorkflowActionAdapter` to `ToolExecutorWorkflowAction`
- Renamed `settingPermission` table to `permissionFlag` and `setting`
column to `flag`
- Decoupled the send email logic from workflows to tools
- Add new `Tools Permission` section in FE

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Abdul Rahman
2025-07-24 16:01:33 +05:30
committed by GitHub
parent eb404478c3
commit e93adde4b8
98 changed files with 1076 additions and 705 deletions
@@ -11,11 +11,16 @@ import { ToolAdapterService } from 'src/engine/core-modules/ai/services/tool-ada
import { ToolService } from 'src/engine/core-modules/ai/services/tool.service';
import { TokenModule } from 'src/engine/core-modules/auth/token/token.module';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { ToolRegistryService } from 'src/engine/core-modules/tool/services/tool-registry.service';
import { SendEmailTool } from 'src/engine/core-modules/tool/tools/send-email-tool/send-email-tool';
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
import { MessagingModule } from 'src/modules/messaging/messaging.module';
@Global()
@Module({
@@ -27,6 +32,9 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
WorkspacePermissionsCacheModule,
WorkspaceCacheStorageModule,
UserRoleModule,
TwentyORMModule,
MessagingModule,
PermissionsModule,
],
controllers: [AiController, McpController],
providers: [
@@ -34,8 +42,10 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
AiModelRegistryService,
ToolService,
ToolAdapterService,
ToolRegistryService,
AIBillingService,
McpService,
SendEmailTool,
],
exports: [
AiService,
@@ -43,7 +53,9 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
AIBillingService,
ToolService,
ToolAdapterService,
ToolRegistryService,
McpService,
SendEmailTool,
],
})
export class AiModule {}
@@ -2,28 +2,49 @@ import { Injectable } from '@nestjs/common';
import { ToolSet } from 'ai';
import { TOOLS } from 'src/engine/core-modules/tool/constants/tools.const';
import { ToolRegistryService } from 'src/engine/core-modules/tool/services/tool-registry.service';
import { ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type';
import { Tool } from 'src/engine/core-modules/tool/types/tool.type';
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
@Injectable()
export class ToolAdapterService {
getCoreTools(): ToolSet {
const tools = Array.from(TOOLS.entries()).reduce<ToolSet>(
(acc, [toolType, tool]) => {
acc[toolType.toLowerCase()] = {
description: tool.description,
parameters: tool.parameters,
execute: async (parameters) => {
const response = await tool.execute(parameters.input);
constructor(
private readonly toolRegistry: ToolRegistryService,
private readonly permissionsService: PermissionsService,
) {}
return response;
},
};
async getTools(roleId?: string, workspaceId?: string): Promise<ToolSet> {
const tools: ToolSet = {};
return acc;
},
{},
);
for (const toolType of this.toolRegistry.getAllToolTypes()) {
const tool = this.toolRegistry.getTool(toolType);
if (!tool.flag) {
tools[toolType.toLowerCase()] = this.createToolSet(tool);
} else if (roleId && workspaceId) {
const hasPermission = await this.permissionsService.hasToolPermission(
roleId,
workspaceId,
tool.flag as PermissionFlagType,
);
if (hasPermission) {
tools[toolType.toLowerCase()] = this.createToolSet(tool);
}
}
}
return tools;
}
private createToolSet(tool: Tool) {
return {
description: tool.description,
parameters: tool.parameters,
execute: async (parameters: { input: ToolInput }) =>
tool.execute(parameters.input),
};
}
}