( params: Record<string, unknown>, context: ExecutionContext )
| 162 | } |
| 163 | |
| 164 | export async function executeVfsGlob( |
| 165 | params: Record<string, unknown>, |
| 166 | context: ExecutionContext |
| 167 | ): Promise<ToolCallResult> { |
| 168 | const pattern = params.pattern as string | undefined |
| 169 | if (!pattern) { |
| 170 | return { success: false, error: "Missing required parameter 'pattern'" } |
| 171 | } |
| 172 | |
| 173 | const workspaceId = context.workspaceId |
| 174 | if (!workspaceId) { |
| 175 | return { success: false, error: 'No workspace context available' } |
| 176 | } |
| 177 | |
| 178 | try { |
| 179 | const vfs = await getOrMaterializeVFS(workspaceId, context.userId) |
| 180 | let files = vfs.glob(pattern) |
| 181 | |
| 182 | if (context.chatId && (pattern === 'uploads/*' || pattern.startsWith('uploads/'))) { |
| 183 | const uploads = await listChatUploads(context.chatId) |
| 184 | // Encode per segment so uploads/ paths match the files/ convention; the |
| 185 | // upload resolver accepts both the encoded path and the raw display name. |
| 186 | const uploadPaths = uploads.map((f) => `uploads/${encodeUploadSegment(f.name)}`) |
| 187 | files = [...files, ...uploadPaths] |
| 188 | } |
| 189 | |
| 190 | logger.debug('vfs_glob result', { pattern, fileCount: files.length }) |
| 191 | return { success: true, output: { files } } |
| 192 | } catch (err) { |
| 193 | logger.error('vfs_glob failed', { |
| 194 | pattern, |
| 195 | error: toError(err).message, |
| 196 | }) |
| 197 | return { success: false, error: getErrorMessage(err, 'vfs_glob failed') } |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | export async function executeVfsRead( |
| 202 | params: Record<string, unknown>, |
no test coverage detected