@@ -268,12 +268,130 @@ function sanitizeSubBlocks(
268268 return sanitized
269269}
270270
271+ /**
272+ * Convert internal condition handle (condition-{uuid}) to semantic format (condition-{blockId}-if)
273+ */
274+ function convertConditionHandleToSemantic (
275+ handle : string ,
276+ blockId : string ,
277+ block : BlockState
278+ ) : string {
279+ if ( ! handle . startsWith ( 'condition-' ) ) {
280+ return handle
281+ }
282+
283+ // Extract the condition UUID from the handle
284+ const conditionId = handle . substring ( 'condition-' . length )
285+
286+ // Get conditions from block subBlocks
287+ const conditionsValue = block . subBlocks ?. conditions ?. value
288+ if ( ! conditionsValue || typeof conditionsValue !== 'string' ) {
289+ return handle
290+ }
291+
292+ let conditions : Array < { id : string ; title : string } >
293+ try {
294+ conditions = JSON . parse ( conditionsValue )
295+ } catch {
296+ return handle
297+ }
298+
299+ if ( ! Array . isArray ( conditions ) ) {
300+ return handle
301+ }
302+
303+ // Find the condition by ID and generate semantic handle
304+ let elseIfCount = 0
305+ for ( const condition of conditions ) {
306+ const title = condition . title ?. toLowerCase ( )
307+ if ( condition . id === conditionId ) {
308+ if ( title === 'if' ) {
309+ return `condition-${ blockId } -if`
310+ } else if ( title === 'else if' ) {
311+ elseIfCount ++
312+ return elseIfCount === 1
313+ ? `condition-${ blockId } -else-if`
314+ : `condition-${ blockId } -else-if-${ elseIfCount } `
315+ } else if ( title === 'else' ) {
316+ return `condition-${ blockId } -else`
317+ }
318+ }
319+ // Count else-ifs as we iterate
320+ if ( title === 'else if' ) {
321+ elseIfCount ++
322+ }
323+ }
324+
325+ // Fallback: return original handle if condition not found
326+ return handle
327+ }
328+
329+ /**
330+ * Convert internal router handle (router-{uuid}) to semantic format (router-{blockId}-route-N)
331+ */
332+ function convertRouterHandleToSemantic (
333+ handle : string ,
334+ blockId : string ,
335+ block : BlockState
336+ ) : string {
337+ if ( ! handle . startsWith ( 'router-' ) ) {
338+ return handle
339+ }
340+
341+ // Extract the route UUID from the handle
342+ const routeId = handle . substring ( 'router-' . length )
343+
344+ // Get routes from block subBlocks
345+ const routesValue = block . subBlocks ?. routes ?. value
346+ if ( ! routesValue || typeof routesValue !== 'string' ) {
347+ return handle
348+ }
349+
350+ let routes : Array < { id : string ; title ?: string } >
351+ try {
352+ routes = JSON . parse ( routesValue )
353+ } catch {
354+ return handle
355+ }
356+
357+ if ( ! Array . isArray ( routes ) ) {
358+ return handle
359+ }
360+
361+ // Find the route by ID and generate semantic handle (1-indexed)
362+ for ( let i = 0 ; i < routes . length ; i ++ ) {
363+ if ( routes [ i ] . id === routeId ) {
364+ return `router-${ blockId } -route-${ i + 1 } `
365+ }
366+ }
367+
368+ // Fallback: return original handle if route not found
369+ return handle
370+ }
371+
372+ /**
373+ * Convert source handle to semantic format for condition and router blocks
374+ */
375+ function convertToSemanticHandle ( handle : string , blockId : string , block : BlockState ) : string {
376+ if ( handle . startsWith ( 'condition-' ) && block . type === 'condition' ) {
377+ return convertConditionHandleToSemantic ( handle , blockId , block )
378+ }
379+
380+ if ( handle . startsWith ( 'router-' ) && block . type === 'router_v2' ) {
381+ return convertRouterHandleToSemantic ( handle , blockId , block )
382+ }
383+
384+ return handle
385+ }
386+
271387/**
272388 * Extract connections for a block from edges and format as operations-style connections
389+ * Converts internal UUID handles to semantic format for training data
273390 */
274391function extractConnectionsForBlock (
275392 blockId : string ,
276- edges : WorkflowState [ 'edges' ]
393+ edges : WorkflowState [ 'edges' ] ,
394+ block : BlockState
277395) : Record < string , string | string [ ] > | undefined {
278396 const connections : Record < string , string [ ] > = { }
279397
@@ -284,9 +402,12 @@ function extractConnectionsForBlock(
284402 return undefined
285403 }
286404
287- // Group by source handle
405+ // Group by source handle (converting to semantic format)
288406 for ( const edge of outgoingEdges ) {
289- const handle = edge . sourceHandle || 'source'
407+ let handle = edge . sourceHandle || 'source'
408+
409+ // Convert internal UUID handles to semantic format
410+ handle = convertToSemanticHandle ( handle , blockId , block )
290411
291412 if ( ! connections [ handle ] ) {
292413 connections [ handle ] = [ ]
@@ -321,7 +442,7 @@ export function sanitizeForCopilot(state: WorkflowState): CopilotWorkflowState {
321442
322443 // Helper to recursively sanitize a block and its children
323444 const sanitizeBlock = ( blockId : string , block : BlockState ) : CopilotBlockState => {
324- const connections = extractConnectionsForBlock ( blockId , state . edges )
445+ const connections = extractConnectionsForBlock ( blockId , state . edges , block )
325446
326447 // For loop/parallel blocks, extract config from block.data instead of subBlocks
327448 let inputs : Record < string , string | number | string [ ] [ ] | object >
0 commit comments