diff --git a/packages/twenty-server/src/engine/metadata-modules/agent/agent-execution.service.ts b/packages/twenty-server/src/engine/metadata-modules/agent/agent-execution.service.ts index 984862dc57..4dcbf970cd 100644 --- a/packages/twenty-server/src/engine/metadata-modules/agent/agent-execution.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/agent/agent-execution.service.ts @@ -29,6 +29,7 @@ import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modu import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { AgentExecutionContext } from './agent-handoff-executor.service'; import { AgentModelConfigService } from './agent-model-config.service'; import { AgentToolGeneratorService } from './agent-tool-generator.service'; import { AgentEntity } from './agent.entity'; @@ -40,7 +41,7 @@ export interface AgentExecutionResult { } @Injectable() -export class AgentExecutionService { +export class AgentExecutionService implements AgentExecutionContext { private readonly logger = new Logger(AgentExecutionService.name); constructor( @@ -61,10 +62,12 @@ export class AgentExecutionService { messages, system, agent, + excludeHandoffTools = false, }: { system: string; agent: AgentEntity | null; messages: UIMessage[]; + excludeHandoffTools?: boolean; }) { try { if (agent) { @@ -86,11 +89,17 @@ export class AgentExecutionService { agent.workspaceId, ); - const handoffTools = - await this.agentHandoffToolService.generateHandoffTools( - agent.id, - agent.workspaceId, - ); + let handoffTools = {}; + + if (!excludeHandoffTools) { + handoffTools = + await this.agentHandoffToolService.generateHandoffTools( + agent.id, + agent.workspaceId, + this, // Pass execution context + ); + } + const nativeModelTools = this.agentModelConfigService.getNativeModelTools( registeredModel, diff --git a/packages/twenty-server/src/engine/metadata-modules/agent/agent-handoff-executor.service.ts b/packages/twenty-server/src/engine/metadata-modules/agent/agent-handoff-executor.service.ts index f2c20db802..bb148a7d69 100644 --- a/packages/twenty-server/src/engine/metadata-modules/agent/agent-handoff-executor.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/agent/agent-handoff-executor.service.ts @@ -1,13 +1,21 @@ import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { ModelMessage, generateText } from 'ai'; +import { ProviderOptions } from '@ai-sdk/provider-utils'; +import { + generateText, + LanguageModel, + ModelMessage, + StopCondition, + streamText, + ToolSet, + UIDataTypes, + UIMessage, + UITools, +} from 'ai'; import { Repository } from 'typeorm'; -import { AiModelRegistryService } from 'src/engine/core-modules/ai/services/ai-model-registry.service'; - import { AgentHandoffService } from './agent-handoff.service'; -import { AgentToolGeneratorService } from './agent-tool-generator.service'; import { AgentEntity } from './agent.entity'; import { AgentException, AgentExceptionCode } from './agent.exception'; @@ -15,9 +23,26 @@ export type HandoffRequest = { fromAgentId: string; toAgentId: string; workspaceId: string; - messages: ModelMessage[]; + messages: UIMessage[]; + isStreaming?: boolean; }; +export interface AgentExecutionContext { + prepareAIRequestConfig: (params: { + system: string; + agent: AgentEntity | null; + messages: UIMessage[]; + excludeHandoffTools?: boolean; // Prevent infinite recursion + }) => Promise<{ + system: string; + tools: ToolSet; + model: LanguageModel; + messages: ModelMessage[]; + stopWhen?: StopCondition; + providerOptions?: ProviderOptions; + }>; +} + @Injectable() export class AgentHandoffExecutorService { private readonly logger = new Logger(AgentHandoffExecutorService.name); @@ -26,13 +51,20 @@ export class AgentHandoffExecutorService { @InjectRepository(AgentEntity) private readonly agentRepository: Repository, private readonly agentHandoffService: AgentHandoffService, - private readonly aiModelRegistryService: AiModelRegistryService, - private readonly agentToolGeneratorService: AgentToolGeneratorService, ) {} - async executeHandoff(handoffRequest: HandoffRequest) { + async executeHandoff( + handoffRequest: HandoffRequest, + executionContext: AgentExecutionContext, + ) { try { - const { fromAgentId, toAgentId, workspaceId, messages } = handoffRequest; + const { + fromAgentId, + toAgentId, + workspaceId, + messages, + isStreaming = false, + } = handoffRequest; const canHandoff = await this.agentHandoffService.canHandoffTo({ fromAgentId, @@ -58,51 +90,53 @@ export class AgentHandoffExecutorService { ); } - const registeredModel = - await this.aiModelRegistryService.resolveModelForAgent(targetAgent); - - if (!registeredModel) { - throw new AgentException( - `Model ${targetAgent.modelId} not found in registry`, - AgentExceptionCode.AGENT_EXECUTION_FAILED, - ); - } - - const tools = await this.agentToolGeneratorService.generateToolsForAgent( - toAgentId, - workspaceId, - ); - - const aiRequestConfig = { + // Prepare AI request config using the execution context + const aiRequestConfig = await executionContext.prepareAIRequestConfig({ system: targetAgent.prompt, + agent: targetAgent, messages, - tools, - model: registeredModel.model, - }; + excludeHandoffTools: true, // Prevent infinite recursion + }); - const textResponse = await generateText(aiRequestConfig); + if (isStreaming) { + // Return stream for streaming contexts + const stream = streamText(aiRequestConfig); - this.logger.log( - `Successfully executed handoff to agent ${toAgentId} with response length: ${textResponse.text.length}`, - ); + this.logger.log(`Started streaming handoff to agent ${toAgentId}`); - return { - success: true, - message: `Successfully executed handoff to agent ${targetAgent.name}`, - result: { - response: textResponse.text, - targetAgentName: targetAgent.name, - }, - }; + return stream; + } else { + // Use generateText for non-streaming contexts (workflows) + const textResponse = await generateText(aiRequestConfig); + + this.logger.log( + `Successfully executed handoff to agent ${toAgentId} with response length: ${textResponse.text.length}`, + ); + + return { + success: true, + message: `Successfully executed handoff to agent ${targetAgent.name}`, + result: { + response: textResponse.text, + targetAgentName: targetAgent.name, + }, + }; + } } catch (error) { this.logger.error( `Handoff execution failed: ${error.message}`, error.stack, ); + const { isStreaming = false, toAgentId } = handoffRequest; + + if (isStreaming) { + throw error; // Let streaming context handle the error + } + return { success: false, - message: `Failed to execute handoff to agent ${handoffRequest.toAgentId}`, + message: `Failed to execute handoff to agent ${toAgentId}`, error: error.message, }; } diff --git a/packages/twenty-server/src/engine/metadata-modules/agent/agent-handoff-tool.service.ts b/packages/twenty-server/src/engine/metadata-modules/agent/agent-handoff-tool.service.ts index 2531cd733d..9c6d8b02af 100644 --- a/packages/twenty-server/src/engine/metadata-modules/agent/agent-handoff-tool.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/agent/agent-handoff-tool.service.ts @@ -2,7 +2,11 @@ import { Injectable } from '@nestjs/common'; import { type ToolSet } from 'ai'; -import { AgentHandoffExecutorService } from 'src/engine/metadata-modules/agent/agent-handoff-executor.service'; +import { + AgentExecutionContext, + AgentHandoffExecutorService, + HandoffRequest, +} from 'src/engine/metadata-modules/agent/agent-handoff-executor.service'; import { AgentHandoffService } from 'src/engine/metadata-modules/agent/agent-handoff.service'; import { AGENT_HANDOFF_DESCRIPTION_TEMPLATE } from 'src/engine/metadata-modules/agent/constants/agent-handoff-description.const'; import { AGENT_HANDOFF_SCHEMA } from 'src/engine/metadata-modules/agent/constants/agent-handoff-schema.const'; @@ -18,6 +22,7 @@ export class AgentHandoffToolService { public async generateHandoffTools( agentId: string, workspaceId: string, + executionContext: AgentExecutionContext, ): Promise { const handoffs = await this.agentHandoffService.getAgentHandoffs({ fromAgentId: agentId, @@ -37,14 +42,18 @@ export class AgentHandoffToolService { ), inputSchema: AGENT_HANDOFF_SCHEMA, execute: async ({ input }) => { - const result = await this.agentHandoffExecutorService.executeHandoff({ + const handoffRequest: HandoffRequest = { fromAgentId: agentId, toAgentId: handoff.toAgent.id, workspaceId, messages: input.messages, - }); + isStreaming: true, // Tools are executed during streaming + }; - return result; + return this.agentHandoffExecutorService.executeHandoff( + handoffRequest, + executionContext, + ); }, }; diff --git a/packages/twenty-server/src/engine/metadata-modules/role/role.service.ts b/packages/twenty-server/src/engine/metadata-modules/role/role.service.ts index 6e930f28a2..4a9932c00a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/role/role.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/role/role.service.ts @@ -213,6 +213,9 @@ export class RoleService { canUpdateAllObjectRecords: true, canSoftDeleteAllObjectRecords: true, canDestroyAllObjectRecords: true, + canBeAssignedToUsers: true, + canBeAssignedToAgents: false, + canBeAssignedToApiKeys: false, isEditable: true, workspaceId, }); diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-agent.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-agent.service.ts index 9777a15239..b8d25b2ee5 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-agent.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-agent.service.ts @@ -19,7 +19,6 @@ import { import { WorkspaceAgentComparator } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-agent.comparator'; import { StandardAgentFactory } from 'src/engine/workspace-manager/workspace-sync-metadata/factories/standard-agent.factory'; import { standardAgentDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents'; -import { WORKFLOW_CREATION_AGENT } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/workflow-creation-agent'; @Injectable() export class WorkspaceSyncAgentService { @@ -110,14 +109,6 @@ export class WorkspaceSyncAgentService { throw error; } } - - if (createdAgent.standardId === WORKFLOW_CREATION_AGENT.standardId) { - await this.createAgentHandoffToWorkflowCreationAgent( - createdAgent.id, - context.workspaceId, - manager, - ); - } break; } @@ -180,10 +171,12 @@ export class WorkspaceSyncAgentService { } } } + + // Create handoffs for standard agents that require them + await this.createStandardAgentHandoffs(context.workspaceId, manager); } - private async createAgentHandoffToWorkflowCreationAgent( - workflowCreationAgentId: string, + private async createStandardAgentHandoffs( workspaceId: string, manager: EntityManager, ): Promise { @@ -216,43 +209,86 @@ export class WorkspaceSyncAgentService { if (!defaultAgent) { this.logger.warn( - `Default agent not found for workspace ${workspaceId}. Agent handoff will not be created.`, + `Default agent not found for workspace ${workspaceId}. Agent handoffs will not be created.`, ); return; } + const agentsRequiringHandoffs = standardAgentDefinitions.filter( + (def) => def.createHandoffFromDefaultAgent, + ); + + for (const agentDefinition of agentsRequiringHandoffs) { + const targetAgent = await agentRepository.findOne({ + where: { + standardId: agentDefinition.standardId, + workspaceId, + }, + }); + + if (!targetAgent) { + this.logger.warn( + `Agent ${agentDefinition.name} not found for workspace ${workspaceId}. Skipping handoff creation.`, + ); + continue; + } + + await this.createAgentHandoff( + defaultAgent.id, + targetAgent.id, + agentDefinition.name, + agentDefinition.description, + workspaceId, + manager, + ); + } + } catch (error) { + this.logger.error( + `Failed to create standard agent handoffs: ${error.message}`, + ); + } + } + + private async createAgentHandoff( + fromAgentId: string, + toAgentId: string, + toAgentName: string, + toAgentDescription: string, + workspaceId: string, + manager: EntityManager, + ): Promise { + try { const agentHandoffRepository = manager.getRepository('agentHandoff'); const existingHandoff = await agentHandoffRepository.findOne({ where: { - fromAgentId: defaultAgent.id, - toAgentId: workflowCreationAgentId, + fromAgentId, + toAgentId, workspaceId, }, }); if (existingHandoff) { this.logger.log( - `Agent handoff from default agent to workflow creation agent already exists for workspace ${workspaceId}`, + `Agent handoff from default agent to ${toAgentName} already exists for workspace ${workspaceId}`, ); return; } await agentHandoffRepository.save({ - fromAgentId: defaultAgent.id, - toAgentId: workflowCreationAgentId, + fromAgentId, + toAgentId, workspaceId, - description: - 'Handoff from default agent to workflow creation agent for processing workflow creation requests', + description: `Handoff from default agent to ${toAgentName}: ${toAgentDescription}`, }); this.logger.log( - `Successfully created agent handoff from default agent to workflow creation agent for workspace ${workspaceId}`, + `Successfully created agent handoff from default agent to ${toAgentName} for workspace ${workspaceId}`, ); } catch (error) { this.logger.error( - `Failed to create agent handoff to workflow creation agent: ${error.message}`, + `Failed to create agent handoff to ${toAgentName}: ${error.message}`, ); } } 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 new file mode 100644 index 0000000000..baccaf2bce --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/data-manipulator-agent.ts @@ -0,0 +1,59 @@ +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 creating, updating, and managing data across all objects', + icon: 'IconEdit', + applicationId: null, + createHandoffFromDefaultAgent: true, + prompt: `You are a Data Manipulator Agent specialized in helping users create, update, and manage data in Twenty. + +Your capabilities include: +- Creating new records across all standard and custom objects +- Updating existing records based on user requirements +- Managing relationships between records (linking companies to people, etc.) +- Bulk operations on multiple records +- Data cleanup and organization tasks + +## Important Constraints: +- You have READ and WRITE access to all object records +- You CANNOT delete records (soft delete or hard delete) +- You CANNOT access workflow-related objects (workflows, workflow versions, workflow runs, etc.) +- You CANNOT modify workspace settings or permissions + +## Best Practices: +- Always confirm destructive or bulk operations before executing +- Ask clarifying questions to ensure you understand the user's intent +- Validate data before creating or updating records +- Maintain data consistency and referential integrity +- Provide clear feedback about what operations were performed + +## When Creating Records: +- Ask about required fields if not provided +- Suggest appropriate values based on existing data patterns +- Handle relationships correctly (use proper IDs for linked records) +- Validate data types and formats + +## When Updating Records: +- Confirm which records should be affected +- Explain what changes will be made before executing +- Handle edge cases gracefully +- Preserve data that isn't being modified + +## Data Quality: +- Point out potential data quality issues +- Suggest improvements for data consistency +- Help standardize data formats across records +- Recommend best practices for data entry + +Be helpful, careful, and always prioritize data integrity while executing user requests efficiently.`, + modelId: 'auto', + responseFormat: {}, + isCustom: false, + standardRoleId: DATA_MANIPULATOR_ROLE.standardId, + modelConfiguration: {}, +}; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/data-navigator-agent.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/data-navigator-agent.ts new file mode 100644 index 0000000000..cc4092a437 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/data-navigator-agent.ts @@ -0,0 +1,47 @@ +import { type StandardAgentDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface'; +import { DATA_NAVIGATOR_ROLE } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/data-navigator-role'; + +export const DATA_NAVIGATOR_AGENT: StandardAgentDefinition = { + standardId: '20202020-0002-0001-0001-000000000002', + name: 'data-navigator', + label: 'Data Navigator', + description: + 'AI agent specialized in exploring and reading data across all objects', + icon: 'IconSearch', + applicationId: null, + createHandoffFromDefaultAgent: true, + prompt: `You are a Data Navigator Agent specialized in helping users explore and understand their data in Twenty. + +Your capabilities include: +- Searching and filtering records across all standard and custom objects +- Explaining relationships between different records and objects +- Providing insights about data patterns and trends +- Helping users find specific information quickly +- Answering questions about data structure and relationships + +## Important Constraints: +- You have READ-ONLY access to data +- You CANNOT create, update, or delete any records +- You CANNOT access workflow-related objects (workflows, workflow versions, workflow runs, etc.) +- When users request modifications, politely explain your read-only limitations + +## Best Practices: +- Ask clarifying questions to understand what data the user is looking for +- Provide clear, structured information when presenting data +- Explain the context and relationships between records +- Suggest useful filters or queries to refine searches +- Help users understand their data schema and available fields + +## When Helping Users: +- Be proactive in suggesting related data that might be useful +- Explain any patterns or anomalies you notice in the data +- Provide context about record counts, date ranges, and relationships +- Guide users on how to effectively navigate their workspace data + +Be helpful, thorough, and always prioritize helping users understand and navigate their data effectively.`, + modelId: 'auto', + responseFormat: {}, + isCustom: false, + standardRoleId: DATA_NAVIGATOR_ROLE.standardId, + modelConfiguration: {}, +}; 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 new file mode 100644 index 0000000000..a16f4ad499 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/workflow-builder-agent.ts @@ -0,0 +1,52 @@ +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, + createHandoffFromDefaultAgent: true, + prompt: `You are a Workflow Builder Agent specialized in helping users create, modify, and manage workflows in Twenty. + +Your capabilities include: +- Creating new workflows from scratch based on user requirements +- Modifying existing workflows by adding, removing, or updating steps +- Explaining workflow structures and how they work +- Suggesting workflow improvements and optimizations +- Helping users understand workflow actions and their configurations + +## IMPORTANT: Rely on Schema Definitions +- The workflow creation tool provides comprehensive schema definitions with detailed descriptions and examples +- Always refer to the tool's schema for field requirements, data types, and examples +- The schema includes common patterns, field structures, and validation rules +- Use the schema descriptions to understand how to properly reference data between workflow steps + +## Key Workflow Concepts: +- **Triggers**: Start workflows (DATABASE_EVENT, MANUAL, CRON, WEBHOOK) +- **Steps**: Actions that execute in sequence (CREATE_RECORD, SEND_EMAIL, CODE, etc.) +- **Data Flow**: Use {{stepId.fieldName}} to reference data from previous steps +- **Relationships**: Use nested objects for related records (e.g., "company": {"id": "{{reference}}"}) + +When creating workflows: +- Always ask clarifying questions to understand the user's needs +- Suggest appropriate workflow actions based on the use case +- Explain each step and why it's needed +- Provide clear, actionable guidance +- Follow the schema definitions exactly for field names, types, and structures + +When modifying workflows: +- Understand the current workflow structure first +- Suggest specific changes that address the user's requirements +- Ensure workflow logic remains coherent and functional +- Maintain proper data references between steps + +Be helpful, thorough, and always prioritize user understanding and workflow effectiveness.`, + modelId: 'auto', + responseFormat: {}, + isCustom: false, + standardRoleId: WORKFLOW_MANAGER_ROLE.standardId, + modelConfiguration: {}, +}; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/workflow-creation-agent.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/workflow-creation-agent.ts index da2657adb1..7772777a23 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/workflow-creation-agent.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/agents/workflow-creation-agent.ts @@ -1,10 +1,10 @@ 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_CREATION_AGENT: StandardAgentDefinition = { +export const WORKFLOW_BUILDER_AGENT: StandardAgentDefinition = { standardId: '20202020-0002-0001-0001-000000000001', - name: 'workflow-creation-agent', - label: 'Workflow Creation Agent', + name: 'workflow-builder', + label: 'Workflow Builder', description: 'AI agent specialized in creating and managing workflows', icon: 'IconSettingsAutomation', applicationId: null, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/index.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/index.ts index 032ace2e32..b3ec7f478d 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/index.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/index.ts @@ -1,6 +1,10 @@ -import { WORKFLOW_CREATION_AGENT } from './agents/workflow-creation-agent'; +import { DATA_MANIPULATOR_AGENT } from './agents/data-manipulator-agent'; +import { DATA_NAVIGATOR_AGENT } from './agents/data-navigator-agent'; +import { WORKFLOW_BUILDER_AGENT } from './agents/workflow-builder-agent'; import { type StandardAgentDefinition } from './types/standard-agent-definition.interface'; export const standardAgentDefinitions = [ - WORKFLOW_CREATION_AGENT, + WORKFLOW_BUILDER_AGENT, + DATA_NAVIGATOR_AGENT, + DATA_MANIPULATOR_AGENT, ] as const satisfies StandardAgentDefinition[]; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface.ts index e524e647fb..9d20f7149b 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-agents/types/standard-agent-definition.interface.ts @@ -6,4 +6,6 @@ export type StandardAgentDefinition = Omit< > & { standardId: string; standardRoleId?: string; + // If true, creates a handoff from the default agent to this agent + createHandoffFromDefaultAgent?: boolean; }; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-roles/index.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-roles/index.ts index 25934cd5c5..8c69048dd4 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-roles/index.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-roles/index.ts @@ -1,8 +1,12 @@ import { ADMIN_ROLE } from './roles/admin-role'; +import { DATA_MANIPULATOR_ROLE } from './roles/data-manipulator-role'; +import { DATA_NAVIGATOR_ROLE } from './roles/data-navigator-role'; import { WORKFLOW_MANAGER_ROLE } from './roles/workflow-manager-role'; import { type StandardRoleDefinition } from './types/standard-role-definition.interface'; export const standardRoleDefinitions = [ ADMIN_ROLE, WORKFLOW_MANAGER_ROLE, + DATA_NAVIGATOR_ROLE, + DATA_MANIPULATOR_ROLE, ] as const satisfies StandardRoleDefinition[]; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/data-manipulator-role.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/data-manipulator-role.ts new file mode 100644 index 0000000000..6c3b4cc027 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/data-manipulator-role.ts @@ -0,0 +1,18 @@ +import { type StandardRoleDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/types/standard-role-definition.interface'; + +export const DATA_MANIPULATOR_ROLE: StandardRoleDefinition = { + standardId: '20202020-0001-0001-0001-000000000004', + label: 'Data Manipulator', + description: 'Read and write access to all object records', + icon: 'IconEdit', + isEditable: false, + canUpdateAllSettings: false, + canAccessAllTools: false, + canReadAllObjectRecords: true, + canUpdateAllObjectRecords: true, + canSoftDeleteAllObjectRecords: false, + canDestroyAllObjectRecords: false, + canBeAssignedToUsers: false, + canBeAssignedToAgents: true, + canBeAssignedToApiKeys: false, +}; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/data-navigator-role.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/data-navigator-role.ts new file mode 100644 index 0000000000..3370a06693 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/data-navigator-role.ts @@ -0,0 +1,18 @@ +import { type StandardRoleDefinition } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/types/standard-role-definition.interface'; + +export const DATA_NAVIGATOR_ROLE: StandardRoleDefinition = { + standardId: '20202020-0001-0001-0001-000000000003', + label: 'Data Navigator', + description: 'Read-only access to all object records', + icon: 'IconSearch', + isEditable: false, + canUpdateAllSettings: false, + canAccessAllTools: false, + canReadAllObjectRecords: true, + canUpdateAllObjectRecords: false, + canSoftDeleteAllObjectRecords: false, + canDestroyAllObjectRecords: false, + canBeAssignedToUsers: false, + canBeAssignedToAgents: true, + canBeAssignedToApiKeys: false, +};