@@ -3,10 +3,7 @@ import { decodeVfsPathSegments, encodeVfsPathSegments } from '@/lib/copilot/vfs/
33import { resolveWorkflowAliasForWorkspace } from '@/lib/copilot/vfs/workflow-alias-resolver'
44import { isPlanAliasPath , workflowAliasSandboxPath } from '@/lib/copilot/vfs/workflow-aliases'
55import { isMothershipBetaFeaturesEnabled } from '@/lib/core/config/feature-flags'
6- import { buildNameById , rowDataIdToName } from '@/lib/table/column-keys'
7- import { toCsvRow } from '@/lib/table/export-format'
8- import { getTableById , listTables , selectExportRowPage } from '@/lib/table/service'
9- import type { TableDefinition } from '@/lib/table/types'
6+ import { getTableById , listTables , queryRows } from '@/lib/table/service'
107import { listWorkspaceFileFolders } from '@/lib/uploads/contexts/workspace/workspace-file-folder-manager'
118import {
129 fetchWorkspaceFileBuffer ,
@@ -65,44 +62,6 @@ async function resolveTableRef(
6562 return tablePathLookup ?. get ( tableName ) ?? null
6663}
6764
68- const TABLE_MOUNT_PAGE_SIZE = 5000
69-
70- /**
71- * Serializes a cell for a sandbox CSV mount. Unlike export downloads this skips
72- * formula neutralization — the CSV is consumed by code, and a prefixed `'`
73- * would corrupt values.
74- */
75- function formatMountCsvValue ( value : unknown ) : string {
76- if ( value === null || value === undefined ) return ''
77- if ( value instanceof Date ) return value . toISOString ( )
78- if ( typeof value === 'object' ) return JSON . stringify ( value )
79- return String ( value )
80- }
81-
82- /**
83- * Serializes a full table to CSV for a sandbox mount. Walks the keyset export
84- * reader page by page so every row is included (`queryRows` with defaults
85- * silently truncated mounts to its 100-row page and paid for a count and
86- * execution metadata the CSV never used), and remaps stored column-id keys
87- * back to display names so headers line up with cell values.
88- */
89- async function buildTableCsvForMount ( table : TableDefinition ) : Promise < string > {
90- const nameById = buildNameById ( table . schema )
91- const headers = table . schema . columns . map ( ( c ) => c . name )
92- const lines = [ toCsvRow ( headers ) ]
93- let after : { position : number ; id : string } | null = null
94- while ( true ) {
95- const page = await selectExportRowPage ( table , after , TABLE_MOUNT_PAGE_SIZE )
96- for ( const row of page ) {
97- const data = rowDataIdToName ( row . data , nameById )
98- lines . push ( toCsvRow ( headers . map ( ( header ) => formatMountCsvValue ( data [ header ] ) ) ) )
99- }
100- if ( page . length < TABLE_MOUNT_PAGE_SIZE ) return lines . join ( '\n' )
101- const last = page [ page . length - 1 ]
102- after = { position : last . position , id : last . id }
103- }
104- }
105-
10665async function resolveInputFiles (
10766 workspaceId : string ,
10867 inputFiles ?: unknown [ ] ,
@@ -288,41 +247,55 @@ async function resolveInputFiles(
288247 const tablePathLookup = hasTablePathRefs
289248 ? new Map ( ( await listTables ( workspaceId ) ) . map ( ( table ) => [ table . name , table ] ) )
290249 : undefined
291- const tableMounts = await Promise . all (
292- inputTables . map ( async ( tableRef ) => {
293- const tableId =
294- typeof tableRef === 'string'
295- ? tableRef
296- : tableRef && typeof tableRef === 'object'
297- ? ( tableRef as CanonicalTableInput ) . tableId || ( tableRef as CanonicalTableInput ) . path
298- : undefined
299- if ( ! tableId ) return null
300- const table = await resolveTableRef ( tableId , tablePathLookup )
301- if ( ! table || table . workspaceId !== workspaceId ) {
302- throw new Error (
303- `Input table not found: "${ tableId } ". Pass the table id (tbl_...) from tables/{name}/meta.json, or a tables/{name}/meta.json path.`
304- )
305- }
306- const csvContent = await buildTableCsvForMount ( table )
307- const sandboxPath =
308- typeof tableRef === 'object' && tableRef !== null
309- ? ( tableRef as CanonicalTableInput ) . sandboxPath
250+ for ( const tableRef of inputTables ) {
251+ const tableId =
252+ typeof tableRef === 'string'
253+ ? tableRef
254+ : tableRef && typeof tableRef === 'object'
255+ ? ( tableRef as CanonicalTableInput ) . tableId || ( tableRef as CanonicalTableInput ) . path
310256 : undefined
311- return {
312- path : sandboxPath || `/home/user/tables/${ table . id } .csv` ,
313- content : csvContent ,
314- }
315- } )
316- )
317- for ( const mount of tableMounts ) {
318- if ( ! mount ) continue
319- if ( totalSize + mount . content . length > MAX_TOTAL_SIZE ) {
257+ if ( ! tableId ) continue
258+ const table = await resolveTableRef ( tableId , tablePathLookup )
259+ if ( ! table || table . workspaceId !== workspaceId ) {
320260 throw new Error (
321- `Mounting table "${ mount . path } " would exceed the ${ MAX_TOTAL_SIZE / 1024 / 1024 } MB total mount limit. Mount fewer or smaller tables.`
261+ `Input table not found: "${ tableId } ". Pass the table id (tbl_...) from tables/{name}/meta.json, or a tables/{name}/meta.json path.`
262+ )
263+ }
264+ const rows = await queryRows ( table , { } , 'copilot-fn-exec' )
265+
266+ const allKeys = new Set ( table . schema . columns . map ( ( column ) => column . name ) )
267+ for ( const row of rows . rows ?? [ ] ) {
268+ if ( row . data && typeof row . data === 'object' ) {
269+ for ( const key of Object . keys ( row . data as Record < string , unknown > ) ) {
270+ allKeys . add ( key )
271+ }
272+ }
273+ }
274+ const headers = Array . from ( allKeys )
275+ const csvLines = [ headers . join ( ',' ) ]
276+ for ( const row of rows . rows ?? [ ] ) {
277+ const data = ( row . data || { } ) as Record < string , unknown >
278+ csvLines . push (
279+ headers
280+ . map ( ( h ) => {
281+ const val = data [ h ]
282+ const str = val === null || val === undefined ? '' : String ( val )
283+ return str . includes ( ',' ) || str . includes ( '"' ) || str . includes ( '\n' )
284+ ? `"${ str . replace ( / " / g, '""' ) } "`
285+ : str
286+ } )
287+ . join ( ',' )
322288 )
323289 }
324- totalSize += mount . content . length
325- sandboxFiles . push ( mount )
290+ const csvContent = csvLines . join ( '\n' )
291+ const sandboxPath =
292+ typeof tableRef === 'object' && tableRef !== null
293+ ? ( tableRef as CanonicalTableInput ) . sandboxPath
294+ : undefined
295+ sandboxFiles . push ( {
296+ path : sandboxPath || `/home/user/tables/${ table . id } .csv` ,
297+ content : csvContent ,
298+ } )
326299 }
327300 }
328301
0 commit comments