@@ -72,6 +72,7 @@ import {
7272 workspaceFiles ,
7373} from '@sim/db/schema'
7474import { hashDurableSecretProvenanceValue } from '@/lib/execution/durable-secret-provenance'
75+ import { StorageService } from '@/lib/uploads'
7576import { uploadExecutionFile } from '@/lib/uploads/contexts/execution/execution-file-manager'
7677import {
7778 EXACT_EMPTY_WORKSPACE_FILE_SECRET_PROVENANCE ,
@@ -84,7 +85,13 @@ import { AgentBlockHandler } from '@/executor/handlers/agent/agent-handler'
8485import type { AgentInputs , Message } from '@/executor/handlers/agent/types'
8586import type { ExecutionContext , StreamingExecution , UserFile } from '@/executor/types'
8687import { ResolvedSecretTraceRegistry } from '@/executor/utils/resolved-secret-trace-registry'
88+ import { INLINE_ATTACHMENT_THRESHOLD_BYTES } from '@/providers/attachments'
89+ import {
90+ attachLargeFileRemoteUrls ,
91+ uploadLargeFilesToProvider ,
92+ } from '@/providers/file-attachments.server'
8793import { createAgentStreamPump } from '@/providers/stream-pump'
94+ import type { ProviderRequest } from '@/providers/types'
8895import type { SerializedBlock } from '@/serializer/types'
8996
9097const databaseUrl = process . env . AGENT_MEMORY_TEST_DATABASE_URL
@@ -382,12 +389,14 @@ describe.skipIf(!databaseUrl)(
382389 } )
383390
384391 it . each (
385- ( [ 'openai' , 'anthropic' ] as const ) . flatMap ( ( provider ) =>
386- [ false , true ] . map ( ( streaming ) => ( { provider, streaming } ) )
392+ ( [ 'workspace' , 'mothership' ] as const ) . flatMap ( ( storageContext ) =>
393+ ( [ 'openai' , 'anthropic' ] as const ) . flatMap ( ( provider ) =>
394+ [ false , true ] . map ( ( streaming ) => ( { storageContext, provider, streaming } ) )
395+ )
387396 )
388397 ) (
389- 'deployed chat reads remembered workspace files with $provider, streaming=$streaming' ,
390- async ( { provider, streaming } ) => {
398+ 'deployed chat reads remembered $storageContext files with $provider, streaming=$streaming' ,
399+ async ( { storageContext , provider, streaming } ) => {
391400 if ( ! fixture . database || ! connection ) throw new Error ( 'Missing harness database' )
392401 vi . stubGlobal ( 'fetch' , interceptFetch )
393402 outbound = [ ]
@@ -414,7 +423,7 @@ describe.skipIf(!databaseUrl)(
414423 key,
415424 userId : scope . userId ,
416425 workspaceId : scope . workspaceId ,
417- context : 'workspace' ,
426+ context : storageContext ,
418427 originalName : 'result.pdf' ,
419428 contentType : 'application/pdf' ,
420429 size : buffer . length ,
@@ -487,6 +496,57 @@ describe.skipIf(!databaseUrl)(
487496 input : { key, assertedWorkspaceId : scope . workspaceId } ,
488497 } )
489498 ) . rejects . toThrow ( 'Principal kind system' )
499+
500+ const strictWorkspaceRead = readWorkspaceFileRecordByKey . execute ( {
501+ principal : {
502+ kind : 'workspace_api_key' ,
503+ workspaceId : scope . workspaceId ,
504+ keyId : 'harness-key' ,
505+ } ,
506+ input : { key, assertedWorkspaceId : scope . workspaceId } ,
507+ } )
508+ if ( storageContext === 'mothership' ) {
509+ await expect ( strictWorkspaceRead ) . rejects . toMatchObject ( { code : 'not_found' } )
510+ } else {
511+ await expect ( strictWorkspaceRead ) . resolves . toMatchObject ( { file : { id : record . id } } )
512+ }
513+
514+ /** Exercise large-file authorization with real metadata and delegation before model dispatch. */
515+ const largeFile = { ...file , size : INLINE_ATTACHMENT_THRESHOLD_BYTES + 1 }
516+ const largeRequest : ProviderRequest = {
517+ model : models [ provider ] ,
518+ apiKey : apiKey ( provider ) ,
519+ userId : scope . userId ,
520+ messages : [ { role : 'user' , content : 'Read the attachment' , files : [ largeFile ] } ] ,
521+ }
522+ const cloudStorage = vi . spyOn ( StorageService , 'hasCloudStorage' ) . mockReturnValue ( true )
523+ const presign = vi
524+ . spyOn ( StorageService , 'generatePresignedDownloadUrl' )
525+ . mockResolvedValue ( 'https://storage.example.com/signed' )
526+ try {
527+ await attachLargeFileRemoteUrls ( largeRequest , provider , firstContext )
528+ expect ( presign ) . toHaveBeenCalledWith ( key , 'workspace' , 3600 )
529+ expect ( largeFile . remoteUrl ) . toBe ( 'https://storage.example.com/signed' )
530+ if ( provider === 'openai' ) {
531+ const upload = vi . fn ( async ( url : string , init ?: RequestInit ) => {
532+ expect ( url ) . toBe ( 'https://api.openai.com/v1/files' )
533+ expect ( init ?. body ) . toBeInstanceOf ( FormData )
534+ const body = init ! . body as FormData
535+ const uploaded = body . get ( 'file' ) as File
536+ expect ( Buffer . from ( await uploaded . arrayBuffer ( ) ) ) . toEqual ( buffer )
537+ return Response . json ( { id : 'file-harness' } )
538+ } )
539+ vi . stubGlobal ( 'fetch' , upload )
540+ await uploadLargeFilesToProvider ( largeRequest , provider , firstContext )
541+ expect ( upload ) . toHaveBeenCalledOnce ( )
542+ expect ( largeFile . providerFileId ) . toBe ( 'file-harness' )
543+ }
544+ } finally {
545+ cloudStorage . mockRestore ( )
546+ presign . mockRestore ( )
547+ vi . stubGlobal ( 'fetch' , interceptFetch )
548+ }
549+
490550 expect ( await executeTurn ( firstContext , { ...inputs , files : [ file ] } ) ) . toBe ( 'READY' )
491551 expect ( requestFiles ( outbound [ 0 ] ) ) . toEqual ( [ buffer . toString ( 'base64' ) ] )
492552 const stored = await readConversation ( conversationId )
@@ -529,7 +589,7 @@ describe.skipIf(!databaseUrl)(
529589 report . push ( {
530590 provider,
531591 streaming,
532- workspaceAttachment : true ,
592+ storageContext ,
533593 stored,
534594 controls : {
535595 missingOrigin : 'blocked before HTTP' ,
0 commit comments