99 selectModelSchemaInputPaths ,
1010} from '@/lib/execution/model-input-provenance'
1111import { readAvailableCustomToolByIdOrTitleAsExecutor } from '@/lib/internal/custom-tools/read-available-by-id-or-title'
12+ import { resolveExecutorFileMaterializationContext } from '@/lib/internal/file/materialization-context'
1213import { discoverMcpServerToolsAsExecutor } from '@/lib/internal/mcp/discover-tools'
1314import {
1415 readWorkflowInputFieldsForTool ,
@@ -31,6 +32,7 @@ import {
3132 MODEL_SUPPORTED_IMAGE_MIME_TYPES ,
3233 processFilesToUserFiles ,
3334 type RawFileInput ,
35+ tryInferContextFromKey ,
3436} from '@/lib/uploads/utils/file-utils'
3537import { selectModelBoundFileInputPaths } from '@/lib/uploads/utils/model-input'
3638import { hydrateUserFilesWithBase64 } from '@/lib/uploads/utils/user-file-base64.server'
@@ -63,7 +65,7 @@ import type {
6365 ToolInput ,
6466} from '@/executor/handlers/agent/types'
6567import { parseResponseFormat } from '@/executor/handlers/shared/response-format'
66- import type { BlockHandler , ExecutionContext , StreamingExecution } from '@/executor/types'
68+ import type { BlockHandler , ExecutionContext , StreamingExecution , UserFile } from '@/executor/types'
6769import { collectBlockData } from '@/executor/utils/block-data'
6870import { stringifyJSON } from '@/executor/utils/json'
6971import { projectResolvedSecretDiagnosticContent } from '@/executor/utils/resolved-secret-content-projection'
@@ -1481,7 +1483,6 @@ export class AgentBlockHandler implements BlockHandler {
14811483 throw new Error ( `File attachments are not supported for provider "${ providerId } "` )
14821484 }
14831485
1484- const requestId = ctx . executionId || ctx . workflowId || 'agent-files'
14851486 const nextMessages = [ ...messages ]
14861487
14871488 const inlineMaxBytes = getInlineHydrationMaxBytes ( providerId )
@@ -1493,36 +1494,46 @@ export class AgentBlockHandler implements BlockHandler {
14931494 }
14941495
14951496 const unsafeGeneratedDocumentFiles = new Set < string > ( )
1496- const hydratedFiles = await hydrateUserFilesWithBase64 ( message . files , {
1497- requestId,
1498- workspaceId : ctx . workspaceId ,
1499- workflowId : ctx . workflowId ,
1500- executionId : ctx . executionId ,
1501- largeValueExecutionIds : ctx . largeValueExecutionIds ,
1502- largeValueKeys : ctx . largeValueKeys ,
1503- fileKeys : ctx . fileKeys ,
1504- allowLargeValueWorkflowScope : ctx . allowLargeValueWorkflowScope ,
1505- userId : ctx . userId ,
1506- principal : ctx . principal ,
1507- logger,
1508- maxBytes : inlineMaxBytes ,
1509- onServableFileContributors : async ( file , contributors ) => {
1510- if ( ! ctx . workspaceId ) return
1511- for ( const identity of contributors ) {
1512- const safe = await importWorkspaceFileSecretProvenanceForModelView ( {
1513- workspaceId : ctx . workspaceId ,
1514- identity,
1515- registry : ctx . resolvedSecretTraceRegistry ,
1516- view : 'opaque' ,
1517- ...( ctx . userId ? { actorUserId : ctx . userId } : { } ) ,
1518- } )
1519- if ( ! safe ) {
1520- unsafeGeneratedDocumentFiles . add ( `${ file . key } :${ file . id } ` )
1521- return
1522- }
1523- }
1524- } ,
1497+ const groups = new Map < boolean , Array < { file : UserFile ; index : number } > > ( )
1498+ message . files . forEach ( ( file , index ) => {
1499+ const workspaceFile =
1500+ ctx . principal ?. kind === 'system' && tryInferContextFromKey ( file . key ) === 'workspace'
1501+ const group = groups . get ( workspaceFile ) ?? [ ]
1502+ group . push ( { file, index } )
1503+ groups . set ( workspaceFile , group )
15251504 } )
1505+ const hydratedFiles = [ ...message . files ]
1506+ await Promise . all (
1507+ [ ...groups . values ( ) ] . map ( async ( group ) => {
1508+ const hydrated = await hydrateUserFilesWithBase64 (
1509+ group . map ( ( { file } ) => file ) ,
1510+ {
1511+ ...( await resolveExecutorFileMaterializationContext ( ctx , group [ 0 ] . file ) ) ,
1512+ logger,
1513+ maxBytes : inlineMaxBytes ,
1514+ onServableFileContributors : async ( file , contributors ) => {
1515+ if ( ! ctx . workspaceId ) return
1516+ for ( const identity of contributors ) {
1517+ const safe = await importWorkspaceFileSecretProvenanceForModelView ( {
1518+ workspaceId : ctx . workspaceId ,
1519+ identity,
1520+ registry : ctx . resolvedSecretTraceRegistry ,
1521+ view : 'opaque' ,
1522+ ...( ctx . userId ? { actorUserId : ctx . userId } : { } ) ,
1523+ } )
1524+ if ( ! safe ) {
1525+ unsafeGeneratedDocumentFiles . add ( `${ file . key } :${ file . id } ` )
1526+ return
1527+ }
1528+ }
1529+ } ,
1530+ }
1531+ )
1532+ group . forEach ( ( { index } , fileIndex ) => {
1533+ hydratedFiles [ index ] = hydrated [ fileIndex ]
1534+ } )
1535+ } )
1536+ )
15261537
15271538 const modelSafeHydratedFiles = hydratedFiles . flatMap ( ( file , fileIndex ) => {
15281539 if ( unsafeGeneratedDocumentFiles . has ( `${ file . key } :${ file . id } ` ) ) return [ ]
0 commit comments