diff --git a/packages/twenty-front/src/modules/sse-db-event/hooks/useTriggerOptimisticEffectFromSseUpdateEvents.ts b/packages/twenty-front/src/modules/sse-db-event/hooks/useTriggerOptimisticEffectFromSseUpdateEvents.ts
index 2f1c2d8dcd..df4ce3bddb 100644
--- a/packages/twenty-front/src/modules/sse-db-event/hooks/useTriggerOptimisticEffectFromSseUpdateEvents.ts
+++ b/packages/twenty-front/src/modules/sse-db-event/hooks/useTriggerOptimisticEffectFromSseUpdateEvents.ts
@@ -10,6 +10,7 @@ import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
import { useRefetchAggregateQueriesForObjectMetadataItem } from '@/object-record/hooks/useRefetchAggregateQueriesForObjectMetadataItem';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
+import { computeOptimisticRecordFromInput } from '@/object-record/utils/computeOptimisticRecordFromInput';
import { useCallback } from 'react';
import { isDefined, isNonEmptyArray } from 'twenty-shared/utils';
import {
@@ -77,8 +78,14 @@ export const useTriggerOptimisticEffectFromSseUpdateEvents = () => {
}
const computedOptimisticRecord = {
- ...cachedRecord,
- ...updatedRecord,
+ ...computeOptimisticRecordFromInput({
+ cache: apolloCoreClient.cache,
+ objectMetadataItem,
+ objectMetadataItems,
+ recordInput: updatedRecord,
+ objectPermissionsByObjectMetadataId,
+ currentWorkspaceMember: null,
+ }),
id: updatedRecord.id,
__typename: getObjectTypename(objectMetadataItem.nameSingular),
};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowCard.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowCard.tsx
index d7c6646b52..32ff76b5d9 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowCard.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowCard.tsx
@@ -2,11 +2,17 @@ import { useTargetRecord } from '@/ui/layout/contexts/useTargetRecord';
import { getWorkflowVisualizerComponentInstanceId } from '@/workflow/utils/getWorkflowVisualizerComponentInstanceId';
import { WorkflowDiagramCanvasEditable } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable';
import { WorkflowDiagramEffect } from '@/workflow/workflow-diagram/components/WorkflowDiagramEffect';
+import { WorkflowSSESubscribeEffect } from '@/workflow/workflow-diagram/components/WorkflowSSESubscribeEffect';
import { WorkflowVisualizerEffect } from '@/workflow/workflow-diagram/components/WorkflowVisualizerEffect';
import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext';
+import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
+import { FeatureFlagKey } from '~/generated/graphql';
export const WorkflowCard = () => {
const targetRecord = useTargetRecord();
+ const isSseDbEventsEnabled = useIsFeatureEnabled(
+ FeatureFlagKey.IS_SSE_DB_EVENTS_ENABLED,
+ );
return (
{
}}
>
+ {isSseDbEventsEnabled && (
+
+ )}
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowSSESubscribeEffect.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowSSESubscribeEffect.tsx
new file mode 100644
index 0000000000..3b19953fda
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowSSESubscribeEffect.tsx
@@ -0,0 +1,24 @@
+import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
+import { useListenToObjectRecordEventsForQuery } from '@/sse-db-event/hooks/useListenToObjectRecordEventsForQuery';
+
+export const WorkflowSSESubscribeEffect = ({
+ workflowId,
+}: {
+ workflowId: string;
+}) => {
+ const queryId = `workflow-versions-for-workflow-${workflowId}`;
+
+ useListenToObjectRecordEventsForQuery({
+ queryId,
+ operationSignature: {
+ objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
+ variables: {
+ filter: {
+ workflowId: { eq: workflowId },
+ },
+ },
+ },
+ });
+
+ return null;
+};
diff --git a/packages/twenty-server/src/engine/core-modules/workflow/dtos/update-workflow-version-trigger-input.dto.ts b/packages/twenty-server/src/engine/core-modules/workflow/dtos/update-workflow-version-trigger-input.dto.ts
new file mode 100644
index 0000000000..f16acf62b4
--- /dev/null
+++ b/packages/twenty-server/src/engine/core-modules/workflow/dtos/update-workflow-version-trigger-input.dto.ts
@@ -0,0 +1,21 @@
+import { Field, InputType } from '@nestjs/graphql';
+
+import graphqlTypeJson from 'graphql-type-json';
+
+import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
+import { WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
+
+@InputType()
+export class UpdateWorkflowVersionTriggerInput {
+ @Field(() => UUIDScalarType, {
+ description: 'Workflow version ID',
+ nullable: false,
+ })
+ workflowVersionId: string;
+
+ @Field(() => graphqlTypeJson, {
+ description: 'Trigger to update in JSON format',
+ nullable: false,
+ })
+ trigger: WorkflowTrigger;
+}
diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.module.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.module.ts
index 54fc35e8d6..e3fff9f610 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.module.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.module.ts
@@ -45,6 +45,7 @@ import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workfl
exports: [
WorkflowVersionStepWorkspaceService,
WorkflowVersionStepOperationsWorkspaceService,
+ WorkflowVersionStepHelpersWorkspaceService,
],
})
export class WorkflowVersionStepModule {}
diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/services/workflow-tool.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/services/workflow-tool.workspace-service.ts
index 63937d7d86..168011ab26 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-tools/services/workflow-tool.workspace-service.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-tools/services/workflow-tool.workspace-service.ts
@@ -8,6 +8,7 @@ import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspac
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service';
import { WorkflowVersionEdgeWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service';
+import { WorkflowVersionStepHelpersWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-helpers.workspace-service';
import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service';
import { WorkflowVersionWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service';
import {
@@ -26,6 +27,7 @@ import { createDeleteWorkflowVersionStepTool } from 'src/modules/workflow/workfl
import { createGetWorkflowCurrentVersionTool } from 'src/modules/workflow/workflow-tools/tools/get-workflow-current-version.tool';
import { createUpdateWorkflowVersionPositionsTool } from 'src/modules/workflow/workflow-tools/tools/update-workflow-version-positions.tool';
import { createUpdateWorkflowVersionStepTool } from 'src/modules/workflow/workflow-tools/tools/update-workflow-version-step.tool';
+import { createUpdateWorkflowVersionTriggerTool } from 'src/modules/workflow/workflow-tools/tools/update-workflow-version-trigger.tool';
import { type WorkflowToolDependencies } from 'src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type';
import { WorkflowTriggerWorkspaceService } from 'src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service';
@@ -36,6 +38,7 @@ export class WorkflowToolWorkspaceService {
constructor(
workflowVersionStepService: WorkflowVersionStepWorkspaceService,
+ workflowVersionStepHelpersService: WorkflowVersionStepHelpersWorkspaceService,
workflowVersionEdgeService: WorkflowVersionEdgeWorkspaceService,
workflowVersionService: WorkflowVersionWorkspaceService,
workflowTriggerService: WorkflowTriggerWorkspaceService,
@@ -46,6 +49,7 @@ export class WorkflowToolWorkspaceService {
) {
this.deps = {
workflowVersionStepService,
+ workflowVersionStepHelpersService,
workflowVersionEdgeService,
workflowVersionService,
workflowTriggerService,
@@ -79,6 +83,10 @@ export class WorkflowToolWorkspaceService {
this.deps,
context,
);
+ const updateWorkflowVersionTrigger = createUpdateWorkflowVersionTriggerTool(
+ this.deps,
+ context,
+ );
const deleteWorkflowVersionStep = createDeleteWorkflowVersionStepTool(
this.deps,
context,
@@ -116,6 +124,7 @@ export class WorkflowToolWorkspaceService {
[createCompleteWorkflow.name]: createCompleteWorkflow,
[createWorkflowVersionStep.name]: createWorkflowVersionStep,
[updateWorkflowVersionStep.name]: updateWorkflowVersionStep,
+ [updateWorkflowVersionTrigger.name]: updateWorkflowVersionTrigger,
[deleteWorkflowVersionStep.name]: deleteWorkflowVersionStep,
[createWorkflowVersionEdge.name]: createWorkflowVersionEdge,
[deleteWorkflowVersionEdge.name]: deleteWorkflowVersionEdge,
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 2b882e8bcb..ea04b86533 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
@@ -82,7 +82,7 @@ export const createCreateCompleteWorkflowTool = (
CRITICAL SCHEMA REQUIREMENTS:
- Trigger type MUST be one of: DATABASE_EVENT, MANUAL, CRON, WEBHOOK
- NEVER use "RECORD_CREATED" - this is invalid. Use "DATABASE_EVENT" instead.
-- Each step MUST include: id, name, type, valid, settings
+- Each step MUST include: id (must be a valid UUID), name, type, valid, settings
- CREATE_RECORD actions MUST have objectName and objectRecord in settings.input
- objectRecord must contain actual field values, not just field names
- Use "trigger" as stepId for trigger step in stepPositions and edges
@@ -97,7 +97,7 @@ IMPORTANT: The tool schema provides comprehensive field descriptions, examples,
- Field requirements and data types
- Common object patterns and field structures
- Proper relationship field formats
-- Variable reference syntax (e.g., {{trigger.object.fieldName}})
+- Variable reference syntax: {{trigger.fieldName}} for trigger data, {{.result.fieldName}} for step outputs (step-id is the step's UUID, not its name)
- Error handling options
This is the most efficient way for AI to create workflows as it handles all the complexity in one call.`,
diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-workflow-version-step.tool.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-workflow-version-step.tool.ts
index 4f71b028e2..8a338e93f0 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-workflow-version-step.tool.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-workflow-version-step.tool.ts
@@ -1,3 +1,5 @@
+import { isDefined } from 'twenty-shared/utils';
+import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
import { z } from 'zod';
import type { CreateWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/create-workflow-version-step-input.dto';
@@ -17,7 +19,9 @@ const createWorkflowVersionStepSchema = z.object({
parentStepId: z
.string()
.optional()
- .describe('Optional ID of the parent step this step should come after'),
+ .describe(
+ 'Optional ID of the parent step this step should come after. If not provided, the step will be added at the end of the workflow.',
+ ),
parentStepConnectionOptions: z
.object({
type: z.string().optional(),
@@ -36,22 +40,56 @@ const createWorkflowVersionStepSchema = z.object({
})
.optional()
.describe('Optional position coordinates for the step'),
- id: z.string().optional().describe('Optional step ID'),
});
export const createCreateWorkflowVersionStepTool = (
- deps: Pick,
+ deps: Pick<
+ WorkflowToolDependencies,
+ 'workflowVersionStepService' | 'workflowVersionStepHelpersService'
+ >,
context: WorkflowToolContext,
) => ({
name: 'create_workflow_version_step' as const,
description:
- 'Create a new step in a workflow version. This adds a step to the specified workflow version with the given configuration.',
+ 'Create a new step in a workflow version. This adds a step to the specified workflow version with the given configuration. If parentStepId is not provided, the step will be appended at the end of the workflow.',
inputSchema: createWorkflowVersionStepSchema,
execute: async (parameters: CreateWorkflowVersionStepInput) => {
try {
+ let effectiveParentStepId = parameters.parentStepId;
+
+ if (!isDefined(effectiveParentStepId)) {
+ const workflowVersion =
+ await deps.workflowVersionStepHelpersService.getValidatedDraftWorkflowVersion(
+ {
+ workflowVersionId: parameters.workflowVersionId,
+ workspaceId: context.workspaceId,
+ },
+ );
+
+ const steps = workflowVersion.steps ?? [];
+
+ if (steps.length === 0) {
+ effectiveParentStepId = TRIGGER_STEP_ID;
+ } else {
+ const leafStep = steps.filter(
+ (step) =>
+ !isDefined(step.nextStepIds) || step.nextStepIds.length === 0,
+ );
+
+ if (leafStep.length > 1) {
+ effectiveParentStepId = undefined;
+ } else {
+ effectiveParentStepId = leafStep[0]?.id;
+ }
+ }
+ }
+
return await deps.workflowVersionStepService.createWorkflowVersionStep({
workspaceId: context.workspaceId,
- input: parameters,
+ input: {
+ ...parameters,
+ parentStepId: effectiveParentStepId,
+ },
});
} catch (error) {
return {
diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/update-workflow-version-step.tool.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/update-workflow-version-step.tool.ts
index 3256e4fc52..962d313ab0 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/update-workflow-version-step.tool.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/update-workflow-version-step.tool.ts
@@ -1,7 +1,4 @@
-import {
- workflowActionSchema,
- workflowTriggerSchema,
-} from 'twenty-shared/workflow';
+import { workflowActionSchema } from 'twenty-shared/workflow';
import { z } from 'zod';
import type { UpdateWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-version-step-input.dto';
@@ -15,7 +12,7 @@ const updateWorkflowVersionStepSchema = z.object({
.string()
.describe('The ID of the workflow version containing the step'),
step: z
- .union([workflowTriggerSchema, workflowActionSchema])
+ .union([workflowActionSchema])
.describe('The updated step configuration'),
});
diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/update-workflow-version-trigger.tool.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/update-workflow-version-trigger.tool.ts
new file mode 100644
index 0000000000..d5d495eb51
--- /dev/null
+++ b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/update-workflow-version-trigger.tool.ts
@@ -0,0 +1,51 @@
+import { workflowTriggerSchema } from 'twenty-shared/workflow';
+import { z } from 'zod';
+
+import type { UpdateWorkflowVersionTriggerInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-version-trigger-input.dto';
+import {
+ type WorkflowToolContext,
+ type WorkflowToolDependencies,
+} from 'src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type';
+
+const updateWorkflowVersionTriggerSchema = z.object({
+ workflowVersionId: z
+ .string()
+ .describe('The ID of the workflow version containing the trigger'),
+ trigger: workflowTriggerSchema.describe('The updated trigger configuration'),
+});
+
+export const createUpdateWorkflowVersionTriggerTool = (
+ deps: Pick,
+ context: WorkflowToolContext,
+) => ({
+ name: 'update_workflow_version_trigger' as const,
+ description:
+ 'Update the trigger of a workflow version. This modifies the trigger configuration (e.g., changing trigger type, settings, or conditions).',
+ inputSchema: updateWorkflowVersionTriggerSchema,
+ execute: async (parameters: UpdateWorkflowVersionTriggerInput) => {
+ try {
+ await deps.workflowVersionStepHelpersService.getValidatedDraftWorkflowVersion(
+ {
+ workflowVersionId: parameters.workflowVersionId,
+ workspaceId: context.workspaceId,
+ },
+ );
+
+ await deps.workflowVersionStepHelpersService.updateWorkflowVersionStepsAndTrigger(
+ {
+ workspaceId: context.workspaceId,
+ workflowVersionId: parameters.workflowVersionId,
+ trigger: parameters.trigger,
+ },
+ );
+
+ return parameters.trigger;
+ } catch (error) {
+ return {
+ success: false,
+ error: error.message,
+ message: `Failed to update workflow version trigger: ${error.message}`,
+ };
+ }
+ },
+});
diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type.ts
index 37e5ac2a8b..adb710c13f 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-tools/types/workflow-tool-dependencies.type.ts
@@ -2,12 +2,14 @@ import type { RecordPositionService } from 'src/engine/core-modules/record-posit
import type { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import type { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service';
import type { WorkflowVersionEdgeWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service';
+import type { WorkflowVersionStepHelpersWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-helpers.workspace-service';
import type { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service';
import type { WorkflowVersionWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service';
import type { WorkflowTriggerWorkspaceService } from 'src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service';
export type WorkflowToolDependencies = {
workflowVersionStepService: WorkflowVersionStepWorkspaceService;
+ workflowVersionStepHelpersService: WorkflowVersionStepHelpersWorkspaceService;
workflowVersionEdgeService: WorkflowVersionEdgeWorkspaceService;
workflowVersionService: WorkflowVersionWorkspaceService;
workflowTriggerService: WorkflowTriggerWorkspaceService;