diff --git a/packages/twenty-server/src/engine/core-modules/skills/skill-definition.type.ts b/packages/twenty-server/src/engine/core-modules/skills/skill-definition.type.ts new file mode 100644 index 0000000000..0d7d391c9e --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/skills/skill-definition.type.ts @@ -0,0 +1,6 @@ +export type SkillDefinition = { + name: string; + label: string; + description: string; + content: string; +}; diff --git a/packages/twenty-server/src/engine/core-modules/skills/skills.module.ts b/packages/twenty-server/src/engine/core-modules/skills/skills.module.ts new file mode 100644 index 0000000000..434da65439 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/skills/skills.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; + +import { SkillsService } from './skills.service'; + +@Module({ + providers: [SkillsService], + exports: [SkillsService], +}) +export class SkillsModule {} diff --git a/packages/twenty-server/src/engine/core-modules/skills/skills.service.ts b/packages/twenty-server/src/engine/core-modules/skills/skills.service.ts new file mode 100644 index 0000000000..8f8d587ae3 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/skills/skills.service.ts @@ -0,0 +1,56 @@ +import { Injectable } from '@nestjs/common'; + +import { SkillDefinition } from 'src/engine/core-modules/skills/skill-definition.type'; +import { DASHBOARD_BUILDING_SKILL } from 'src/engine/core-modules/skills/skills/dashboard-building.skill'; +import { DATA_MANIPULATION_SKILL } from 'src/engine/core-modules/skills/skills/data-manipulation.skill'; +import { METADATA_BUILDING_SKILL } from 'src/engine/core-modules/skills/skills/metadata-building.skill'; +import { RESEARCH_SKILL } from 'src/engine/core-modules/skills/skills/research.skill'; +import { WORKFLOW_BUILDING_SKILL } from 'src/engine/core-modules/skills/skills/workflow-building.skill'; + +const SKILL_DEFINITIONS: SkillDefinition[] = [ + WORKFLOW_BUILDING_SKILL, + DATA_MANIPULATION_SKILL, + DASHBOARD_BUILDING_SKILL, + METADATA_BUILDING_SKILL, + RESEARCH_SKILL, +]; + +export type Skill = { + name: string; + label: string; + description: string; + content: string; +}; + +@Injectable() +export class SkillsService { + getAllSkills(): Skill[] { + return SKILL_DEFINITIONS.map((skill) => ({ + name: skill.name, + label: skill.label, + description: skill.description, + content: skill.content, + })); + } + + getSkillByName(name: string): Skill | undefined { + const skillDef = SKILL_DEFINITIONS.find((skill) => skill.name === name); + + if (!skillDef) { + return undefined; + } + + return { + name: skillDef.name, + label: skillDef.label, + description: skillDef.description, + content: skillDef.content, + }; + } + + getSkillsByNames(names: string[]): Skill[] { + return names + .map((name) => this.getSkillByName(name)) + .filter((skill): skill is Skill => skill !== undefined); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/skills/skills/dashboard-building.skill.ts b/packages/twenty-server/src/engine/core-modules/skills/skills/dashboard-building.skill.ts new file mode 100644 index 0000000000..76223083f3 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/skills/skills/dashboard-building.skill.ts @@ -0,0 +1,66 @@ +import { type SkillDefinition } from 'src/engine/core-modules/skills/skill-definition.type'; + +export const DASHBOARD_BUILDING_SKILL: SkillDefinition = { + name: 'dashboard-building', + label: 'Dashboard Building', + description: 'Creating and managing dashboards with widgets and layouts', + content: `# Dashboard Building Skill + +You help users create and manage dashboards with widgets. + +## Capabilities + +- Create new dashboards from scratch +- Add, modify, and remove widgets from dashboards +- Configure widget types (VIEW, GRAPH, FIELDS, TIMELINE, TASKS, NOTES, FILES, EMAILS, CALENDAR, RICH_TEXT, IFRAME, WORKFLOW) +- Manage dashboard tabs and layouts +- Position widgets in a grid system (12-column layout) + +## Dashboard Structure + +- **Dashboard**: Container with a title and pageLayout +- **PageLayout**: Contains tabs (type: DASHBOARD) +- **PageLayoutTab**: Contains widgets with a title, position, and layoutMode (grid/vertical-list/canvas) +- **PageLayoutWidget**: Individual widget with type, title, gridPosition, and optional configuration + +## Grid System + +- 12 columns total +- Grid positions: { row, column, rowSpan, columnSpan } +- Common sizes: Full width (columnSpan: 12), Half width (columnSpan: 6), Quarter width (columnSpan: 3) +- Typical heights: Small (rowSpan: 4), Medium (rowSpan: 6), Large (rowSpan: 8) + +## Widget Types Explained + +- **VIEW**: Display a filtered view of records (companies, people, opportunities, etc.) +- **GRAPH**: Show charts and visualizations of data +- **FIELDS**: Display specific fields from a record +- **TIMELINE**: Show activity timeline +- **TASKS**: Display tasks list +- **NOTES**: Show notes +- **FILES**: Display file attachments +- **EMAILS**: Show email communications +- **CALENDAR**: Display calendar events +- **RICH_TEXT**: Custom text content +- **IFRAME**: Embed external content +- **WORKFLOW**: Display workflow information + +## Approach + +- Ask clarifying questions about dashboard purpose and desired widgets +- Suggest appropriate widget types and layouts for the use case +- Create well-organized, visually balanced dashboards +- For modifications, first understand current structure +- Explain widget placement and purpose +- Consider responsive design (widgets stack on smaller screens) + +## Layout Best Practices + +- Place most important information at the top +- Group related widgets together +- Use consistent widget sizes when possible +- Leave some whitespace for visual clarity +- Consider logical reading order (left to right, top to bottom) + +Prioritize user needs and dashboard usability.`, +}; diff --git a/packages/twenty-server/src/engine/core-modules/skills/skills/data-manipulation.skill.ts b/packages/twenty-server/src/engine/core-modules/skills/skills/data-manipulation.skill.ts new file mode 100644 index 0000000000..323de52f58 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/skills/skills/data-manipulation.skill.ts @@ -0,0 +1,44 @@ +import { type SkillDefinition } from 'src/engine/core-modules/skills/skill-definition.type'; + +export const DATA_MANIPULATION_SKILL: SkillDefinition = { + name: 'data-manipulation', + label: 'Data Manipulation', + description: + 'Searching, filtering, creating, and updating records across all objects', + content: `# Data Manipulation Skill + +You explore and manage data across companies, people, opportunities, tasks, notes, and custom objects. + +## Capabilities + +- Search, filter, sort, create, update records +- Manage relationships between records +- Bulk operations and data analysis + +## Constraints + +- READ and WRITE access to all objects +- CANNOT delete records or access workflow objects +- CANNOT modify workspace settings + +## Multi-step Approach + +- Chain queries to solve complex requests (e.g., find companies → get their opportunities → calculate totals) +- If a query fails or returns no results, try alternative filters or approaches +- Validate data exists before referencing it (search before update) +- Use results from one query to inform the next +- Try 2-3 different approaches before giving up + +## Sorting (Critical) + +For "top N" queries, use orderBy with limit: +- Examples: orderBy: [{"employees": "DescNullsLast"}], orderBy: [{"createdAt": "AscNullsFirst"}] +- Valid directions: "AscNullsFirst", "AscNullsLast", "DescNullsFirst", "DescNullsLast" + +## Before Bulk Operations + +- Confirm the scope and impact +- Explain what will change + +Prioritize data integrity and provide clear feedback on operations performed.`, +}; diff --git a/packages/twenty-server/src/engine/core-modules/skills/skills/metadata-building.skill.ts b/packages/twenty-server/src/engine/core-modules/skills/skills/metadata-building.skill.ts new file mode 100644 index 0000000000..c58af5f026 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/skills/skills/metadata-building.skill.ts @@ -0,0 +1,64 @@ +import { type SkillDefinition } from 'src/engine/core-modules/skills/skill-definition.type'; + +export const METADATA_BUILDING_SKILL: SkillDefinition = { + name: 'metadata-building', + label: 'Metadata Building', + description: + 'Managing the data model: creating objects, fields, and relations', + content: `# Metadata Building Skill + +You help users manage their workspace data model by creating, updating, and organizing custom objects and fields. + +## Capabilities + +- Create new custom objects with appropriate naming and configuration +- Add fields to existing objects (text, number, date, select, relation, etc.) +- Update object and field properties (labels, descriptions, icons) +- Manage field settings (required, unique, default values) +- Create relations between objects + +## Key Concepts + +- **Objects**: Represent entities in the data model (e.g., Company, Person, Opportunity) +- **Fields**: Properties of objects with specific types (TEXT, NUMBER, DATE_TIME, SELECT, RELATION, etc.) +- **Relations**: Links between objects (one-to-many, many-to-one) +- **Labels vs Names**: Labels are for display, names are internal identifiers (camelCase) + +## Field Types Available + +- **TEXT**: Simple text fields +- **NUMBER**: Numeric values (integers or decimals) +- **BOOLEAN**: True/false values +- **DATE_TIME**: Date and time values +- **DATE**: Date only values +- **SELECT**: Single choice from options +- **MULTI_SELECT**: Multiple choices from options +- **LINK**: URL fields +- **LINKS**: Multiple URL fields +- **EMAIL**: Email address fields +- **EMAILS**: Multiple email fields +- **PHONE**: Phone number fields +- **PHONES**: Multiple phone fields +- **CURRENCY**: Monetary values +- **RATING**: Star ratings +- **RELATION**: Links to other objects +- **RICH_TEXT**: Formatted text content + +## Best Practices + +- Use clear, descriptive names for objects and fields +- Follow naming conventions: singular for object names, camelCase for field names +- Add helpful descriptions to objects and fields +- Choose appropriate field types for the data being stored +- Consider relationships between objects when designing the data model + +## Approach + +- Ask clarifying questions to understand the user's data modeling needs +- Suggest best practices for naming and organization +- Explain the impact of changes to the data model +- Verify object and field existence before making updates +- Provide clear feedback on operations performed + +Prioritize data model integrity and user understanding.`, +}; diff --git a/packages/twenty-server/src/engine/core-modules/skills/skills/research.skill.ts b/packages/twenty-server/src/engine/core-modules/skills/skills/research.skill.ts new file mode 100644 index 0000000000..4648b90a8c --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/skills/skills/research.skill.ts @@ -0,0 +1,34 @@ +import { type SkillDefinition } from 'src/engine/core-modules/skills/skill-definition.type'; + +export const RESEARCH_SKILL: SkillDefinition = { + name: 'research', + label: 'Research', + description: 'Finding information and gathering facts from the web', + content: `# Research Skill + +You find information and gather facts from the web. + +## Capabilities + +- Search for current information and facts +- Research companies, people, technologies, trends +- Gather competitive intelligence and market data +- Find contact details and verify information + +## Research Strategy + +- Try multiple search queries from different angles +- If initial searches fail, use alternative search terms +- Cross-reference information when possible +- Cite sources and provide context + +## Present Findings + +- Be thorough but concise +- Organize information logically +- Distinguish facts from speculation +- Note if information might be outdated +- Include relevant sources + +Be persistent in finding accurate information.`, +}; diff --git a/packages/twenty-server/src/engine/core-modules/skills/skills/workflow-building.skill.ts b/packages/twenty-server/src/engine/core-modules/skills/skills/workflow-building.skill.ts new file mode 100644 index 0000000000..56cae0e859 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/skills/skills/workflow-building.skill.ts @@ -0,0 +1,62 @@ +import { type SkillDefinition } from 'src/engine/core-modules/skills/skill-definition.type'; + +export const WORKFLOW_BUILDING_SKILL: SkillDefinition = { + name: 'workflow-building', + label: 'Workflow Building', + description: + 'Creating and managing automation workflows with triggers and steps', + content: `# Workflow Building Skill + +You help users create and manage automation workflows. + +## Capabilities + +- Create workflows from scratch +- Modify existing workflows (add, remove, update steps) +- Explain workflow structure and suggest improvements + +## Key Concepts + +- **Triggers**: DATABASE_EVENT, MANUAL, CRON, WEBHOOK +- **Steps**: CREATE_RECORD, SEND_EMAIL, CODE, etc. +- **Data flow**: Use {{stepId.fieldName}} to reference previous step outputs +- **Relationships**: Use nested objects like {"company": {"id": "{{reference}}"}} + +## CRON Trigger Settings Schema + +For CRON triggers, settings.type must be one of these exact values: + +1. **DAYS** - Daily schedule + - Requires: schedule: { day: number (1+), hour: number (0-23), minute: number (0-59) } + - Example: { type: "DAYS", schedule: { day: 1, hour: 9, minute: 0 }, outputSchema: {} } + +2. **HOURS** - Hourly schedule (USE THIS FOR "EVERY HOUR") + - Requires: schedule: { hour: number (1+), minute: number (0-59) } + - Example: { type: "HOURS", schedule: { hour: 1, minute: 0 }, outputSchema: {} } + - This runs every X hours at Y minutes past the hour + +3. **MINUTES** - Minute-based schedule + - Requires: schedule: { minute: number (1+) } + - Example: { type: "MINUTES", schedule: { minute: 15 }, outputSchema: {} } + +4. **CUSTOM** - Custom cron pattern + - Requires: pattern: string (cron expression) + - Example: { type: "CUSTOM", pattern: "0 * * * *", outputSchema: {} } + +## Critical Notes + +Always rely on tool schema definitions: +- The workflow creation tool provides comprehensive schemas with examples +- Follow schema definitions exactly for field names, types, and structures +- Schema includes validation rules and common patterns + +## Approach + +- Ask clarifying questions to understand user needs +- Suggest appropriate actions for the use case +- Explain each step and why it's needed +- For modifications, understand current structure first +- Ensure workflow logic remains coherent + +Prioritize user understanding and workflow effectiveness.`, +}; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/tool-provider.module.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/tool-provider.module.ts index 9997d78c77..cbf0a244ad 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/tool-provider.module.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/tool-provider.module.ts @@ -26,8 +26,9 @@ import { ToolRegistryService } from './services/tool-registry.service'; // -> WorkflowRunnerModule -> WorkflowExecutorModule -> AiAgentActionModule // -> AiAgentExecutionModule -> ToolProviderModule // -// Instead, WorkflowToolWorkspaceService is an optional dependency that must be -// provided by the importing module (e.g., AiChatModule imports WorkflowToolsModule). +// Instead, WorkflowToolsModule is a @Global() module that provides WORKFLOW_TOOL_SERVICE_TOKEN. +// When WorkflowToolsModule is imported anywhere in the app (e.g., AiChatModule), +// the token becomes available globally to WorkflowToolProvider via @Optional() injection. @Module({ imports: [ diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/tools/agent-search.tool.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/tools/agent-search.tool.ts deleted file mode 100644 index 7e8b8e9e8a..0000000000 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/tools/agent-search.tool.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { z } from 'zod'; - -import { type AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity'; - -export const AGENT_SEARCH_TOOL_NAME = 'agent_search'; - -export const agentSearchInputSchema = z.object({ - input: z.object({ - query: z.string().describe('What kind of expertise or help you need'), - limit: z - .number() - .optional() - .default(2) - .describe('Maximum number of agents to return'), - }), -}); - -export type AgentSearchInput = z.infer['input']; - -export type AgentSearchResult = { - agents: Array<{ - name: string; - label: string; - expertise: string; - }>; - message: string; -}; - -export type AgentSearchFunction = ( - query: string, - options: { limit: number }, -) => Promise; - -export const createAgentSearchTool = (searchAgents: AgentSearchFunction) => ({ - description: - 'Search for agent expertise/skills to help with specialized tasks. Returns agent instructions that provide domain knowledge for workflows, data manipulation, metadata management, etc.', - inputSchema: agentSearchInputSchema, - execute: async (parameters: { - input: AgentSearchInput; - }): Promise => { - const { query, limit = 2 } = parameters.input; - - const agents = await searchAgents(query, { limit }); - - if (agents.length === 0) { - return { - agents: [], - message: `No agent expertise found matching "${query}". Try searching for: "workflow", "data", "metadata", "dashboard", or "research".`, - }; - } - - return { - agents: agents.map((agent) => ({ - name: agent.name, - label: agent.label, - expertise: agent.prompt, - })), - message: `Found ${agents.length} agent(s) with relevant expertise. Their instructions are included above to help guide your approach.`, - }; - }, -}); diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/tools/index.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/tools/index.ts index 49ee91a1e4..9eff6640de 100644 --- a/packages/twenty-server/src/engine/core-modules/tool-provider/tools/index.ts +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/tools/index.ts @@ -1,17 +1,17 @@ export { - createLoadToolsTool, LOAD_TOOLS_TOOL_NAME, + createLoadToolsTool, loadToolsInputSchema, + type DynamicToolStore, type LoadToolsInput, type LoadToolsResult, - type DynamicToolStore, } from './load-tools.tool'; export { - createAgentSearchTool, - AGENT_SEARCH_TOOL_NAME, - agentSearchInputSchema, - type AgentSearchInput, - type AgentSearchResult, - type AgentSearchFunction, -} from './agent-search.tool'; + LOAD_SKILL_TOOL_NAME, + createLoadSkillTool, + loadSkillInputSchema, + type LoadSkillFunction, + type LoadSkillInput, + type LoadSkillResult, +} from './load-skill.tool'; diff --git a/packages/twenty-server/src/engine/core-modules/tool-provider/tools/load-skill.tool.ts b/packages/twenty-server/src/engine/core-modules/tool-provider/tools/load-skill.tool.ts new file mode 100644 index 0000000000..822a823855 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/tool-provider/tools/load-skill.tool.ts @@ -0,0 +1,57 @@ +import { z } from 'zod'; + +import { type Skill } from 'src/engine/core-modules/skills/skills.service'; + +export const LOAD_SKILL_TOOL_NAME = 'load_skill'; + +export const loadSkillInputSchema = z.object({ + input: z.object({ + skillNames: z + .array(z.string()) + .describe( + 'Names of the skills to load (e.g., ["workflow-building", "data-manipulation"])', + ), + }), +}); + +export type LoadSkillInput = z.infer['input']; + +export type LoadSkillResult = { + skills: Array<{ + name: string; + label: string; + content: string; + }>; + message: string; +}; + +export type LoadSkillFunction = (names: string[]) => Skill[]; + +export const createLoadSkillTool = (loadSkills: LoadSkillFunction) => ({ + description: + 'Load specialized skills/expertise by name. Returns detailed instructions for workflows, data manipulation, dashboards, metadata, or research.', + inputSchema: loadSkillInputSchema, + execute: async (parameters: { + input: LoadSkillInput; + }): Promise => { + const { skillNames } = parameters.input; + + const skills = loadSkills(skillNames); + + if (skills.length === 0) { + return { + skills: [], + message: `No skills found with names: ${skillNames.join(', ')}. Available skills: workflow-building, data-manipulation, dashboard-building, metadata-building, research.`, + }; + } + + return { + skills: skills.map((skill) => ({ + name: skill.name, + label: skill.label, + content: skill.content, + })), + message: `Loaded ${skills.length} skill(s). Use the instructions above to guide your approach.`, + }; + }, +}); 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 dc9056c866..6115e32abb 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 @@ -7,17 +7,18 @@ import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature- import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity'; import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module'; import { FileModule } from 'src/engine/core-modules/file/file.module'; +import { SkillsModule } from 'src/engine/core-modules/skills/skills.module'; import { ThrottlerModule } from 'src/engine/core-modules/throttler/throttler.module'; import { ToolProviderModule } from 'src/engine/core-modules/tool-provider/tool-provider.module'; 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 { AiAgentExecutionModule } from 'src/engine/metadata-modules/ai/ai-agent-execution/ai-agent-execution.module'; -import { AiAgentModule } from 'src/engine/metadata-modules/ai/ai-agent/ai-agent.module'; import { AiBillingModule } from 'src/engine/metadata-modules/ai/ai-billing/ai-billing.module'; import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module'; import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module'; import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module'; +import { WorkflowToolsModule } from 'src/modules/workflow/workflow-tools/workflow-tools.module'; import { AgentChatController } from './controllers/agent-chat.controller'; import { AgentChatThreadEntity } from './entities/agent-chat-thread.entity'; @@ -34,13 +35,13 @@ import { ChatExecutionService } from './services/chat-execution.service'; FileEntity, UserWorkspaceEntity, ]), - AiAgentModule, AiAgentExecutionModule, ThrottlerModule, FeatureFlagModule, FileUploadModule, FileModule, PermissionsModule, + SkillsModule, WorkspaceCacheStorageModule, WorkspaceCacheModule, WorkspaceDomainsModule, @@ -49,6 +50,7 @@ import { ChatExecutionService } from './services/chat-execution.service'; UserWorkspaceModule, AiBillingModule, ToolProviderModule, + WorkflowToolsModule, ], controllers: [AgentChatController], providers: [ diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/chat-execution.service.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/chat-execution.service.ts index d724f929e5..403ed2a4d9 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/chat-execution.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-chat/services/chat-execution.service.ts @@ -15,22 +15,21 @@ import { AppPath } from 'twenty-shared/types'; import { getAppPath } from 'twenty-shared/utils'; import { WorkspaceDomainsService } from 'src/engine/core-modules/domain/workspace-domains/services/workspace-domains.service'; +import { SkillsService } from 'src/engine/core-modules/skills/skills.service'; import { type ToolIndexEntry, ToolRegistryService, } from 'src/engine/core-modules/tool-provider/services/tool-registry.service'; import { - AGENT_SEARCH_TOOL_NAME, - createAgentSearchTool, + createLoadSkillTool, createLoadToolsTool, type DynamicToolStore, + LOAD_SKILL_TOOL_NAME, LOAD_TOOLS_TOOL_NAME, } from 'src/engine/core-modules/tool-provider/tools'; import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AgentActorContextService } from 'src/engine/metadata-modules/ai/ai-agent-execution/services/agent-actor-context.service'; -import { AgentService } from 'src/engine/metadata-modules/ai/ai-agent/agent.service'; import { AGENT_CONFIG } from 'src/engine/metadata-modules/ai/ai-agent/constants/agent-config.const'; -import { type AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity'; import { type BrowsingContextType } from 'src/engine/metadata-modules/ai/ai-agent/types/browsingContext.type'; import { repairToolCall } from 'src/engine/metadata-modules/ai/ai-agent/utils/repair-tool-call.util'; import { AIBillingService } from 'src/engine/metadata-modules/ai/ai-billing/services/ai-billing.service'; @@ -49,11 +48,8 @@ export type ChatExecutionOptions = { export type ChatExecutionResult = { stream: ReturnType; preloadedTools: string[]; - initialAgents: string[]; }; -const INITIAL_AGENTS_LIMIT = 2; - // Common tools to pre-load for quick access const COMMON_PRELOAD_TOOLS = ['http_request', 'search_help_center']; @@ -63,7 +59,7 @@ export class ChatExecutionService { constructor( private readonly toolRegistry: ToolRegistryService, - private readonly agentService: AgentService, + private readonly skillsService: SkillsService, private readonly aiModelRegistryService: AiModelRegistryService, private readonly aiBillingService: AIBillingService, private readonly agentActorContextService: AgentActorContextService, @@ -84,21 +80,19 @@ export class ChatExecutionService { const toolContext = { workspaceId: workspace.id, roleId, actorContext }; - const lastUserMessage = this.getLastUserMessage(messages); - const contextString = browsingContext ? this.buildContextFromBrowsingContext(workspace, browsingContext) : undefined; - const [toolCatalog, initialAgents] = await Promise.all([ - this.toolRegistry.buildToolIndex(workspace.id, roleId), - this.agentService.searchAgents(lastUserMessage, workspace.id, { - limit: INITIAL_AGENTS_LIMIT, - }), - ]); + const toolCatalog = await this.toolRegistry.buildToolIndex( + workspace.id, + roleId, + ); + + const skillCatalog = this.skillsService.getAllSkills(); this.logger.log( - `Built tool catalog with ${toolCatalog.length} tools, ${initialAgents.length} agents`, + `Built tool catalog with ${toolCatalog.length} tools, ${skillCatalog.length} skills available`, ); const preloadedTools = await this.toolRegistry.getToolsByName( @@ -132,14 +126,14 @@ export class ChatExecutionService { this.logger.log(`Dynamically loaded tools: ${toolNames.join(', ')}`); }, ), - [AGENT_SEARCH_TOOL_NAME]: createAgentSearchTool((query, options) => - this.agentService.searchAgents(query, workspace.id, options), + [LOAD_SKILL_TOOL_NAME]: createLoadSkillTool((skillNames) => + this.skillsService.getSkillsByNames(skillNames), ), }; const systemPrompt = this.buildSystemPrompt( toolCatalog, - initialAgents, + skillCatalog, preloadedToolNames, contextString, ); @@ -187,7 +181,6 @@ export class ChatExecutionService { return { stream, preloadedTools: preloadedToolNames, - initialAgents: initialAgents.map((a) => a.name), }; } @@ -247,27 +240,9 @@ export class ChatExecutionService { return context; } - private getLastUserMessage( - messages: UIMessage[], - ): string { - for (let i = messages.length - 1; i >= 0; i--) { - const message = messages[i]; - - if (message.role === 'user') { - const textPart = message.parts.find((part) => part.type === 'text'); - - if (textPart && 'text' in textPart) { - return textPart.text; - } - } - } - - return ''; - } - private buildSystemPrompt( toolCatalog: ToolIndexEntry[], - agents: AgentEntity[], + skillCatalog: Array<{ name: string; label: string; description: string }>, preloadedTools: string[], contextString?: string, ): string { @@ -276,15 +251,8 @@ export class ChatExecutionService { CHAT_SYSTEM_PROMPTS.RESPONSE_FORMAT, ]; - if (agents.length > 0) { - const skillsSection = agents - .map((agent) => `## ${agent.label} Expertise\n${agent.prompt}`) - .join('\n\n'); - - parts.push(`\nYou have the following expertise:\n\n${skillsSection}`); - } - parts.push(this.buildToolCatalogSection(toolCatalog, preloadedTools)); + parts.push(this.buildSkillCatalogSection(skillCatalog)); if (contextString) { parts.push( @@ -295,6 +263,26 @@ export class ChatExecutionService { return parts.join('\n'); } + private buildSkillCatalogSection( + skillCatalog: Array<{ name: string; label: string; description: string }>, + ): string { + if (skillCatalog.length === 0) { + return ''; + } + + const skillsList = skillCatalog + .map((skill) => `- \`${skill.name}\`: ${skill.description}`) + .join('\n'); + + return ` +## Available Skills + +Skills provide detailed expertise for specialized tasks. Load a skill before attempting complex operations. +To load a skill, call \`${LOAD_SKILL_TOOL_NAME}\` with the skill name(s). + +${skillsList}`; + } + private buildToolCatalogSection( toolCatalog: ToolIndexEntry[], preloadedTools: string[], @@ -357,8 +345,7 @@ ${tools ### How to Use Tools 1. **Web search** (\`web_search\`): Use for ANY request requiring current/real-time information from the internet 2. **Pre-loaded tools** (marked with ✓): Use directly -3. **Other tools**: First call \`${LOAD_TOOLS_TOOL_NAME}({toolNames: ["tool_name"]})\`, then use the tool -4. **Agent expertise**: Call \`${AGENT_SEARCH_TOOL_NAME}\` to load specialized knowledge for workflows, etc.`); +3. **Other tools**: First call \`${LOAD_TOOLS_TOOL_NAME}({toolNames: ["tool_name"]})\`, then use the tool`); return sections.join('\n'); } diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/tools/field-metadata-tools.factory.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/tools/field-metadata-tools.factory.ts index 2d42b22073..5e7091dc1b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/tools/field-metadata-tools.factory.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/tools/field-metadata-tools.factory.ts @@ -146,7 +146,7 @@ export class FieldMetadataToolsFactory { generateTools(workspaceId: string): ToolSet { return { - 'get-field-metadata': { + get_field_metadata: { description: 'Find fields metadata. Retrieve information about the fields of objects in the workspace data model.', inputSchema: GetFieldMetadataInputSchema, @@ -169,7 +169,7 @@ export class FieldMetadataToolsFactory { }); }, }, - 'create-field-metadata': { + create_field_metadata: { description: 'Create a new field metadata on an object. Specify the objectMetadataId and field properties.', inputSchema: CreateFieldMetadataInputSchema, @@ -209,7 +209,7 @@ export class FieldMetadataToolsFactory { } }, }, - 'update-field-metadata': { + update_field_metadata: { description: 'Update an existing field metadata. Provide the field ID and the properties to update.', inputSchema: UpdateFieldMetadataInputSchema, @@ -249,7 +249,7 @@ export class FieldMetadataToolsFactory { } }, }, - 'delete-field-metadata': { + delete_field_metadata: { description: 'Delete a field metadata by its ID.', inputSchema: DeleteFieldMetadataInputSchema, execute: async (parameters: { input: { id: string } }) => { diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/tools/object-metadata-tools.factory.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/tools/object-metadata-tools.factory.ts index 03e6b4c41e..3843be2a3b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/tools/object-metadata-tools.factory.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/tools/object-metadata-tools.factory.ts @@ -123,7 +123,7 @@ export class ObjectMetadataToolsFactory { generateTools(workspaceId: string): ToolSet { return { - 'get-object-metadata': { + get_object_metadata: { description: 'Find objects metadata. Retrieve information about the data model objects in the workspace.', inputSchema: GetObjectMetadataInputSchema, @@ -146,7 +146,7 @@ export class ObjectMetadataToolsFactory { ); }, }, - 'create-object-metadata': { + create_object_metadata: { description: 'Create a new object metadata in the workspace data model.', inputSchema: CreateObjectMetadataInputSchema, @@ -183,7 +183,7 @@ export class ObjectMetadataToolsFactory { } }, }, - 'update-object-metadata': { + update_object_metadata: { description: 'Update an existing object metadata. Provide the object ID and the fields to update.', inputSchema: UpdateObjectMetadataInputSchema, @@ -223,7 +223,7 @@ export class ObjectMetadataToolsFactory { } }, }, - 'delete-object-metadata': { + delete_object_metadata: { description: 'Delete an object metadata by its ID. This will also delete all associated fields.', inputSchema: DeleteObjectMetadataInputSchema, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/dashboard-builder-agent.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/dashboard-builder-agent.ts deleted file mode 100644 index 2ae9b0279e..0000000000 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/dashboard-builder-agent.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const'; -import { type StandardAgentDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface'; -import { DASHBOARD_MANAGER_ROLE } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/dashboard-manager-role'; - -export const DASHBOARD_BUILDER_AGENT: StandardAgentDefinition = { - standardId: '20202020-0002-0001-0001-000000000006', - name: 'dashboard-builder', - label: 'Dashboard Builder', - description: 'AI agent specialized in creating and managing dashboards', - icon: 'IconLayoutDashboard', - applicationId: null, - prompt: `You are a Dashboard Builder Agent for Twenty. You help users create and manage dashboards with widgets. - -Capabilities: -- Create new dashboards from scratch -- Add, modify, and remove widgets from dashboards -- Configure widget types (VIEW, GRAPH, FIELDS, TIMELINE, TASKS, NOTES, FILES, EMAILS, CALENDAR, RICH_TEXT, IFRAME, WORKFLOW) -- Manage dashboard tabs and layouts -- Position widgets in a grid system (12-column layout) - -Dashboard structure: -- Dashboard: Container with a title and pageLayout -- PageLayout: Contains tabs (type: DASHBOARD) -- PageLayoutTab: Contains widgets with a title, position, and layoutMode (grid/vertical-list/canvas) -- PageLayoutWidget: Individual widget with type, title, gridPosition, and optional configuration - -Grid system: -- 12 columns total -- Grid positions: { row, column, rowSpan, columnSpan } -- Common sizes: Full width (columnSpan: 12), Half width (columnSpan: 6), Quarter width (columnSpan: 3) -- Typical heights: Small (rowSpan: 4), Medium (rowSpan: 6), Large (rowSpan: 8) - -Widget types explained: -- VIEW: Display a filtered view of records (companies, people, opportunities, etc.) -- GRAPH: Show charts and visualizations of data -- FIELDS: Display specific fields from a record -- TIMELINE: Show activity timeline -- TASKS: Display tasks list -- NOTES: Show notes -- FILES: Display file attachments -- EMAILS: Show email communications -- CALENDAR: Display calendar events -- RICH_TEXT: Custom text content -- IFRAME: Embed external content -- WORKFLOW: Display workflow information - -Approach: -- Ask clarifying questions about dashboard purpose and desired widgets -- Suggest appropriate widget types and layouts for the use case -- Create well-organized, visually balanced dashboards -- For modifications, first understand current structure -- Explain widget placement and purpose -- Consider responsive design (widgets stack on smaller screens) - -Layout best practices: -- Place most important information at the top -- Group related widgets together -- Use consistent widget sizes when possible -- Leave some whitespace for visual clarity -- Consider logical reading order (left to right, top to bottom) - -Prioritize user needs and dashboard usability.`, - modelId: DEFAULT_SMART_MODEL, - responseFormat: { type: 'text' }, - isCustom: false, - standardRoleId: DASHBOARD_MANAGER_ROLE.standardId, - modelConfiguration: {}, - outputStrategy: 'direct', - evaluationInputs: [], -}; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/data-manipulator-agent.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/data-manipulator-agent.ts deleted file mode 100644 index d896ba7c75..0000000000 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/data-manipulator-agent.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const'; -import { type StandardAgentDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface'; -import { DATA_MANIPULATOR_ROLE } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/data-manipulator-role'; - -export const DATA_MANIPULATOR_AGENT: StandardAgentDefinition = { - standardId: '20202020-0002-0001-0001-000000000003', - name: 'data-manipulator', - label: 'Data Manipulator', - description: - 'AI agent specialized in exploring, reading, creating, updating, and managing data across all objects', - icon: 'IconEdit', - applicationId: null, - prompt: `You are a Data Manipulator Agent for Twenty. You explore and manage data across companies, people, opportunities, tasks, notes, and custom objects. - -Capabilities: -- Search, filter, sort, create, update records -- Manage relationships between records -- Bulk operations and data analysis - -Constraints: -- READ and WRITE access to all objects -- CANNOT delete records or access workflow objects -- CANNOT modify workspace settings - -Multi-step approach: -- Chain queries to solve complex requests (e.g., find companies → get their opportunities → calculate totals) -- If a query fails or returns no results, try alternative filters or approaches -- Validate data exists before referencing it (search before update) -- Use results from one query to inform the next -- Try 2-3 different approaches before giving up - -Sorting (critical): -- For "top N" queries, use orderBy with limit -- Examples: orderBy: [{"employees": "DescNullsLast"}], orderBy: [{"createdAt": "AscNullsFirst"}] -- Valid directions: "AscNullsFirst", "AscNullsLast", "DescNullsFirst", "DescNullsLast" - -Before bulk operations: -- Confirm the scope and impact -- Explain what will change - -Prioritize data integrity and provide clear feedback on operations performed.`, - modelId: DEFAULT_SMART_MODEL, - responseFormat: { type: 'text' }, - isCustom: false, - standardRoleId: DATA_MANIPULATOR_ROLE.standardId, - modelConfiguration: {}, - evaluationInputs: [], -}; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/metadata-builder-agent.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/metadata-builder-agent.ts deleted file mode 100644 index 7282b18f20..0000000000 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/metadata-builder-agent.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const'; -import { type StandardAgentDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface'; -import { DATA_MODEL_MANAGER_ROLE } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/data-model-manager-role'; - -export const METADATA_BUILDER_AGENT: StandardAgentDefinition = { - standardId: '20202020-0002-0001-0001-000000000007', - name: 'metadata-builder', - label: 'Metadata Builder', - description: - 'AI agent specialized in modifying the workspace SCHEMA/DATA MODEL - creating new object types, adding fields to objects, and managing object structure (NOT for creating data records)', - icon: 'IconDatabaseEdit', - applicationId: null, - prompt: `You are a Metadata Builder Agent for Twenty. You help users manage their workspace data model by creating, updating, and organizing custom objects and fields. - -Capabilities: -- Create new custom objects with appropriate naming and configuration -- Add fields to existing objects (text, number, date, select, relation, etc.) -- Update object and field properties (labels, descriptions, icons) -- Manage field settings (required, unique, default values) -- Create relations between objects - -Key concepts: -- Objects: Represent entities in the data model (e.g., Company, Person, Opportunity) -- Fields: Properties of objects with specific types (TEXT, NUMBER, DATE_TIME, SELECT, RELATION, etc.) -- Relations: Links between objects (one-to-many, many-to-one) -- Labels vs Names: Labels are for display, names are internal identifiers (camelCase) - -Field types available: -- TEXT: Simple text fields -- NUMBER: Numeric values (integers or decimals) -- BOOLEAN: True/false values -- DATE_TIME: Date and time values -- DATE: Date only values -- SELECT: Single choice from options -- MULTI_SELECT: Multiple choices from options -- LINK: URL fields -- LINKS: Multiple URL fields -- EMAIL: Email address fields -- EMAILS: Multiple email fields -- PHONE: Phone number fields -- PHONES: Multiple phone fields -- CURRENCY: Monetary values -- RATING: Star ratings -- RELATION: Links to other objects -- RICH_TEXT: Formatted text content - -Best practices: -- Use clear, descriptive names for objects and fields -- Follow naming conventions: singular for object names, camelCase for field names -- Add helpful descriptions to objects and fields -- Choose appropriate field types for the data being stored -- Consider relationships between objects when designing the data model - -Approach: -- Ask clarifying questions to understand the user's data modeling needs -- Suggest best practices for naming and organization -- Explain the impact of changes to the data model -- Verify object and field existence before making updates -- Provide clear feedback on operations performed - -Prioritize data model integrity and user understanding.`, - modelId: DEFAULT_SMART_MODEL, - responseFormat: { type: 'text' }, - isCustom: false, - standardRoleId: DATA_MODEL_MANAGER_ROLE.standardId, - modelConfiguration: {}, - outputStrategy: 'direct', - evaluationInputs: [ - 'Create a custom object called "Project" with fields for name, description, start date, and status', - 'Add a currency field called "budget" to the Project object', - 'Create a relation between Project and Company so each project belongs to a company', - 'Add a multi-select field for project tags with options: urgent, internal, client-facing', - 'Update the Project object description to explain what it tracks', - 'Create a "Task" object that relates to both Project and Person', - 'Add a rating field to track project priority from 1-5 stars', - 'Create a custom object for tracking Invoices with amount, date, and status fields', - 'Add a link field to the Company object for their website', - 'Create a relation between Person and Company for the account manager', - ], -}; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/researcher-agent.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/researcher-agent.ts deleted file mode 100644 index 6502464a4b..0000000000 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/researcher-agent.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const'; -import { type StandardAgentDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface'; - -export const RESEARCHER_AGENT: StandardAgentDefinition = { - standardId: '20202020-0002-0001-0001-000000000005', - name: 'researcher', - label: 'Researcher', - description: - 'AI agent specialized in researching information, finding facts, and gathering data from the web', - icon: 'IconSearch', - applicationId: null, - prompt: `You are a Researcher Agent for Twenty. You find information and gather facts from the web. - -Capabilities: -- Search for current information and facts -- Research companies, people, technologies, trends -- Gather competitive intelligence and market data -- Find contact details and verify information - -Research strategy: -- Try multiple search queries from different angles -- If initial searches fail, use alternative search terms -- Cross-reference information when possible -- Cite sources and provide context - -Present findings: -- Be thorough but concise -- Organize information logically -- Distinguish facts from speculation -- Note if information might be outdated -- Include relevant sources - -Be persistent in finding accurate information.`, - modelId: DEFAULT_SMART_MODEL, - responseFormat: { type: 'text' }, - isCustom: false, - modelConfiguration: { - webSearch: { - enabled: true, - }, - }, - evaluationInputs: [], -}; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/workflow-builder-agent.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/workflow-builder-agent.ts deleted file mode 100644 index 53b61cdcfa..0000000000 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/workflow-builder-agent.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const'; -import { type StandardAgentDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface'; -import { WORKFLOW_MANAGER_ROLE } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/workflow-manager-role'; - -export const WORKFLOW_BUILDER_AGENT: StandardAgentDefinition = { - standardId: '20202020-0002-0001-0001-000000000001', - name: 'workflow-builder', - label: 'Workflow Builder', - description: 'AI agent specialized in creating and managing workflows', - icon: 'IconSettingsAutomation', - applicationId: null, - prompt: `You are a Workflow Builder Agent for Twenty. You help users create and manage automation workflows. - -Capabilities: -- Create workflows from scratch -- Modify existing workflows (add, remove, update steps) -- Explain workflow structure and suggest improvements - -Key concepts: -- Triggers: DATABASE_EVENT, MANUAL, CRON, WEBHOOK -- Steps: CREATE_RECORD, SEND_EMAIL, CODE, etc. -- Data flow: Use {{stepId.fieldName}} to reference previous step outputs -- Relationships: Use nested objects like {"company": {"id": "{{reference}}"}} - -CRON Trigger Settings Schema: - For CRON triggers, settings.type must be one of these exact values: -1. "DAYS" - Daily schedule - - Requires: schedule: { day: number (1+), hour: number (0-23), minute: number (0-59) } - - Example: { type: "DAYS", schedule: { day: 1, hour: 9, minute: 0 }, outputSchema: {} } - -2. "HOURS" - Hourly schedule (USE THIS FOR "EVERY HOUR") - - Requires: schedule: { hour: number (1+), minute: number (0-59) } - - Example: { type: "HOURS", schedule: { hour: 1, minute: 0 }, outputSchema: {} } - - This runs every X hours at Y minutes past the hour - -3. "MINUTES" - Minute-based schedule - - Requires: schedule: { minute: number (1+) } - - Example: { type: "MINUTES", schedule: { minute: 15 }, outputSchema: {} } - -4. "CUSTOM" - Custom cron pattern - - Requires: pattern: string (cron expression) - - Example: { type: "CUSTOM", pattern: "0 * * * *", outputSchema: {} } - - -Critical: Always rely on tool schema definitions -- The workflow creation tool provides comprehensive schemas with examples -- Follow schema definitions exactly for field names, types, and structures -- Schema includes validation rules and common patterns - -Approach: -- Ask clarifying questions to understand user needs -- Suggest appropriate actions for the use case -- Explain each step and why it's needed -- For modifications, understand current structure first -- Ensure workflow logic remains coherent - -Prioritize user understanding and workflow effectiveness.`, - modelId: DEFAULT_SMART_MODEL, - responseFormat: { type: 'text' }, - isCustom: false, - standardRoleId: WORKFLOW_MANAGER_ROLE.standardId, - modelConfiguration: {}, - outputStrategy: 'direct', - evaluationInputs: [ - 'Build a workflow that runs everyday at 9:00 AM, finds the companies added in the last 24 hours, and create task title Welcome {companyName} for each', - 'Create a workflow that listens to company creation events and makes an http call to companies.twenty.com/{domain} to enrich them', - 'Update the quick lead workflow to add an http request to Google.com as the last step', - 'when a new lead is created, automatically send an email to the sales team with the lead details', - 'I need a workflow that runs every monday morning and creates a weekly summary report of all closed deals', - 'can you make a workflow to automatically assign new oppurtunities to sales reps based on territory?', - 'create workflow that updates contact status to inactive if theres no activity for 90 days', - 'Build automation to send followup email 3 days after first contact with prospect', - 'i want to automatically create a task for account manager when deal reaches negotiation stage', - 'setup a workflow that enriches company data from clearbit when new account is created', - 'make a workflow to notify slack channel when deal amount is over $50k', - 'need workflow that runs daily and finds all overdue tasks then sends reminder emails', - 'Create automation to update lead score when contact opens email or clicks link', - 'workflow to automatically create renewal opportunity 60 days before contract end date', - 'can you build a flow that copies contact info to company record when deal is won?', - 'I need to send a survey email 7 days after deal closes', - 'make workflow that assigns leads round-robin style to available sales reps', - 'create automation to tag contacts as "hot lead" when they visit pricing page 3 times', - 'workflow that escalates support tickets to manager if not resolved in 48 hours', - 'need a workflow to sync new contacts to mailchimp mailing list', - 'build flow that updates deal stage to lost if no activity for 30 days', - 'create workflow that sends birthday email to contacts on thier birthday', - 'can i get a workflow that creates calendar event when meeting is scheduled with prospect', - 'workflow to automatically generate quote pdf when opportunity moves to proposal stage', - ], -}; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/standard-agent-definitions.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/standard-agent-definitions.ts index 9546b76bb5..ca71dc5cbc 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/standard-agent-definitions.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/standard-agent-definitions.ts @@ -1,16 +1,6 @@ -import { DASHBOARD_BUILDER_AGENT } from './agents/dashboard-builder-agent'; -import { DATA_MANIPULATOR_AGENT } from './agents/data-manipulator-agent'; import { HELPER_AGENT } from './agents/helper-agent'; -import { METADATA_BUILDER_AGENT } from './agents/metadata-builder-agent'; -import { RESEARCHER_AGENT } from './agents/researcher-agent'; -import { WORKFLOW_BUILDER_AGENT } from './agents/workflow-builder-agent'; import { type StandardAgentDefinition } from './types/standard-agent-definition.interface'; export const STANDARD_AGENT_DEFINITIONS = [ - WORKFLOW_BUILDER_AGENT, - DATA_MANIPULATOR_AGENT, - DASHBOARD_BUILDER_AGENT, HELPER_AGENT, - RESEARCHER_AGENT, - METADATA_BUILDER_AGENT, ] as const satisfies StandardAgentDefinition[]; diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-complete-workflow.tool.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-complete-workflow.tool.ts index 0c87e8bda8..902a2cd9da 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-complete-workflow.tool.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-complete-workflow.tool.ts @@ -177,6 +177,13 @@ This is the most efficient way for AI to create workflows as it handles all the trigger: parameters.trigger, steps: parameters.steps, }, + recordReferences: [ + { + objectNameSingular: 'workflow', + recordId: workflowId, + displayName: parameters.name, + }, + ], }; } catch (error) { return { diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/workflow-tools.module.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/workflow-tools.module.ts index 85ed37ab07..bc093bf9fc 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-tools/workflow-tools.module.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-tools/workflow-tools.module.ts @@ -1,7 +1,8 @@ -import { Module } from '@nestjs/common'; +import { Global, Module } from '@nestjs/common'; import { RecordPositionModule } from 'src/engine/core-modules/record-position/record-position.module'; import { ToolGeneratorModule } from 'src/engine/core-modules/tool-generator/tool-generator.module'; +import { WORKFLOW_TOOL_SERVICE_TOKEN } from 'src/engine/core-modules/tool-provider/constants/workflow-tool-service.token'; import { WorkflowSchemaModule } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.module'; import { WorkflowVersionEdgeModule } from 'src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.module'; import { WorkflowVersionStepModule } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.module'; @@ -10,6 +11,9 @@ import { WorkflowTriggerModule } from 'src/modules/workflow/workflow-trigger/wor import { WorkflowToolWorkspaceService } from './services/workflow-tool.workspace-service'; +// Global module to make WORKFLOW_TOOL_SERVICE_TOKEN available to ToolProviderModule +// without creating a circular dependency (ToolProviderModule cannot import this module directly) +@Global() @Module({ imports: [ WorkflowVersionStepModule, @@ -20,7 +24,13 @@ import { WorkflowToolWorkspaceService } from './services/workflow-tool.workspace RecordPositionModule, ToolGeneratorModule, ], - providers: [WorkflowToolWorkspaceService], - exports: [WorkflowToolWorkspaceService], + providers: [ + WorkflowToolWorkspaceService, + { + provide: WORKFLOW_TOOL_SERVICE_TOKEN, + useExisting: WorkflowToolWorkspaceService, + }, + ], + exports: [WorkflowToolWorkspaceService, WORKFLOW_TOOL_SERVICE_TOKEN], }) export class WorkflowToolsModule {} diff --git a/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-deletion.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-deletion.integration-spec.ts.snap index 789b2e7078..2d38ec088b 100644 --- a/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-deletion.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-deletion.integration-spec.ts.snap @@ -16,7 +16,7 @@ exports[`Agent deletion should fail should fail when attempting to delete a stan ], "flatEntityMinimalInformation": { "id": Any, - "name": "dashboard-builder", + "name": "helper", }, "status": "fail", "type": "delete_agent", diff --git a/packages/twenty-server/test/integration/metadata/suites/agent/failing-agent-deletion.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/agent/failing-agent-deletion.integration-spec.ts index 5d1760a2ed..cdb28453f0 100644 --- a/packages/twenty-server/test/integration/metadata/suites/agent/failing-agent-deletion.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/agent/failing-agent-deletion.integration-spec.ts @@ -50,16 +50,16 @@ describe('Agent deletion should fail', () => { gqlFields: 'id name isCustom', }); - const dashboardBuilderAgent = data.findManyAgents.find( - (agent) => agent.name === 'dashboard-builder' && agent.isCustom === false, + const helperAgent = data.findManyAgents.find( + (agent) => agent.name === 'helper' && agent.isCustom === false, ); - expect(dashboardBuilderAgent).toBeDefined(); + expect(helperAgent).toBeDefined(); const { errors } = await deleteOneAgent({ expectToFail: true, input: { - id: dashboardBuilderAgent!.id, + id: helperAgent!.id, }, }); diff --git a/packages/twenty-server/test/integration/metadata/suites/agent/failing-agent-update.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/agent/failing-agent-update.integration-spec.ts index a628a053fa..a6b9076789 100644 --- a/packages/twenty-server/test/integration/metadata/suites/agent/failing-agent-update.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/agent/failing-agent-update.integration-spec.ts @@ -205,16 +205,16 @@ describe('Agent update should fail', () => { gqlFields: 'id name isCustom', }); - const dashboardBuilderAgent = data.findManyAgents.find( - (agent) => agent.name === 'dashboard-builder' && agent.isCustom === false, + const helperAgent = data.findManyAgents.find( + (agent) => agent.name === 'helper' && agent.isCustom === false, ); - expect(dashboardBuilderAgent).toBeDefined(); + expect(helperAgent).toBeDefined(); const { errors } = await updateOneAgent({ expectToFail: true, input: { - id: dashboardBuilderAgent!.id, + id: helperAgent!.id, label: 'Attempted Update to Standard Agent', }, });