@@ -114,15 +114,13 @@ export async function loadDeployedWorkflowState(
114114 resolvedWorkspaceId = wfRow ?. workspaceId ?? undefined
115115 }
116116
117- const resolvedBlocks = state . blocks || { }
118- const { blocks : credMigratedBlocks } = resolvedWorkspaceId
119- ? await migrateCredentialIds ( resolvedBlocks , resolvedWorkspaceId )
120- : { blocks : resolvedBlocks }
121-
122- migrateSubblockIds ( credMigratedBlocks )
117+ const { blocks : migratedBlocks } = await applyBlockMigrations (
118+ state . blocks || { } ,
119+ resolvedWorkspaceId
120+ )
123121
124122 return {
125- blocks : credMigratedBlocks ,
123+ blocks : migratedBlocks ,
126124 edges : state . edges || [ ] ,
127125 loops : state . loops || { } ,
128126 parallels : state . parallels || { } ,
@@ -136,6 +134,50 @@ export async function loadDeployedWorkflowState(
136134 }
137135}
138136
137+ interface MigrationContext {
138+ blocks : Record < string , BlockState >
139+ workspaceId ?: string
140+ migrated : boolean
141+ }
142+
143+ type BlockMigration = ( ctx : MigrationContext ) => MigrationContext | Promise < MigrationContext >
144+
145+ function createMigrationPipeline ( migrations : BlockMigration [ ] ) {
146+ return async (
147+ blocks : Record < string , BlockState > ,
148+ workspaceId ?: string
149+ ) : Promise < { blocks : Record < string , BlockState > ; migrated : boolean } > => {
150+ let ctx : MigrationContext = { blocks, workspaceId, migrated : false }
151+ for ( const migration of migrations ) {
152+ ctx = await migration ( ctx )
153+ }
154+ return { blocks : ctx . blocks , migrated : ctx . migrated }
155+ }
156+ }
157+
158+ const applyBlockMigrations = createMigrationPipeline ( [
159+ ( ctx ) => {
160+ const { blocks } = sanitizeAgentToolsInBlocks ( ctx . blocks )
161+ return { ...ctx , blocks }
162+ } ,
163+
164+ ( ctx ) => ( {
165+ ...ctx ,
166+ blocks : migrateAgentBlocksToMessagesFormat ( ctx . blocks ) ,
167+ } ) ,
168+
169+ async ( ctx ) => {
170+ if ( ! ctx . workspaceId ) return ctx
171+ const { blocks, migrated } = await migrateCredentialIds ( ctx . blocks , ctx . workspaceId )
172+ return { ...ctx , blocks, migrated : ctx . migrated || migrated }
173+ } ,
174+
175+ ( ctx ) => {
176+ const { blocks, migrated } = migrateSubblockIds ( ctx . blocks )
177+ return { ...ctx , blocks, migrated : ctx . migrated || migrated }
178+ } ,
179+ ] )
180+
139181/**
140182 * Migrates agent blocks from old format (systemPrompt/userPrompt) to new format (messages array)
141183 * This ensures backward compatibility for workflows created before the messages-input refactor.
@@ -359,22 +401,16 @@ export async function loadWorkflowFromNormalizedTables(
359401 blocksMap [ block . id ] = assembled
360402 } )
361403
362- // Sanitize any invalid custom tools in agent blocks to prevent client crashes
363- const { blocks : sanitizedBlocks } = sanitizeAgentToolsInBlocks ( blocksMap )
364-
365- // Migrate old agent block format (systemPrompt/userPrompt) to new messages array format
366- const migratedBlocks = migrateAgentBlocksToMessagesFormat ( sanitizedBlocks )
367-
368- // Migrate legacy account.id → credential.id in OAuth subblocks
369- const { blocks : credMigratedBlocks , migrated : credentialsMigrated } = workflowRow ?. workspaceId
370- ? await migrateCredentialIds ( migratedBlocks , workflowRow . workspaceId )
371- : { blocks : migratedBlocks , migrated : false }
404+ const { blocks : finalBlocks , migrated } = await applyBlockMigrations (
405+ blocksMap ,
406+ workflowRow ?. workspaceId ?? undefined
407+ )
372408
373- if ( credentialsMigrated ) {
409+ if ( migrated ) {
374410 Promise . resolve ( ) . then ( async ( ) => {
375411 try {
376- for ( const [ blockId , block ] of Object . entries ( credMigratedBlocks ) ) {
377- if ( block . subBlocks !== migratedBlocks [ blockId ] ?. subBlocks ) {
412+ for ( const [ blockId , block ] of Object . entries ( finalBlocks ) ) {
413+ if ( block . subBlocks !== blocksMap [ blockId ] ?. subBlocks ) {
378414 await db
379415 . update ( workflowBlocks )
380416 . set ( { subBlocks : block . subBlocks , updatedAt : new Date ( ) } )
@@ -384,23 +420,7 @@ export async function loadWorkflowFromNormalizedTables(
384420 }
385421 }
386422 } catch ( err ) {
387- logger . warn ( 'Failed to persist credential ID migration' , { workflowId, error : err } )
388- }
389- } )
390- }
391-
392- const subblockMigrated = migrateSubblockIds ( credMigratedBlocks )
393- if ( subblockMigrated ) {
394- Promise . resolve ( ) . then ( async ( ) => {
395- try {
396- for ( const [ blockId , block ] of Object . entries ( credMigratedBlocks ) ) {
397- await db
398- . update ( workflowBlocks )
399- . set ( { subBlocks : block . subBlocks , updatedAt : new Date ( ) } )
400- . where ( and ( eq ( workflowBlocks . id , blockId ) , eq ( workflowBlocks . workflowId , workflowId ) ) )
401- }
402- } catch ( err ) {
403- logger . warn ( 'Failed to persist subblock ID migration' , { workflowId, error : err } )
423+ logger . warn ( 'Failed to persist block migrations' , { workflowId, error : err } )
404424 }
405425 } )
406426 }
@@ -441,13 +461,13 @@ export async function loadWorkflowFromNormalizedTables(
441461 forEachItems : ( config as Loop ) . forEachItems ?? '' ,
442462 whileCondition : ( config as Loop ) . whileCondition ?? '' ,
443463 doWhileCondition : ( config as Loop ) . doWhileCondition ?? '' ,
444- enabled : credMigratedBlocks [ subflow . id ] ?. enabled ?? true ,
464+ enabled : finalBlocks [ subflow . id ] ?. enabled ?? true ,
445465 }
446466 loops [ subflow . id ] = loop
447467
448- if ( credMigratedBlocks [ subflow . id ] ) {
449- const block = credMigratedBlocks [ subflow . id ]
450- credMigratedBlocks [ subflow . id ] = {
468+ if ( finalBlocks [ subflow . id ] ) {
469+ const block = finalBlocks [ subflow . id ]
470+ finalBlocks [ subflow . id ] = {
451471 ...block ,
452472 data : {
453473 ...block . data ,
@@ -468,7 +488,7 @@ export async function loadWorkflowFromNormalizedTables(
468488 ( config as Parallel ) . parallelType === 'collection'
469489 ? ( config as Parallel ) . parallelType
470490 : 'count' ,
471- enabled : credMigratedBlocks [ subflow . id ] ?. enabled ?? true ,
491+ enabled : finalBlocks [ subflow . id ] ?. enabled ?? true ,
472492 }
473493 parallels [ subflow . id ] = parallel
474494 } else {
@@ -477,7 +497,7 @@ export async function loadWorkflowFromNormalizedTables(
477497 } )
478498
479499 return {
480- blocks : credMigratedBlocks ,
500+ blocks : finalBlocks ,
481501 edges : edgesArray ,
482502 loops,
483503 parallels,
0 commit comments