@@ -5,7 +5,7 @@ import { z } from 'zod'
55import { env } from '@/lib/env'
66import { createLogger } from '@/lib/logs/console/logger'
77import { db } from '@/db'
8- import { workflow as workflowTable , workflowCheckpoints } from '@/db/schema'
8+ import { workflow as workflowTable , workflowCheckpoints , customTools } from '@/db/schema'
99import { getAllBlocks , getBlock } from '@/blocks'
1010import type { BlockConfig } from '@/blocks/types'
1111import { generateLoopBlocks , generateParallelBlocks } from '@/stores/workflows/workflow/utils'
@@ -137,6 +137,105 @@ async function createWorkflowCheckpoint(
137137 }
138138}
139139
140+ async function upsertCustomToolsFromBlocks (
141+ userId : string ,
142+ blocks : Record < string , any > ,
143+ requestId : string
144+ ) : Promise < { created : number ; updated : number } > {
145+ try {
146+ // Collect custom tools from all agent blocks
147+ const collected : Array < { title : string ; schema : any ; code : string } > = [ ]
148+
149+ for ( const block of Object . values ( blocks ) ) {
150+ if ( ! block || block . type !== 'agent' ) continue
151+ const toolsSub = block . subBlocks ?. tools
152+ if ( ! toolsSub ) continue
153+
154+ let value = toolsSub . value
155+ if ( ! value ) continue
156+ if ( typeof value === 'string' ) {
157+ try {
158+ value = JSON . parse ( value )
159+ } catch {
160+ continue
161+ }
162+ }
163+ if ( ! Array . isArray ( value ) ) continue
164+
165+ for ( const tool of value ) {
166+ if (
167+ tool &&
168+ tool . type === 'custom-tool' &&
169+ tool . schema &&
170+ tool . schema . function &&
171+ tool . schema . function . name &&
172+ typeof tool . code === 'string'
173+ ) {
174+ collected . push ( { title : tool . title || tool . schema . function . name , schema : tool . schema , code : tool . code } )
175+ }
176+ }
177+ }
178+
179+ if ( collected . length === 0 ) return { created : 0 , updated : 0 }
180+
181+ // Ensure unique by function name
182+ const byName = new Map < string , { title : string ; schema : any ; code : string } > ( )
183+ for ( const t of collected ) {
184+ const name = t . schema . function . name
185+ if ( ! byName . has ( name ) ) byName . set ( name , t )
186+ }
187+
188+ // Load existing user's tools
189+ const existing = await db
190+ . select ( )
191+ . from ( customTools )
192+ . where ( eq ( customTools . userId , userId ) )
193+
194+ const existingByName = new Map < string , ( typeof existing ) [ number ] > ( )
195+ for ( const row of existing ) {
196+ try {
197+ const fnName = ( row . schema as any ) ?. function ?. name
198+ if ( fnName ) existingByName . set ( fnName , row as any )
199+ } catch { }
200+ }
201+
202+ let created = 0
203+ let updated = 0
204+ const now = new Date ( )
205+
206+ // Upsert by function name
207+ for ( const [ name , tool ] of byName . entries ( ) ) {
208+ const match = existingByName . get ( name )
209+ if ( ! match ) {
210+ await db . insert ( customTools ) . values ( {
211+ id : crypto . randomUUID ( ) ,
212+ userId,
213+ title : tool . title ,
214+ schema : tool . schema ,
215+ code : tool . code ,
216+ createdAt : now ,
217+ updatedAt : now ,
218+ } )
219+ created ++
220+ } else {
221+ await db
222+ . update ( customTools )
223+ . set ( { title : tool . title , schema : tool . schema , code : tool . code , updatedAt : now } )
224+ . where ( eq ( customTools . id , match . id ) )
225+ updated ++
226+ }
227+ }
228+
229+ logger . info ( `[${ requestId } ] Upserted custom tools from YAML` , { created, updated } )
230+ return { created, updated }
231+ } catch ( err ) {
232+ logger . warn ( `[${ requestId } ] Failed to upsert custom tools from YAML` , {
233+ error : err instanceof Error ? err . message : String ( err ) ,
234+ } )
235+ return { created : 0 , updated : 0 }
236+ }
237+ }
238+
140239/**
141240 * PUT /api/workflows/[id]/yaml
142241 * Consolidated YAML workflow saving endpoint
@@ -226,7 +325,10 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
226325
227326 const conversionResult = await conversionResponse . json ( )
228327
229- if ( ! conversionResult . success || ! conversionResult . workflowState ) {
328+ const workflowState =
329+ conversionResult . workflowState || ( conversionResult . diff && conversionResult . diff . proposedState )
330+
331+ if ( ! conversionResult . success || ! workflowState ) {
230332 logger . error ( `[${ requestId } ] YAML conversion failed` , {
231333 errors : conversionResult . errors ,
232334 warnings : conversionResult . warnings ,
@@ -239,8 +341,6 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
239341 } )
240342 }
241343
242- const { workflowState } = conversionResult
243-
244344 // Ensure all blocks have required fields
245345 Object . values ( workflowState . blocks ) . forEach ( ( block : any ) => {
246346 if ( block . enabled === undefined ) {
@@ -545,6 +645,9 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
545645 }
546646 newWorkflowState . blocks = sanitizedBlocks
547647
648+ // Upsert custom tools from blocks
649+ await upsertCustomToolsFromBlocks ( userId , newWorkflowState . blocks , requestId )
650+
548651 // Save to database
549652 const saveResult = await saveWorkflowToNormalizedTables ( workflowId , newWorkflowState )
550653
0 commit comments