From 2b110159c15371f50e0c180b424807cd6bf74ab8 Mon Sep 17 00:00:00 2001 From: Chen Yefan Date: Mon, 23 Mar 2026 18:24:40 +0800 Subject: [PATCH 1/2] fix(workflow): enable source handle connector on End node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The End (Output) node had `availableNextBlocks` hardcoded to `[]`, making `isConnectable = false` on its source handle. This hid the "+" BlockSelector button entirely, preventing users from adding new connections from the Output node — unlike every other regular node. Remove `BlockEnum.End` from the empty-list guard in both the memo and the `getAvailableBlocks` callback in `use-available-blocks.ts`. The container-level filter (`availableBlocksFilter`) that prevents End from being added inside Iteration/Loop containers is unchanged. Co-Authored-By: Claude Sonnet 4.6 --- web/app/components/workflow/hooks/use-available-blocks.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/components/workflow/hooks/use-available-blocks.ts b/web/app/components/workflow/hooks/use-available-blocks.ts index 3d92142b10..888e126474 100644 --- a/web/app/components/workflow/hooks/use-available-blocks.ts +++ b/web/app/components/workflow/hooks/use-available-blocks.ts @@ -30,7 +30,7 @@ export const useAvailableBlocks = (nodeType?: BlockEnum, inContainer?: boolean) return availableNodesType }, [availableNodesType, nodeType]) const availableNextBlocks = useMemo(() => { - if (!nodeType || nodeType === BlockEnum.End || nodeType === BlockEnum.LoopEnd || nodeType === BlockEnum.KnowledgeBase) + if (!nodeType || nodeType === BlockEnum.LoopEnd || nodeType === BlockEnum.KnowledgeBase) return [] return availableNodesType @@ -42,7 +42,7 @@ export const useAvailableBlocks = (nodeType?: BlockEnum, inContainer?: boolean) availablePrevBlocks = [] let availableNextBlocks = availableNodesType - if (!nodeType || nodeType === BlockEnum.End || nodeType === BlockEnum.LoopEnd || nodeType === BlockEnum.KnowledgeBase) + if (!nodeType || nodeType === BlockEnum.LoopEnd || nodeType === BlockEnum.KnowledgeBase) availableNextBlocks = [] return { From f3864f0781ab57df0a78b6360aba98905c06d5e1 Mon Sep 17 00:00:00 2001 From: Chen Yefan Date: Mon, 23 Mar 2026 18:45:38 +0800 Subject: [PATCH 2/2] test(workflow): update use-available-blocks tests for End node behavior End node now returns availableNextBlocks like a regular node. Update two tests that expected an empty array for End node to reflect the new intended behavior. LoopEnd and KnowledgeBase remain restricted. Co-Authored-By: Claude Sonnet 4.6 --- .../hooks/__tests__/use-available-blocks.spec.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/web/app/components/workflow/hooks/__tests__/use-available-blocks.spec.ts b/web/app/components/workflow/hooks/__tests__/use-available-blocks.spec.ts index c89ba9ce96..b8dc614fdd 100644 --- a/web/app/components/workflow/hooks/__tests__/use-available-blocks.spec.ts +++ b/web/app/components/workflow/hooks/__tests__/use-available-blocks.spec.ts @@ -81,9 +81,10 @@ describe('useAvailableBlocks', () => { expect(result.current.availableNextBlocks).toEqual([]) }) - it('should return empty array for End node', () => { + it('should return available blocks for End node (same as regular nodes)', () => { const { result } = renderWorkflowHook(() => useAvailableBlocks(BlockEnum.End), { hooksStoreProps }) - expect(result.current.availableNextBlocks).toEqual([]) + expect(result.current.availableNextBlocks.length).toBeGreaterThan(0) + expect(result.current.availableNextBlocks).toContain(BlockEnum.LLM) }) it('should return empty array for LoopEnd node', () => { @@ -143,14 +144,21 @@ describe('useAvailableBlocks', () => { expect(blocks.availablePrevBlocks).toEqual([]) }) - it('should return empty nextBlocks for End/LoopEnd/KnowledgeBase', () => { + it('should return empty nextBlocks for LoopEnd/KnowledgeBase', () => { const { result } = renderWorkflowHook(() => useAvailableBlocks(BlockEnum.LLM), { hooksStoreProps }) - expect(result.current.getAvailableBlocks(BlockEnum.End).availableNextBlocks).toEqual([]) expect(result.current.getAvailableBlocks(BlockEnum.LoopEnd).availableNextBlocks).toEqual([]) expect(result.current.getAvailableBlocks(BlockEnum.KnowledgeBase).availableNextBlocks).toEqual([]) }) + it('should return available nextBlocks for End node (same as regular nodes)', () => { + const { result } = renderWorkflowHook(() => useAvailableBlocks(BlockEnum.LLM), { hooksStoreProps }) + const blocks = result.current.getAvailableBlocks(BlockEnum.End) + + expect(blocks.availableNextBlocks.length).toBeGreaterThan(0) + expect(blocks.availableNextBlocks).toContain(BlockEnum.LLM) + }) + it('should filter by inContainer when provided', () => { const { result } = renderWorkflowHook(() => useAvailableBlocks(BlockEnum.LLM), { hooksStoreProps }) const blocks = result.current.getAvailableBlocks(BlockEnum.Code, true)