Skip to content

Commit eafbb9f

Browse files
authored
fix(tag-dropdown): exclude downstream blocks in loops and parallel siblings (#3312)
* fix(tag-dropdown): exclude downstream blocks in loops and parallel siblings from reference picker * chore(serializer): remove unused computeAccessibleBlockIds method * chore(block-path-calculator): remove unused calculateAccessibleBlocksForWorkflow method * chore(tag-dropdown): remove no-op loop node filter * fix(tag-dropdown): remove parallel container from accessible references in parallel branches * chore(tag-dropdown): remove no-op starter block filter * fix(tag-dropdown): restore parallel container in accessible references for blocks inside parallel * fix(copilot): exclude downstream loop nodes and parallel siblings from accessible references
1 parent 132fef0 commit eafbb9f

File tree

4 files changed

+6
-118
lines changed

4 files changed

+6
-118
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes.ts

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { useMemo } from 'react'
22
import { useShallow } from 'zustand/react/shallow'
33
import { BlockPathCalculator } from '@/lib/workflows/blocks/block-path-calculator'
44
import { SYSTEM_REFERENCE_PREFIXES } from '@/lib/workflows/sanitization/references'
5-
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
65
import { normalizeName } from '@/executor/constants'
76
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
87
import type { Loop, Parallel } from '@/stores/workflows/workflow/types'
@@ -27,27 +26,12 @@ export function useAccessibleReferencePrefixes(blockId?: string | null): Set<str
2726
const accessibleIds = new Set<string>(ancestorIds)
2827
accessibleIds.add(blockId)
2928

30-
const starterBlock = Object.values(blocks).find((block) => isInputDefinitionTrigger(block.type))
31-
if (starterBlock && ancestorIds.includes(starterBlock.id)) {
32-
accessibleIds.add(starterBlock.id)
33-
}
34-
35-
const loopValues = Object.values(loops as Record<string, Loop>)
36-
loopValues.forEach((loop) => {
37-
if (!loop?.nodes) return
38-
if (loop.nodes.includes(blockId)) {
39-
accessibleIds.add(loop.id) // Add the loop block itself
40-
loop.nodes.forEach((nodeId) => accessibleIds.add(nodeId))
41-
}
29+
Object.values(loops as Record<string, Loop>).forEach((loop) => {
30+
if (loop?.nodes?.includes(blockId)) accessibleIds.add(loop.id)
4231
})
4332

44-
const parallelValues = Object.values(parallels as Record<string, Parallel>)
45-
parallelValues.forEach((parallel) => {
46-
if (!parallel?.nodes) return
47-
if (parallel.nodes.includes(blockId)) {
48-
accessibleIds.add(parallel.id) // Add the parallel block itself
49-
parallel.nodes.forEach((nodeId) => accessibleIds.add(nodeId))
50-
}
33+
Object.values(parallels as Record<string, Parallel>).forEach((parallel) => {
34+
if (parallel?.nodes?.includes(blockId)) accessibleIds.add(parallel.id)
5135
})
5236

5337
const prefixes = new Set<string>()

apps/sim/lib/copilot/orchestrator/tool-executor/workflow-tools/queries.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
loadDeployedWorkflowState,
1515
loadWorkflowFromNormalizedTables,
1616
} from '@/lib/workflows/persistence/utils'
17-
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
1817
import { hasTriggerCapability } from '@/lib/workflows/triggers/trigger-utils'
1918
import { getBlock } from '@/blocks/registry'
2019
import { normalizeName } from '@/executor/constants'
@@ -457,20 +456,9 @@ export async function executeGetBlockUpstreamReferences(
457456
const accessibleIds = new Set<string>(ancestorIds)
458457
accessibleIds.add(blockId)
459458

460-
const starterBlock = Object.values(blocks).find((b) => isInputDefinitionTrigger(b.type))
461-
if (starterBlock && ancestorIds.includes(starterBlock.id)) {
462-
accessibleIds.add(starterBlock.id)
463-
}
464-
465-
containingLoopIds.forEach((loopId) => {
466-
accessibleIds.add(loopId)
467-
loops[loopId]?.nodes?.forEach((nodeId: string) => accessibleIds.add(nodeId))
468-
})
459+
containingLoopIds.forEach((loopId) => accessibleIds.add(loopId))
469460

470-
containingParallelIds.forEach((parallelId) => {
471-
accessibleIds.add(parallelId)
472-
parallels[parallelId]?.nodes?.forEach((nodeId: string) => accessibleIds.add(nodeId))
473-
})
461+
containingParallelIds.forEach((parallelId) => accessibleIds.add(parallelId))
474462

475463
const accessibleBlocks: AccessibleBlockEntry[] = []
476464

apps/sim/lib/workflows/blocks/block-path-calculator.ts

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import type { SerializedWorkflow } from '@/serializer/types'
2-
31
/**
42
* Shared utility for calculating block paths and accessible connections.
53
* Used by both frontend (useBlockConnections) and backend (InputResolver) to ensure consistency.
@@ -65,36 +63,4 @@ export class BlockPathCalculator {
6563

6664
return Array.from(pathNodes)
6765
}
68-
69-
/**
70-
* Calculates accessible blocks for all blocks in a workflow.
71-
* This ensures consistent block reference resolution across frontend and backend.
72-
*
73-
* @param workflow - The serialized workflow
74-
* @returns Map of block ID to Set of accessible block IDs
75-
*/
76-
static calculateAccessibleBlocksForWorkflow(
77-
workflow: SerializedWorkflow
78-
): Map<string, Set<string>> {
79-
const accessibleMap = new Map<string, Set<string>>()
80-
81-
for (const block of workflow.blocks) {
82-
const accessibleBlocks = new Set<string>()
83-
84-
// Find all blocks along paths leading to this block
85-
const pathNodes = BlockPathCalculator.findAllPathNodes(workflow.connections, block.id)
86-
pathNodes.forEach((nodeId) => accessibleBlocks.add(nodeId))
87-
88-
// Only add starter block if it's actually upstream (already in pathNodes)
89-
// Don't add it just because it exists on the canvas
90-
const starterBlock = workflow.blocks.find((b) => b.metadata?.id === 'starter')
91-
if (starterBlock && starterBlock.id !== block.id && pathNodes.includes(starterBlock.id)) {
92-
accessibleBlocks.add(starterBlock.id)
93-
}
94-
95-
accessibleMap.set(block.id, accessibleBlocks)
96-
}
97-
98-
return accessibleMap
99-
}
10066
}

apps/sim/serializer/index.ts

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { createLogger } from '@sim/logger'
22
import type { Edge } from 'reactflow'
3-
import { BlockPathCalculator } from '@/lib/workflows/blocks/block-path-calculator'
43
import type { CanonicalModeOverrides } from '@/lib/workflows/subblocks/visibility'
54
import {
65
buildCanonicalIndex,
@@ -153,13 +152,6 @@ export class Serializer {
153152
const safeLoops = Object.keys(canonicalLoops).length > 0 ? canonicalLoops : loops || {}
154153
const safeParallels =
155154
Object.keys(canonicalParallels).length > 0 ? canonicalParallels : parallels || {}
156-
const accessibleBlocksMap = this.computeAccessibleBlockIds(
157-
blocks,
158-
edges,
159-
safeLoops,
160-
safeParallels
161-
)
162-
163155
if (validateRequired) {
164156
this.validateSubflowsBeforeExecution(blocks, safeLoops, safeParallels)
165157
}
@@ -170,7 +162,6 @@ export class Serializer {
170162
this.serializeBlock(block, {
171163
validateRequired,
172164
allBlocks: blocks,
173-
accessibleBlocksMap,
174165
})
175166
),
176167
connections: edges.map((edge) => ({
@@ -201,7 +192,6 @@ export class Serializer {
201192
options: {
202193
validateRequired: boolean
203194
allBlocks: Record<string, BlockState>
204-
accessibleBlocksMap: Map<string, Set<string>>
205195
}
206196
): SerializedBlock {
207197
// Special handling for subflow blocks (loops, parallels, etc.)
@@ -540,46 +530,6 @@ export class Serializer {
540530
}
541531
}
542532

543-
private computeAccessibleBlockIds(
544-
blocks: Record<string, BlockState>,
545-
edges: Edge[],
546-
loops: Record<string, Loop>,
547-
parallels: Record<string, Parallel>
548-
): Map<string, Set<string>> {
549-
const accessibleMap = new Map<string, Set<string>>()
550-
const simplifiedEdges = edges.map((edge) => ({ source: edge.source, target: edge.target }))
551-
552-
const starterBlock = Object.values(blocks).find((block) => block.type === 'starter')
553-
554-
Object.keys(blocks).forEach((blockId) => {
555-
const ancestorIds = BlockPathCalculator.findAllPathNodes(simplifiedEdges, blockId)
556-
const accessibleIds = new Set<string>(ancestorIds)
557-
accessibleIds.add(blockId)
558-
559-
if (starterBlock && ancestorIds.includes(starterBlock.id)) {
560-
accessibleIds.add(starterBlock.id)
561-
}
562-
563-
Object.values(loops).forEach((loop) => {
564-
if (!loop?.nodes) return
565-
if (loop.nodes.includes(blockId)) {
566-
loop.nodes.forEach((nodeId) => accessibleIds.add(nodeId))
567-
}
568-
})
569-
570-
Object.values(parallels).forEach((parallel) => {
571-
if (!parallel?.nodes) return
572-
if (parallel.nodes.includes(blockId)) {
573-
parallel.nodes.forEach((nodeId) => accessibleIds.add(nodeId))
574-
}
575-
})
576-
577-
accessibleMap.set(blockId, accessibleIds)
578-
})
579-
580-
return accessibleMap
581-
}
582-
583533
private selectToolId(blockConfig: any, params: Record<string, any>): string {
584534
try {
585535
return blockConfig.tools.config?.tool

0 commit comments

Comments
 (0)