@@ -6,9 +6,12 @@ import { sleep } from '@sim/utils/helpers'
66import { and , eq , inArray , isNull } from 'drizzle-orm'
77import { normalizeStringRecord , normalizeWorkflowVariables } from '@/lib/core/utils/records'
88import { createMcpToolId } from '@/lib/mcp/utils'
9+ import { processFilesToUserFiles , type RawFileInput } from '@/lib/uploads/utils/file-utils'
10+ import { hydrateUserFilesWithBase64 } from '@/lib/uploads/utils/user-file-base64.server'
911import { getCustomToolById } from '@/lib/workflows/custom-tools/operations'
1012import { getAllBlocks } from '@/blocks'
1113import type { BlockOutput } from '@/blocks/types'
14+ import { normalizeFileInput } from '@/blocks/utils'
1215import {
1316 validateBlockType ,
1417 validateCustomToolsAllowed ,
@@ -36,6 +39,7 @@ import { buildAPIUrl, buildAuthHeaders } from '@/executor/utils/http'
3639import { stringifyJSON } from '@/executor/utils/json'
3740import { resolveVertexCredential } from '@/executor/utils/vertex-credential'
3841import { executeProviderRequest } from '@/providers'
42+ import { getAttachmentProvider , getProviderAttachmentMaxBytes } from '@/providers/attachments'
3943import { getProviderFromModel , transformBlockTool } from '@/providers/utils'
4044import type { SerializedBlock } from '@/serializer/types'
4145import { filterSchemaForLLM , type ToolSchema } from '@/tools/params'
@@ -87,12 +91,18 @@ export class AgentBlockHandler implements BlockHandler {
8791
8892 const streamingConfig = this . getStreamingConfig ( ctx , block )
8993 const messages = await this . buildMessages ( ctx , filteredInputs , skillMetadata )
94+ const messagesWithFiles = await this . attachFilesToLastUserMessage (
95+ ctx ,
96+ messages ,
97+ filteredInputs . files ,
98+ providerId
99+ )
90100
91101 const providerRequest = this . buildProviderRequest ( {
92102 ctx,
93103 providerId,
94104 model,
95- messages,
105+ messages : messagesWithFiles ,
96106 inputs : filteredInputs ,
97107 formattedTools,
98108 responseFormat,
@@ -672,6 +682,62 @@ export class AgentBlockHandler implements BlockHandler {
672682 return messages . length > 0 ? messages : undefined
673683 }
674684
685+ private async attachFilesToLastUserMessage (
686+ ctx : ExecutionContext ,
687+ messages : Message [ ] | undefined ,
688+ filesInput : unknown ,
689+ providerId : string
690+ ) : Promise < Message [ ] | undefined > {
691+ const normalizedFiles = normalizeFileInput ( filesInput )
692+ if ( ! normalizedFiles || normalizedFiles . length === 0 ) {
693+ return messages
694+ }
695+
696+ if ( ! messages || messages . length === 0 ) {
697+ throw new Error ( 'Files require at least one user message in the agent prompt' )
698+ }
699+
700+ if ( ! getAttachmentProvider ( providerId ) ) {
701+ throw new Error ( `File attachments are not supported for provider "${ providerId } "` )
702+ }
703+
704+ let lastUserMessageIndex = - 1
705+ for ( let index = messages . length - 1 ; index >= 0 ; index -- ) {
706+ if ( messages [ index ] . role === 'user' ) {
707+ lastUserMessageIndex = index
708+ break
709+ }
710+ }
711+ if ( lastUserMessageIndex === - 1 ) {
712+ throw new Error ( 'Files require at least one user message in the agent prompt' )
713+ }
714+
715+ const requestId = ctx . executionId || ctx . workflowId || 'agent-files'
716+ const userFiles = processFilesToUserFiles ( normalizedFiles as RawFileInput [ ] , requestId , logger )
717+ if ( userFiles . length === 0 ) {
718+ throw new Error ( 'Files must include at least one valid file object' )
719+ }
720+
721+ const hydratedFiles = await hydrateUserFilesWithBase64 ( userFiles , {
722+ requestId,
723+ workspaceId : ctx . workspaceId ,
724+ workflowId : ctx . workflowId ,
725+ executionId : ctx . executionId ,
726+ userId : ctx . userId ,
727+ logger,
728+ maxBytes : getProviderAttachmentMaxBytes ( providerId ) ,
729+ } )
730+
731+ const lastUserMessage = messages [ lastUserMessageIndex ]
732+ const nextMessages = [ ...messages ]
733+ nextMessages [ lastUserMessageIndex ] = {
734+ ...lastUserMessage ,
735+ files : [ ...( lastUserMessage . files ?? [ ] ) , ...hydratedFiles ] ,
736+ }
737+
738+ return nextMessages
739+ }
740+
675741 private extractValidMessages ( messages ?: Message [ ] ) : Message [ ] {
676742 if ( ! messages || ! Array . isArray ( messages ) ) return [ ]
677743
0 commit comments