From 0893c8531d5e97ceeac634bfc409608c2b7d6508 Mon Sep 17 00:00:00 2001 From: yyh Date: Fri, 13 Feb 2026 12:56:49 +0800 Subject: [PATCH] fix(workflow): lock chatflow start node from type change --- .../hooks/use-available-nodes-meta-data.spec.ts | 2 ++ .../hooks/use-available-nodes-meta-data.ts | 1 + .../workflow/hooks/use-nodes-interactions.ts | 12 +++++++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.spec.ts b/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.spec.ts index bbe1e2e93e..6301c5ab39 100644 --- a/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.spec.ts +++ b/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.spec.ts @@ -102,6 +102,7 @@ describe('workflow-app/useAvailableNodesMetaData', () => { expect(nodeTypes).toContain(BlockEnum.TriggerPlugin) expect(nodeTypes).not.toContain(BlockEnum.Answer) expect(startNode?.metaData.isUndeletable).toBe(false) + expect(startNode?.metaData.isTypeFixed).toBe(false) }) it('should include chatflow-only nodes when chat mode is enabled', () => { @@ -117,6 +118,7 @@ describe('workflow-app/useAvailableNodesMetaData', () => { expect(nodeTypes).not.toContain(BlockEnum.TriggerSchedule) expect(nodeTypes).not.toContain(BlockEnum.TriggerPlugin) expect(startNode?.metaData.isUndeletable).toBe(true) + expect(startNode?.metaData.isTypeFixed).toBe(true) }) it('should hide sandbox-only nodes and keep agent when sandbox is disabled', () => { diff --git a/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts b/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts index 143005da6f..a24fb3d6d6 100644 --- a/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts +++ b/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts @@ -57,6 +57,7 @@ export const useAvailableNodesMetaData = () => { metaData: { ...StartDefault.metaData, isUndeletable: isChatMode, // start node is undeletable in chat mode, @use-nodes-interactions: handleNodeDelete function + isTypeFixed: isChatMode, // start node (user input) is immutable in chatflow mode }, }), [isChatMode]) diff --git a/web/app/components/workflow/hooks/use-nodes-interactions.ts b/web/app/components/workflow/hooks/use-nodes-interactions.ts index ffd4d1cd5f..f1188309c2 100644 --- a/web/app/components/workflow/hooks/use-nodes-interactions.ts +++ b/web/app/components/workflow/hooks/use-nodes-interactions.ts @@ -63,6 +63,7 @@ import { checkMakeGroupAvailability } from './use-make-group' import { useNodesMetaData } from './use-nodes-meta-data' import { useNodesSyncDraft } from './use-nodes-sync-draft' import { + useIsChatMode, useNodesReadOnly, useWorkflow, useWorkflowReadOnly, @@ -326,6 +327,7 @@ export const useNodesInteractions = () => { const { store: workflowHistoryStore } = useWorkflowHistoryStore() const { handleSyncWorkflowDraft } = useNodesSyncDraft() const { getAfterNodesInSameBranch } = useWorkflow() + const isChatMode = useIsChatMode() const { getNodesReadOnly } = useNodesReadOnly() const { getWorkflowReadOnly } = useWorkflowReadOnly() const { handleSetHelpline } = useHelpline() @@ -1963,7 +1965,14 @@ export const useNodesInteractions = () => { return const { nodes, setNodes, edges, setEdges } = collaborativeWorkflow.getState() - const currentNode = nodes.find(node => node.id === currentNodeId)! + const currentNode = nodes.find(node => node.id === currentNodeId) + if (!currentNode) + return + + // In chatflow mode, the Start (user input) node is immutable. + if (isChatMode && currentNode.data.type === BlockEnum.Start) + return + const connectedEdges = getConnectedEdges([currentNode], edges) const nodesWithSameType = nodes.filter( node => node.data.type === nodeType, @@ -2051,6 +2060,7 @@ export const useNodesInteractions = () => { }) }, [ + isChatMode, getNodesReadOnly, collaborativeWorkflow, handleSyncWorkflowDraft,