Skip to content

Commit a5d8ce2

Browse files
committed
File upload fixes
1 parent 24526b7 commit a5d8ce2

File tree

17 files changed

+15032
-12
lines changed

17 files changed

+15032
-12
lines changed

apps/sim/app/api/files/authorization.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ export async function verifyFileAccess(
120120
return true
121121
}
122122

123-
// 1. Workspace files: Check database first (most reliable for both local and cloud)
124-
if (inferredContext === 'workspace') {
123+
// 1. Workspace / mothership files: Check database first (most reliable for both local and cloud)
124+
if (inferredContext === 'workspace' || inferredContext === 'mothership') {
125125
return await verifyWorkspaceFileAccess(cloudKey, userId, customConfig, isLocal)
126126
}
127127

apps/sim/app/api/files/upload/route.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
validateFileType,
1313
} from '@/lib/uploads/utils/validation'
1414
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
15+
import { generateWorkspaceFileKey } from '@/lib/uploads/contexts/workspace/workspace-file-manager'
1516
import {
1617
createErrorResponse,
1718
createOptionsResponse,
@@ -232,6 +233,55 @@ export async function POST(request: NextRequest) {
232233
}
233234
}
234235

236+
// Handle mothership context (chat-scoped uploads to workspace S3)
237+
if (context === 'mothership') {
238+
if (!workspaceId) {
239+
throw new InvalidRequestError('Mothership context requires workspaceId parameter')
240+
}
241+
242+
logger.info(`Uploading mothership file: ${originalName}`)
243+
244+
const storageKey = generateWorkspaceFileKey(workspaceId, originalName)
245+
246+
const metadata: Record<string, string> = {
247+
originalName: originalName,
248+
uploadedAt: new Date().toISOString(),
249+
purpose: 'mothership',
250+
userId: session.user.id,
251+
workspaceId,
252+
}
253+
254+
const fileInfo = await storageService.uploadFile({
255+
file: buffer,
256+
fileName: storageKey,
257+
contentType: file.type || 'application/octet-stream',
258+
context: 'mothership',
259+
preserveKey: true,
260+
customKey: storageKey,
261+
metadata,
262+
})
263+
264+
const finalPath = usingCloudStorage
265+
? `${fileInfo.path}?context=mothership`
266+
: fileInfo.path
267+
268+
uploadResults.push({
269+
fileName: originalName,
270+
presignedUrl: '',
271+
fileInfo: {
272+
path: finalPath,
273+
key: fileInfo.key,
274+
name: originalName,
275+
size: buffer.length,
276+
type: file.type || 'application/octet-stream',
277+
},
278+
directUploadSupported: false,
279+
})
280+
281+
logger.info(`Successfully uploaded mothership file: ${fileInfo.key}`)
282+
continue
283+
}
284+
235285
// Handle copilot, chat, profile-pictures contexts
236286
if (context === 'copilot' || context === 'chat' || context === 'profile-pictures') {
237287
if (context === 'copilot') {

apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ export function UserInput({
212212

213213
const files = useFileAttachments({
214214
userId: userId || session?.user?.id,
215+
workspaceId,
215216
disabled: false,
216217
isLoading: isSending,
217218
})

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-file-attachments.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export interface MessageFileAttachment {
4343

4444
interface UseFileAttachmentsProps {
4545
userId?: string
46+
workspaceId?: string
4647
disabled?: boolean
4748
isLoading?: boolean
4849
}
@@ -55,7 +56,7 @@ interface UseFileAttachmentsProps {
5556
* @returns File attachment state and operations
5657
*/
5758
export function useFileAttachments(props: UseFileAttachmentsProps) {
58-
const { userId, disabled, isLoading } = props
59+
const { userId, workspaceId, disabled, isLoading } = props
5960

6061
const [attachedFiles, setAttachedFiles] = useState<AttachedFile[]>([])
6162
const [isDragging, setIsDragging] = useState(false)
@@ -135,7 +136,10 @@ export function useFileAttachments(props: UseFileAttachmentsProps) {
135136
try {
136137
const formData = new FormData()
137138
formData.append('file', file)
138-
formData.append('context', 'copilot')
139+
formData.append('context', 'mothership')
140+
if (workspaceId) {
141+
formData.append('workspaceId', workspaceId)
142+
}
139143

140144
const uploadResponse = await fetch('/api/files/upload', {
141145
method: 'POST',
@@ -171,7 +175,7 @@ export function useFileAttachments(props: UseFileAttachmentsProps) {
171175
}
172176
}
173177
},
174-
[userId]
178+
[userId, workspaceId]
175179
)
176180

177181
/**

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/user-input.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ const UserInput = forwardRef<UserInputRef, UserInputProps>(
188188

189189
const fileAttachments = useFileAttachments({
190190
userId: session?.user?.id,
191+
workspaceId,
191192
disabled,
192193
isLoading,
193194
})

apps/sim/lib/copilot/chat-payload.ts

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { createLogger } from '@sim/logger'
22
import { getUserSubscriptionState } from '@/lib/billing/core/subscription'
3-
import { processFileAttachments } from '@/lib/copilot/chat-context'
43
import { getCopilotToolDescription } from '@/lib/copilot/tool-descriptions'
54
import { isHosted } from '@/lib/core/config/feature-flags'
65
import { createMcpToolId } from '@/lib/mcp/utils'
6+
import { trackChatUpload } from '@/lib/uploads/contexts/workspace/workspace-file-manager'
77
import { getWorkflowById } from '@/lib/workflows/utils'
88
import { tools } from '@/tools/registry'
99
import { getLatestVersionTools, stripVersionSuffix } from '@/tools/utils'
@@ -126,7 +126,47 @@ export async function buildCopilotRequestPayload(
126126
const effectiveMode = mode === 'agent' ? 'build' : mode
127127
const transportMode = effectiveMode === 'build' ? 'agent' : effectiveMode
128128

129-
const processedFileContents = await processFileAttachments(fileAttachments ?? [], userId)
129+
// Track uploaded files in the DB and build context tags instead of base64 inlining
130+
const uploadContexts: Array<{ type: string; content: string }> = []
131+
if (chatId && params.workspaceId && fileAttachments && fileAttachments.length > 0) {
132+
for (const f of fileAttachments) {
133+
const filename = (f.filename ?? f.name ?? 'file') as string
134+
const mediaType = (f.media_type ?? f.mimeType ?? 'application/octet-stream') as string
135+
try {
136+
await trackChatUpload(
137+
params.workspaceId,
138+
userId,
139+
chatId,
140+
f.key,
141+
filename,
142+
mediaType,
143+
f.size
144+
)
145+
const lines = [
146+
`File "${filename}" (${mediaType}, ${f.size} bytes) uploaded.`,
147+
`Read with: read("uploads/${filename}")`,
148+
`To save permanently: materialize_file(fileName: "${filename}")`,
149+
]
150+
if (filename.endsWith('.json')) {
151+
lines.push(
152+
`To import as a workflow: materialize_file(fileName: "${filename}", operation: "import")`
153+
)
154+
}
155+
uploadContexts.push({
156+
type: 'uploaded_file',
157+
content: lines.join('\n'),
158+
})
159+
} catch (err) {
160+
logger.warn('Failed to track chat upload', {
161+
filename,
162+
chatId,
163+
error: err instanceof Error ? err.message : String(err),
164+
})
165+
}
166+
}
167+
}
168+
169+
const allContexts = [...(contexts ?? []), ...uploadContexts]
130170

131171
let integrationTools: ToolSchema[] = []
132172

@@ -170,11 +210,10 @@ export async function buildCopilotRequestPayload(
170210
...(provider ? { provider } : {}),
171211
mode: transportMode,
172212
messageId: userMessageId,
173-
...(contexts && contexts.length > 0 ? { context: contexts } : {}),
213+
...(allContexts.length > 0 ? { context: allContexts } : {}),
174214
...(chatId ? { chatId } : {}),
175215
...(typeof prefetch === 'boolean' ? { prefetch } : {}),
176216
...(implicitFeedback ? { implicitFeedback } : {}),
177-
...(processedFileContents.length > 0 ? { fileAttachments: processedFileContents } : {}),
178217
...(integrationTools.length > 0 ? { integrationTools } : {}),
179218
...(commands && commands.length > 0 ? { commands } : {}),
180219
...(params.workspaceContext ? { workspaceContext: params.workspaceContext } : {}),

apps/sim/lib/copilot/orchestrator/tool-executor/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
executeUpdateWorkspaceMcpServer,
4242
} from './deployment-tools'
4343
import { executeIntegrationToolDirect } from './integration-tools'
44+
import { executeMaterializeFile } from './materialize-file'
4445
import {
4546
executeCompleteJob,
4647
executeCreateJob,
@@ -984,6 +985,7 @@ const SIM_WORKFLOW_TOOL_HANDLERS: Record<
984985
},
985986
}
986987
},
988+
materialize_file: (p, c) => executeMaterializeFile(p, c),
987989
manage_custom_tool: (p, c) => executeManageCustomTool(p, c),
988990
manage_mcp_tool: (p, c) => executeManageMcpTool(p, c),
989991
manage_skill: (p, c) => executeManageSkill(p, c),

0 commit comments

Comments
 (0)