Skip to content

Commit f89b404

Browse files
committed
fix(files): mount rendered documents into the sandbox, not generator source
Mounting a generated document handed the sandbox its generator source under a .docx name, so a python-docx or openpyxl script failed on a file that looked fine and the agent debugged code that was correct. Both branches were wrong, not just the buffered one: with cloud storage the mount presigns record.key, which is the raw source object, so swapping the buffered read alone would have fixed only local storage. Source-backed documents now always take the servable path — they are bounded by the render ceiling, so routing them through the web process instead of presigning is affordable. The text/binary decision also keyed off record.type, which for these files is text/x-docxjs, so a resolved binary would have been decoded as UTF-8. It reads the resolved content type now.
1 parent 7d336b0 commit f89b404

1 file changed

Lines changed: 16 additions & 3 deletions

File tree

apps/sim/lib/copilot/tools/handlers/function-execute.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { getTableById, listTables } from '@/lib/table/service'
1010
import { getOrCreateTableSnapshot, SNAPSHOT_MAX_BYTES } from '@/lib/table/snapshot-cache'
1111
import { listWorkspaceFileFolders } from '@/lib/uploads/contexts/workspace/workspace-file-folder-manager'
1212
import {
13+
fetchServableWorkspaceFileBuffer,
1314
fetchWorkspaceFileBuffer,
1415
findWorkspaceFileRecord,
1516
getSandboxWorkspaceFilePath,
@@ -21,6 +22,7 @@ import {
2122
generatePresignedDownloadUrl,
2223
hasCloudStorage,
2324
} from '@/lib/uploads/core/storage-service'
25+
import { isGeneratedDocumentSourceType } from '@/lib/uploads/utils/file-utils'
2426
import { executeTool as executeAppTool } from '@/tools'
2527
import type { ToolExecutionContext, ToolExecutionResult } from '../../tool-executor/types'
2628

@@ -87,7 +89,14 @@ async function pushWorkspaceFileMount(
8789
mountPath: string,
8890
mounted: MountedBytes
8991
): Promise<void> {
90-
if (hasCloudStorage()) {
92+
// A generated document stores its generator source, so a presigned URL for
93+
// `record.key` would hand the sandbox source text under a `.docx` name and the
94+
// user's script would fail on a file that looks fine. Those resolve through the
95+
// servable reader instead — they are bounded by the render ceiling, so routing them
96+
// through the web process rather than presigning is affordable.
97+
const rendersFromSource = isGeneratedDocumentSourceType(record.type)
98+
99+
if (hasCloudStorage() && !rendersFromSource) {
91100
if (record.size > MOUNT_URL_MAX_BYTES) {
92101
throw new Error(
93102
`Input file "${mountPath}" is ${Math.round(record.size / 1024 / 1024)}MB, over the ${MOUNT_URL_MAX_BYTES / 1024 / 1024}MB per-file mount limit.`
@@ -118,9 +127,13 @@ async function pushWorkspaceFileMount(
118127
`Mounting "${mountPath}" would exceed the ${MAX_TOTAL_SIZE / 1024 / 1024}MB total mount limit. Mount fewer or smaller files.`
119128
)
120129
}
121-
const buffer = await fetchWorkspaceFileBuffer(record)
130+
const { buffer, contentType } = rendersFromSource
131+
? await fetchServableWorkspaceFileBuffer(record, { maxBytes: MAX_FILE_SIZE })
132+
: { buffer: await fetchWorkspaceFileBuffer(record), contentType: record.type }
133+
// Keyed off the resolved type: a rendered document's source MIME is `text/x-…`, and
134+
// decoding the binary as UTF-8 would corrupt it just as surely as shipping the source.
122135
const isText = /^text\/|application\/json|application\/xml|application\/csv/.test(
123-
record.type || ''
136+
contentType || ''
124137
)
125138
sandboxFiles.push({
126139
path: mountPath,

0 commit comments

Comments
 (0)