Просмотр исходного кода

feat(tools): add image support to read_file tool (#5172)

Co-authored-by: Daniel <[email protected]>
Co-authored-by: Daniel Riccio <[email protected]>
Sam Hoang Van 5 месяцев назад
Родитель
Сommit
017622d3e3
49 измененных файлов с 1507 добавлено и 90 удалено
  1. 2 0
      packages/types/src/global-settings.ts
  2. 867 60
      src/core/tools/__tests__/readFileTool.spec.ts
  3. 192 0
      src/core/tools/helpers/imageHelpers.ts
  4. 114 12
      src/core/tools/readFileTool.ts
  5. 6 0
      src/core/webview/ClineProvider.ts
  6. 2 0
      src/core/webview/__tests__/ClineProvider.spec.ts
  7. 8 0
      src/core/webview/webviewMessageHandler.ts
  8. 3 1
      src/i18n/locales/ca/tools.json
  9. 3 1
      src/i18n/locales/de/tools.json
  10. 3 1
      src/i18n/locales/en/tools.json
  11. 3 1
      src/i18n/locales/es/tools.json
  12. 3 1
      src/i18n/locales/fr/tools.json
  13. 3 1
      src/i18n/locales/hi/tools.json
  14. 3 1
      src/i18n/locales/id/tools.json
  15. 3 1
      src/i18n/locales/it/tools.json
  16. 3 1
      src/i18n/locales/ja/tools.json
  17. 3 1
      src/i18n/locales/ko/tools.json
  18. 3 1
      src/i18n/locales/nl/tools.json
  19. 3 1
      src/i18n/locales/pl/tools.json
  20. 3 1
      src/i18n/locales/pt-BR/tools.json
  21. 3 1
      src/i18n/locales/ru/tools.json
  22. 3 1
      src/i18n/locales/tr/tools.json
  23. 3 1
      src/i18n/locales/vi/tools.json
  24. 3 1
      src/i18n/locales/zh-CN/tools.json
  25. 3 1
      src/i18n/locales/zh-TW/tools.json
  26. 2 0
      src/shared/ExtensionMessage.ts
  27. 2 0
      src/shared/WebviewMessage.ts
  28. 62 0
      webview-ui/src/components/settings/ContextManagementSettings.tsx
  29. 6 0
      webview-ui/src/components/settings/SettingsView.tsx
  30. 8 0
      webview-ui/src/context/ExtensionStateContext.tsx
  31. 2 0
      webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx
  32. 10 0
      webview-ui/src/i18n/locales/ca/settings.json
  33. 10 0
      webview-ui/src/i18n/locales/de/settings.json
  34. 10 0
      webview-ui/src/i18n/locales/en/settings.json
  35. 10 0
      webview-ui/src/i18n/locales/es/settings.json
  36. 10 0
      webview-ui/src/i18n/locales/fr/settings.json
  37. 10 0
      webview-ui/src/i18n/locales/hi/settings.json
  38. 10 0
      webview-ui/src/i18n/locales/id/settings.json
  39. 10 0
      webview-ui/src/i18n/locales/it/settings.json
  40. 10 0
      webview-ui/src/i18n/locales/ja/settings.json
  41. 10 0
      webview-ui/src/i18n/locales/ko/settings.json
  42. 10 0
      webview-ui/src/i18n/locales/nl/settings.json
  43. 10 0
      webview-ui/src/i18n/locales/pl/settings.json
  44. 10 0
      webview-ui/src/i18n/locales/pt-BR/settings.json
  45. 10 0
      webview-ui/src/i18n/locales/ru/settings.json
  46. 10 0
      webview-ui/src/i18n/locales/tr/settings.json
  47. 10 0
      webview-ui/src/i18n/locales/vi/settings.json
  48. 10 0
      webview-ui/src/i18n/locales/zh-CN/settings.json
  49. 10 0
      webview-ui/src/i18n/locales/zh-TW/settings.json

+ 2 - 0
packages/types/src/global-settings.ts

@@ -101,6 +101,8 @@ export const globalSettingsSchema = z.object({
 	maxWorkspaceFiles: z.number().optional(),
 	showRooIgnoredFiles: z.boolean().optional(),
 	maxReadFileLine: z.number().optional(),
+	maxImageFileSize: z.number().optional(),
+	maxTotalImageSize: z.number().optional(),
 
 	terminalOutputLineLimit: z.number().optional(),
 	terminalOutputCharacterLimit: z.number().optional(),

Разница между файлами не показана из-за своего большого размера
+ 867 - 60
src/core/tools/__tests__/readFileTool.spec.ts


+ 192 - 0
src/core/tools/helpers/imageHelpers.ts

@@ -0,0 +1,192 @@
+import path from "path"
+import * as fs from "fs/promises"
+import { t } from "../../../i18n"
+import prettyBytes from "pretty-bytes"
+
+/**
+ * Default maximum allowed image file size in bytes (5MB)
+ */
+export const DEFAULT_MAX_IMAGE_FILE_SIZE_MB = 5
+
+/**
+ * Default maximum total memory usage for all images in a single read operation (20MB)
+ * This is a cumulative limit - as each image is processed, its size is added to the total.
+ * If including another image would exceed this limit, it will be skipped with a notice.
+ * Example: With a 20MB limit, reading 3 images of 8MB, 7MB, and 10MB would process
+ * the first two (15MB total) but skip the third to stay under the limit.
+ */
+export const DEFAULT_MAX_TOTAL_IMAGE_SIZE_MB = 20
+
+/**
+ * Supported image formats that can be displayed
+ */
+export const SUPPORTED_IMAGE_FORMATS = [
+	".png",
+	".jpg",
+	".jpeg",
+	".gif",
+	".webp",
+	".svg",
+	".bmp",
+	".ico",
+	".tiff",
+	".tif",
+	".avif",
+] as const
+
+export const IMAGE_MIME_TYPES: Record<string, string> = {
+	".png": "image/png",
+	".jpg": "image/jpeg",
+	".jpeg": "image/jpeg",
+	".gif": "image/gif",
+	".webp": "image/webp",
+	".svg": "image/svg+xml",
+	".bmp": "image/bmp",
+	".ico": "image/x-icon",
+	".tiff": "image/tiff",
+	".tif": "image/tiff",
+	".avif": "image/avif",
+}
+
+/**
+ * Result of image validation
+ */
+export interface ImageValidationResult {
+	isValid: boolean
+	reason?: "size_limit" | "memory_limit" | "unsupported_model"
+	notice?: string
+	sizeInMB?: number
+}
+
+/**
+ * Result of image processing
+ */
+export interface ImageProcessingResult {
+	dataUrl: string
+	buffer: Buffer
+	sizeInKB: number
+	sizeInMB: number
+	notice: string
+}
+
+/**
+ * Reads an image file and returns both the data URL and buffer
+ */
+export async function readImageAsDataUrlWithBuffer(filePath: string): Promise<{ dataUrl: string; buffer: Buffer }> {
+	const fileBuffer = await fs.readFile(filePath)
+	const base64 = fileBuffer.toString("base64")
+	const ext = path.extname(filePath).toLowerCase()
+
+	const mimeType = IMAGE_MIME_TYPES[ext] || "image/png"
+	const dataUrl = `data:${mimeType};base64,${base64}`
+
+	return { dataUrl, buffer: fileBuffer }
+}
+
+/**
+ * Checks if a file extension is a supported image format
+ */
+export function isSupportedImageFormat(extension: string): boolean {
+	return SUPPORTED_IMAGE_FORMATS.includes(extension.toLowerCase() as (typeof SUPPORTED_IMAGE_FORMATS)[number])
+}
+
+/**
+ * Validates if an image can be processed based on size limits and model support
+ */
+export async function validateImageForProcessing(
+	fullPath: string,
+	supportsImages: boolean,
+	maxImageFileSize: number,
+	maxTotalImageSize: number,
+	currentTotalMemoryUsed: number,
+): Promise<ImageValidationResult> {
+	// Check if model supports images
+	if (!supportsImages) {
+		return {
+			isValid: false,
+			reason: "unsupported_model",
+			notice: "Image file detected but current model does not support images. Skipping image processing.",
+		}
+	}
+
+	const imageStats = await fs.stat(fullPath)
+	const imageSizeInMB = imageStats.size / (1024 * 1024)
+
+	// Check individual file size limit
+	if (imageStats.size > maxImageFileSize * 1024 * 1024) {
+		const imageSizeFormatted = prettyBytes(imageStats.size)
+		return {
+			isValid: false,
+			reason: "size_limit",
+			notice: t("tools:readFile.imageTooLarge", {
+				size: imageSizeFormatted,
+				max: maxImageFileSize,
+			}),
+			sizeInMB: imageSizeInMB,
+		}
+	}
+
+	// Check total memory limit
+	if (currentTotalMemoryUsed + imageSizeInMB > maxTotalImageSize) {
+		const currentMemoryFormatted = prettyBytes(currentTotalMemoryUsed * 1024 * 1024)
+		const fileMemoryFormatted = prettyBytes(imageStats.size)
+		return {
+			isValid: false,
+			reason: "memory_limit",
+			notice: `Image skipped to avoid size limit (${maxTotalImageSize}MB). Current: ${currentMemoryFormatted} + this file: ${fileMemoryFormatted}. Try fewer or smaller images.`,
+			sizeInMB: imageSizeInMB,
+		}
+	}
+
+	return {
+		isValid: true,
+		sizeInMB: imageSizeInMB,
+	}
+}
+
+/**
+ * Processes an image file and returns the result
+ */
+export async function processImageFile(fullPath: string): Promise<ImageProcessingResult> {
+	const imageStats = await fs.stat(fullPath)
+	const { dataUrl, buffer } = await readImageAsDataUrlWithBuffer(fullPath)
+	const imageSizeInKB = Math.round(imageStats.size / 1024)
+	const imageSizeInMB = imageStats.size / (1024 * 1024)
+	const noticeText = t("tools:readFile.imageWithSize", { size: imageSizeInKB })
+
+	return {
+		dataUrl,
+		buffer,
+		sizeInKB: imageSizeInKB,
+		sizeInMB: imageSizeInMB,
+		notice: noticeText,
+	}
+}
+
+/**
+ * Memory tracker for image processing
+ */
+export class ImageMemoryTracker {
+	private totalMemoryUsed: number = 0
+
+	/**
+	 * Gets the current total memory used in MB
+	 */
+	getTotalMemoryUsed(): number {
+		return this.totalMemoryUsed
+	}
+
+	/**
+	 * Adds to the total memory used
+	 */
+	addMemoryUsage(sizeInMB: number): void {
+		this.totalMemoryUsed += sizeInMB
+	}
+
+	/**
+	 * Resets the memory tracker
+	 */
+	reset(): void {
+		this.totalMemoryUsed = 0
+	}
+}

+ 114 - 12
src/core/tools/readFileTool.ts

@@ -14,6 +14,14 @@ import { readLines } from "../../integrations/misc/read-lines"
 import { extractTextFromFile, addLineNumbers, getSupportedBinaryFormats } from "../../integrations/misc/extract-text"
 import { parseSourceCodeDefinitionsForFile } from "../../services/tree-sitter"
 import { parseXml } from "../../utils/xml"
+import {
+	DEFAULT_MAX_IMAGE_FILE_SIZE_MB,
+	DEFAULT_MAX_TOTAL_IMAGE_SIZE_MB,
+	isSupportedImageFormat,
+	validateImageForProcessing,
+	processImageFile,
+	ImageMemoryTracker,
+} from "./helpers/imageHelpers"
 
 export function getReadFileToolDescription(blockName: string, blockParams: any): string {
 	// Handle both single path and multiple files via args
@@ -66,6 +74,7 @@ interface FileResult {
 	notice?: string
 	lineRanges?: LineRange[]
 	xmlContent?: string // Final XML content for this file
+	imageDataUrl?: string // Image data URL for image files
 	feedbackText?: string // User feedback text from approval/denial
 	feedbackImages?: any[] // User feedback images from approval/denial
 }
@@ -83,6 +92,10 @@ export async function readFileTool(
 	const legacyStartLineStr: string | undefined = block.params.start_line
 	const legacyEndLineStr: string | undefined = block.params.end_line
 
+	// Check if the current model supports images at the beginning
+	const modelInfo = cline.api.getModel().info
+	const supportsImages = modelInfo.supportsImages ?? false
+
 	// Handle partial message first
 	if (block.partial) {
 		let filePath = ""
@@ -420,6 +433,15 @@ export async function readFileTool(
 			}
 		}
 
+		// Track total image memory usage across all files
+		const imageMemoryTracker = new ImageMemoryTracker()
+		const state = await cline.providerRef.deref()?.getState()
+		const {
+			maxReadFileLine = -1,
+			maxImageFileSize = DEFAULT_MAX_IMAGE_FILE_SIZE_MB,
+			maxTotalImageSize = DEFAULT_MAX_TOTAL_IMAGE_SIZE_MB,
+		} = state ?? {}
+
 		// Then process only approved files
 		for (const fileResult of fileResults) {
 			// Skip files that weren't approved
@@ -429,7 +451,6 @@ export async function readFileTool(
 
 			const relPath = fileResult.path
 			const fullPath = path.resolve(cline.cwd, relPath)
-			const { maxReadFileLine = -1 } = (await cline.providerRef.deref()?.getState()) ?? {}
 
 			// Process approved files
 			try {
@@ -440,14 +461,71 @@ export async function readFileTool(
 					const fileExtension = path.extname(relPath).toLowerCase()
 					const supportedBinaryFormats = getSupportedBinaryFormats()
 
-					if (!supportedBinaryFormats.includes(fileExtension)) {
+					// Check if it's a supported image format
+					if (isSupportedImageFormat(fileExtension)) {
+						try {
+							// Validate image for processing
+							const validationResult = await validateImageForProcessing(
+								fullPath,
+								supportsImages,
+								maxImageFileSize,
+								maxTotalImageSize,
+								imageMemoryTracker.getTotalMemoryUsed(),
+							)
+
+							if (!validationResult.isValid) {
+								// Track file read
+								await cline.fileContextTracker.trackFileContext(relPath, "read_tool" as RecordSource)
+
+								updateFileResult(relPath, {
+									xmlContent: `<file><path>${relPath}</path>\n<notice>${validationResult.notice}</notice>\n</file>`,
+								})
+								continue
+							}
+
+							// Process the image
+							const imageResult = await processImageFile(fullPath)
+
+							// Track memory usage for this image
+							imageMemoryTracker.addMemoryUsage(imageResult.sizeInMB)
+
+							// Track file read
+							await cline.fileContextTracker.trackFileContext(relPath, "read_tool" as RecordSource)
+
+							// Store image data URL separately - NOT in XML
+							updateFileResult(relPath, {
+								xmlContent: `<file><path>${relPath}</path>\n<notice>${imageResult.notice}</notice>\n</file>`,
+								imageDataUrl: imageResult.dataUrl,
+							})
+							continue
+						} catch (error) {
+							const errorMsg = error instanceof Error ? error.message : String(error)
+							updateFileResult(relPath, {
+								status: "error",
+								error: `Error reading image file: ${errorMsg}`,
+								xmlContent: `<file><path>${relPath}</path><error>Error reading image file: ${errorMsg}</error></file>`,
+							})
+							await handleError(
+								`reading image file ${relPath}`,
+								error instanceof Error ? error : new Error(errorMsg),
+							)
+							continue
+						}
+					}
+
+					// Check if it's a supported binary format that can be processed
+					if (supportedBinaryFormats && supportedBinaryFormats.includes(fileExtension)) {
+						// For supported binary formats (.pdf, .docx, .ipynb), continue to extractTextFromFile
+						// Fall through to the normal extractTextFromFile processing below
+					} else {
+						// Handle unknown binary format
+						const fileFormat = fileExtension.slice(1) || "bin" // Remove the dot, fallback to "bin"
 						updateFileResult(relPath, {
-							notice: "Binary file",
-							xmlContent: `<file><path>${relPath}</path>\n<notice>Binary file</notice>\n</file>`,
+							notice: `Binary file format: ${fileFormat}`,
+							xmlContent: `<file><path>${relPath}</path>\n<binary_file format="${fileFormat}">Binary file - content not displayed</binary_file>\n</file>`,
 						})
 						continue
 					}
-					// For supported binary formats (.pdf, .docx, .ipynb), continue to extractTextFromFile
 				}
 
 				// Handle range reads (bypass maxReadFileLine)
@@ -546,6 +624,11 @@ export async function readFileTool(
 		const xmlResults = fileResults.filter((result) => result.xmlContent).map((result) => result.xmlContent)
 		const filesXml = `<files>\n${xmlResults.join("\n")}\n</files>`
 
+		// Collect all image data URLs from file results
+		const fileImageUrls = fileResults
+			.filter((result) => result.imageDataUrl)
+			.map((result) => result.imageDataUrl as string)
+
 		// Process all feedback in a unified way without branching
 		let statusMessage = ""
 		let feedbackImages: any[] = []
@@ -573,20 +656,39 @@ export async function readFileTool(
 			}
 		}
 
+		// Combine all images: feedback images first, then file images
+		const allImages = [...feedbackImages, ...fileImageUrls]
+
+		// Re-check if the model supports images before including them, in case it changed during execution.
+		const finalModelSupportsImages = cline.api.getModel().info.supportsImages ?? false
+		const imagesToInclude = finalModelSupportsImages ? allImages : []
+
 		// Push the result with appropriate formatting
-		if (statusMessage) {
-			const result = formatResponse.toolResult(statusMessage, feedbackImages)
+		if (statusMessage || imagesToInclude.length > 0) {
+			// Always use formatResponse.toolResult when we have a status message or images
+			const result = formatResponse.toolResult(
+				statusMessage || filesXml,
+				imagesToInclude.length > 0 ? imagesToInclude : undefined,
+			)
 
 			// Handle different return types from toolResult
 			if (typeof result === "string") {
-				pushToolResult(`${result}\n${filesXml}`)
+				if (statusMessage) {
+					pushToolResult(`${result}\n${filesXml}`)
+				} else {
+					pushToolResult(result)
+				}
 			} else {
-				// For block-based results, we need to convert the filesXml to a text block and append it
-				const textBlock = { type: "text" as const, text: filesXml }
-				pushToolResult([...result, textBlock])
+				// For block-based results, append the files XML as a text block if not already included
+				if (statusMessage) {
+					const textBlock = { type: "text" as const, text: filesXml }
+					pushToolResult([...result, textBlock])
+				} else {
+					pushToolResult(result)
+				}
 			}
 		} else {
-			// No status message, just push the files XML
+			// No images or status message, just push the files XML
 			pushToolResult(filesXml)
 		}
 	} catch (error) {

+ 6 - 0
src/core/webview/ClineProvider.ts

@@ -1493,6 +1493,8 @@ export class ClineProvider
 			showRooIgnoredFiles,
 			language,
 			maxReadFileLine,
+			maxImageFileSize,
+			maxTotalImageSize,
 			terminalCompressProgressBar,
 			historyPreviewCollapsed,
 			cloudUserInfo,
@@ -1600,6 +1602,8 @@ export class ClineProvider
 			language: language ?? formatLanguage(vscode.env.language),
 			renderContext: this.renderContext,
 			maxReadFileLine: maxReadFileLine ?? -1,
+			maxImageFileSize: maxImageFileSize ?? 5,
+			maxTotalImageSize: maxTotalImageSize ?? 20,
 			maxConcurrentFileReads: maxConcurrentFileReads ?? 5,
 			settingsImportedAt: this.settingsImportedAt,
 			terminalCompressProgressBar: terminalCompressProgressBar ?? true,
@@ -1770,6 +1774,8 @@ export class ClineProvider
 			telemetrySetting: stateValues.telemetrySetting || "unset",
 			showRooIgnoredFiles: stateValues.showRooIgnoredFiles ?? true,
 			maxReadFileLine: stateValues.maxReadFileLine ?? -1,
+			maxImageFileSize: stateValues.maxImageFileSize ?? 5,
+			maxTotalImageSize: stateValues.maxTotalImageSize ?? 20,
 			maxConcurrentFileReads: stateValues.maxConcurrentFileReads ?? 5,
 			historyPreviewCollapsed: stateValues.historyPreviewCollapsed ?? false,
 			cloudUserInfo,

+ 2 - 0
src/core/webview/__tests__/ClineProvider.spec.ts

@@ -533,6 +533,8 @@ describe("ClineProvider", () => {
 			showRooIgnoredFiles: true,
 			renderContext: "sidebar",
 			maxReadFileLine: 500,
+			maxImageFileSize: 5,
+			maxTotalImageSize: 20,
 			cloudUserInfo: null,
 			organizationAllowList: ORGANIZATION_ALLOW_ALL,
 			autoCondenseContext: true,

+ 8 - 0
src/core/webview/webviewMessageHandler.ts

@@ -1265,6 +1265,14 @@ export const webviewMessageHandler = async (
 			await updateGlobalState("maxReadFileLine", message.value)
 			await provider.postStateToWebview()
 			break
+		case "maxImageFileSize":
+			await updateGlobalState("maxImageFileSize", message.value)
+			await provider.postStateToWebview()
+			break
+		case "maxTotalImageSize":
+			await updateGlobalState("maxTotalImageSize", message.value)
+			await provider.postStateToWebview()
+			break
 		case "maxConcurrentFileReads":
 			const valueToSave = message.value // Capture the value intended for saving
 			await updateGlobalState("maxConcurrentFileReads", valueToSave)

+ 3 - 1
src/i18n/locales/ca/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (línies {{start}}-{{end}})",
 		"definitionsOnly": " (només definicions)",
-		"maxLines": " (màxim {{max}} línies)"
+		"maxLines": " (màxim {{max}} línies)",
+		"imageTooLarge": "El fitxer d'imatge és massa gran ({{size}} MB). La mida màxima permesa és {{max}} MB.",
+		"imageWithSize": "Fitxer d'imatge ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Roo sembla estar atrapat en un bucle, intentant la mateixa acció ({{toolName}}) repetidament. Això podria indicar un problema amb la seva estratègia actual. Considera reformular la tasca, proporcionar instruccions més específiques o guiar-lo cap a un enfocament diferent.",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/de/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (Zeilen {{start}}-{{end}})",
 		"definitionsOnly": " (nur Definitionen)",
-		"maxLines": " (maximal {{max}} Zeilen)"
+		"maxLines": " (maximal {{max}} Zeilen)",
+		"imageTooLarge": "Die Bilddatei ist zu groß ({{size}} MB). Die maximal erlaubte Größe beträgt {{max}} MB.",
+		"imageWithSize": "Bilddatei ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Roo scheint in einer Schleife festzustecken und versucht wiederholt dieselbe Aktion ({{toolName}}). Dies könnte auf ein Problem mit der aktuellen Strategie hindeuten. Überlege dir, die Aufgabe umzuformulieren, genauere Anweisungen zu geben oder Roo zu einem anderen Ansatz zu führen.",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/en/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (lines {{start}}-{{end}})",
 		"definitionsOnly": " (definitions only)",
-		"maxLines": " (max {{max}} lines)"
+		"maxLines": " (max {{max}} lines)",
+		"imageTooLarge": "Image file is too large ({{size}} MB). The maximum allowed size is {{max}} MB.",
+		"imageWithSize": "Image file ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Roo appears to be stuck in a loop, attempting the same action ({{toolName}}) repeatedly. This might indicate a problem with its current strategy. Consider rephrasing the task, providing more specific instructions, or guiding it towards a different approach.",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/es/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (líneas {{start}}-{{end}})",
 		"definitionsOnly": " (solo definiciones)",
-		"maxLines": " (máximo {{max}} líneas)"
+		"maxLines": " (máximo {{max}} líneas)",
+		"imageTooLarge": "El archivo de imagen es demasiado grande ({{size}} MB). El tamaño máximo permitido es {{max}} MB.",
+		"imageWithSize": "Archivo de imagen ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Roo parece estar atrapado en un bucle, intentando la misma acción ({{toolName}}) repetidamente. Esto podría indicar un problema con su estrategia actual. Considera reformular la tarea, proporcionar instrucciones más específicas o guiarlo hacia un enfoque diferente.",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/fr/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (lignes {{start}}-{{end}})",
 		"definitionsOnly": " (définitions uniquement)",
-		"maxLines": " (max {{max}} lignes)"
+		"maxLines": " (max {{max}} lignes)",
+		"imageTooLarge": "Le fichier image est trop volumineux ({{size}} MB). La taille maximale autorisée est {{max}} MB.",
+		"imageWithSize": "Fichier image ({{size}} Ko)"
 	},
 	"toolRepetitionLimitReached": "Roo semble être bloqué dans une boucle, tentant la même action ({{toolName}}) de façon répétée. Cela pourrait indiquer un problème avec sa stratégie actuelle. Envisage de reformuler la tâche, de fournir des instructions plus spécifiques ou de le guider vers une approche différente.",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/hi/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (पंक्तियाँ {{start}}-{{end}})",
 		"definitionsOnly": " (केवल परिभाषाएँ)",
-		"maxLines": " (अधिकतम {{max}} पंक्तियाँ)"
+		"maxLines": " (अधिकतम {{max}} पंक्तियाँ)",
+		"imageTooLarge": "छवि फ़ाइल बहुत बड़ी है ({{size}} MB)। अधिकतम अनुमतित आकार {{max}} MB है।",
+		"imageWithSize": "छवि फ़ाइल ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Roo एक लूप में फंसा हुआ लगता है, बार-बार एक ही क्रिया ({{toolName}}) को दोहरा रहा है। यह उसकी वर्तमान रणनीति में किसी समस्या का संकेत हो सकता है। कार्य को पुनः परिभाषित करने, अधिक विशिष्ट निर्देश देने, या उसे एक अलग दृष्टिकोण की ओर मार्गदर्शित करने पर विचार करें।",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/id/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (baris {{start}}-{{end}})",
 		"definitionsOnly": " (hanya definisi)",
-		"maxLines": " (maks {{max}} baris)"
+		"maxLines": " (maks {{max}} baris)",
+		"imageTooLarge": "File gambar terlalu besar ({{size}} MB). Ukuran maksimum yang diizinkan adalah {{max}} MB.",
+		"imageWithSize": "File gambar ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Roo tampaknya terjebak dalam loop, mencoba aksi yang sama ({{toolName}}) berulang kali. Ini mungkin menunjukkan masalah dengan strategi saat ini. Pertimbangkan untuk mengubah frasa tugas, memberikan instruksi yang lebih spesifik, atau mengarahkannya ke pendekatan yang berbeda.",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/it/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (righe {{start}}-{{end}})",
 		"definitionsOnly": " (solo definizioni)",
-		"maxLines": " (max {{max}} righe)"
+		"maxLines": " (max {{max}} righe)",
+		"imageTooLarge": "Il file immagine è troppo grande ({{size}} MB). La dimensione massima consentita è {{max}} MB.",
+		"imageWithSize": "File immagine ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Roo sembra essere bloccato in un ciclo, tentando ripetutamente la stessa azione ({{toolName}}). Questo potrebbe indicare un problema con la sua strategia attuale. Considera di riformulare l'attività, fornire istruzioni più specifiche o guidarlo verso un approccio diverso.",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/ja/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " ({{start}}-{{end}}行目)",
 		"definitionsOnly": " (定義のみ)",
-		"maxLines": " (最大{{max}}行)"
+		"maxLines": " (最大{{max}}行)",
+		"imageTooLarge": "画像ファイルが大きすぎます({{size}} MB)。最大許可サイズは {{max}} MB です。",
+		"imageWithSize": "画像ファイル({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Rooが同じ操作({{toolName}})を繰り返し試みるループに陥っているようです。これは現在の方法に問題がある可能性を示しています。タスクの言い換え、より具体的な指示の提供、または別のアプローチへの誘導を検討してください。",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/ko/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " ({{start}}-{{end}}행)",
 		"definitionsOnly": " (정의만)",
-		"maxLines": " (최대 {{max}}행)"
+		"maxLines": " (최대 {{max}}행)",
+		"imageTooLarge": "이미지 파일이 너무 큽니다 ({{size}} MB). 최대 허용 크기는 {{max}} MB입니다.",
+		"imageWithSize": "이미지 파일 ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Roo가 같은 동작({{toolName}})을 반복적으로 시도하면서 루프에 갇힌 것 같습니다. 이는 현재 전략에 문제가 있을 수 있음을 나타냅니다. 작업을 다시 표현하거나, 더 구체적인 지침을 제공하거나, 다른 접근 방식으로 안내해 보세요.",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/nl/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (regels {{start}}-{{end}})",
 		"definitionsOnly": " (alleen definities)",
-		"maxLines": " (max {{max}} regels)"
+		"maxLines": " (max {{max}} regels)",
+		"imageTooLarge": "Afbeeldingsbestand is te groot ({{size}} MB). De maximaal toegestane grootte is {{max}} MB.",
+		"imageWithSize": "Afbeeldingsbestand ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Roo lijkt vast te zitten in een lus, waarbij hij herhaaldelijk dezelfde actie ({{toolName}}) probeert. Dit kan duiden op een probleem met de huidige strategie. Overweeg de taak te herformuleren, specifiekere instructies te geven of Roo naar een andere aanpak te leiden.",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/pl/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (linie {{start}}-{{end}})",
 		"definitionsOnly": " (tylko definicje)",
-		"maxLines": " (maks. {{max}} linii)"
+		"maxLines": " (maks. {{max}} linii)",
+		"imageTooLarge": "Plik obrazu jest zbyt duży ({{size}} MB). Maksymalny dozwolony rozmiar to {{max}} MB.",
+		"imageWithSize": "Plik obrazu ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Wygląda na to, że Roo utknął w pętli, wielokrotnie próbując wykonać tę samą akcję ({{toolName}}). Może to wskazywać na problem z jego obecną strategią. Rozważ przeformułowanie zadania, podanie bardziej szczegółowych instrukcji lub nakierowanie go na inne podejście.",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/pt-BR/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (linhas {{start}}-{{end}})",
 		"definitionsOnly": " (apenas definições)",
-		"maxLines": " (máx. {{max}} linhas)"
+		"maxLines": " (máx. {{max}} linhas)",
+		"imageTooLarge": "Arquivo de imagem é muito grande ({{size}} MB). O tamanho máximo permitido é {{max}} MB.",
+		"imageWithSize": "Arquivo de imagem ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Roo parece estar preso em um loop, tentando a mesma ação ({{toolName}}) repetidamente. Isso pode indicar um problema com sua estratégia atual. Considere reformular a tarefa, fornecer instruções mais específicas ou guiá-lo para uma abordagem diferente.",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/ru/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (строки {{start}}-{{end}})",
 		"definitionsOnly": " (только определения)",
-		"maxLines": " (макс. {{max}} строк)"
+		"maxLines": " (макс. {{max}} строк)",
+		"imageTooLarge": "Файл изображения слишком большой ({{size}} МБ). Максимально допустимый размер {{max}} МБ.",
+		"imageWithSize": "Файл изображения ({{size}} КБ)"
 	},
 	"toolRepetitionLimitReached": "Похоже, что Roo застрял в цикле, многократно пытаясь выполнить одно и то же действие ({{toolName}}). Это может указывать на проблему с его текущей стратегией. Попробуйте переформулировать задачу, предоставить более конкретные инструкции или направить его к другому подходу.",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/tr/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (satır {{start}}-{{end}})",
 		"definitionsOnly": " (sadece tanımlar)",
-		"maxLines": " (maks. {{max}} satır)"
+		"maxLines": " (maks. {{max}} satır)",
+		"imageTooLarge": "Görüntü dosyası çok büyük ({{size}} MB). İzin verilen maksimum boyut {{max}} MB.",
+		"imageWithSize": "Görüntü dosyası ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Roo bir döngüye takılmış gibi görünüyor, aynı eylemi ({{toolName}}) tekrar tekrar deniyor. Bu, mevcut stratejisinde bir sorun olduğunu gösterebilir. Görevi yeniden ifade etmeyi, daha spesifik talimatlar vermeyi veya onu farklı bir yaklaşıma yönlendirmeyi düşünün.",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/vi/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (dòng {{start}}-{{end}})",
 		"definitionsOnly": " (chỉ định nghĩa)",
-		"maxLines": " (tối đa {{max}} dòng)"
+		"maxLines": " (tối đa {{max}} dòng)",
+		"imageTooLarge": "Tệp hình ảnh quá lớn ({{size}} MB). Kích thước tối đa cho phép là {{max}} MB.",
+		"imageWithSize": "Tệp hình ảnh ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Roo dường như đang bị mắc kẹt trong một vòng lặp, liên tục cố gắng thực hiện cùng một hành động ({{toolName}}). Điều này có thể cho thấy vấn đề với chiến lược hiện tại. Hãy cân nhắc việc diễn đạt lại nhiệm vụ, cung cấp hướng dẫn cụ thể hơn, hoặc hướng Roo theo một cách tiếp cận khác.",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/zh-CN/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (第 {{start}}-{{end}} 行)",
 		"definitionsOnly": " (仅定义)",
-		"maxLines": " (最多 {{max}} 行)"
+		"maxLines": " (最多 {{max}} 行)",
+		"imageTooLarge": "图片文件过大 ({{size}} MB)。允许的最大大小为 {{max}} MB。",
+		"imageWithSize": "图片文件 ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Roo 似乎陷入循环,反复尝试同一操作 ({{toolName}})。这可能表明当前策略存在问题。请考虑重新描述任务、提供更具体的指示或引导其尝试不同的方法。",
 	"codebaseSearch": {

+ 3 - 1
src/i18n/locales/zh-TW/tools.json

@@ -2,7 +2,9 @@
 	"readFile": {
 		"linesRange": " (第 {{start}}-{{end}} 行)",
 		"definitionsOnly": " (僅定義)",
-		"maxLines": " (最多 {{max}} 行)"
+		"maxLines": " (最多 {{max}} 行)",
+		"imageTooLarge": "圖片檔案過大 ({{size}} MB)。允許的最大大小為 {{max}} MB。",
+		"imageWithSize": "圖片檔案 ({{size}} KB)"
 	},
 	"toolRepetitionLimitReached": "Roo 似乎陷入循環,反覆嘗試同一操作 ({{toolName}})。這可能表明目前策略存在問題。請考慮重新描述工作、提供更具體的指示或引導其嘗試不同的方法。",
 	"codebaseSearch": {

+ 2 - 0
src/shared/ExtensionMessage.ts

@@ -284,6 +284,8 @@ export type ExtensionState = Pick<
 	maxWorkspaceFiles: number // Maximum number of files to include in current working directory details (0-500)
 	showRooIgnoredFiles: boolean // Whether to show .rooignore'd files in listings
 	maxReadFileLine: number // Maximum number of lines to read from a file before truncating
+	maxImageFileSize: number // Maximum size of image files to process in MB
+	maxTotalImageSize: number // Maximum total size for all images in a single read operation in MB
 
 	experiments: Experiments // Map of experiment IDs to their enabled state
 

+ 2 - 0
src/shared/WebviewMessage.ts

@@ -162,6 +162,8 @@ export interface WebviewMessage {
 		| "remoteBrowserEnabled"
 		| "language"
 		| "maxReadFileLine"
+		| "maxImageFileSize"
+		| "maxTotalImageSize"
 		| "maxConcurrentFileReads"
 		| "includeDiagnosticMessages"
 		| "maxDiagnosticMessages"

+ 62 - 0
webview-ui/src/components/settings/ContextManagementSettings.tsx

@@ -20,6 +20,8 @@ type ContextManagementSettingsProps = HTMLAttributes<HTMLDivElement> & {
 	maxWorkspaceFiles: number
 	showRooIgnoredFiles?: boolean
 	maxReadFileLine?: number
+	maxImageFileSize?: number
+	maxTotalImageSize?: number
 	maxConcurrentFileReads?: number
 	profileThresholds?: Record<string, number>
 	includeDiagnosticMessages?: boolean
@@ -32,6 +34,8 @@ type ContextManagementSettingsProps = HTMLAttributes<HTMLDivElement> & {
 		| "maxWorkspaceFiles"
 		| "showRooIgnoredFiles"
 		| "maxReadFileLine"
+		| "maxImageFileSize"
+		| "maxTotalImageSize"
 		| "maxConcurrentFileReads"
 		| "profileThresholds"
 		| "includeDiagnosticMessages"
@@ -49,6 +53,8 @@ export const ContextManagementSettings = ({
 	showRooIgnoredFiles,
 	setCachedStateField,
 	maxReadFileLine,
+	maxImageFileSize,
+	maxTotalImageSize,
 	maxConcurrentFileReads,
 	profileThresholds = {},
 	includeDiagnosticMessages,
@@ -206,6 +212,62 @@ export const ContextManagementSettings = ({
 					</div>
 				</div>
 
+				<div>
+					<div className="flex flex-col gap-2">
+						<span className="font-medium">{t("settings:contextManagement.maxImageFileSize.label")}</span>
+						<div className="flex items-center gap-4">
+							<Input
+								type="number"
+								pattern="[0-9]*"
+								className="w-24 bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border px-2 py-1 rounded text-right [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
+								value={maxImageFileSize ?? 5}
+								min={1}
+								max={100}
+								onChange={(e) => {
+									const newValue = parseInt(e.target.value, 10)
+									if (!isNaN(newValue) && newValue >= 1 && newValue <= 100) {
+										setCachedStateField("maxImageFileSize", newValue)
+									}
+								}}
+								onClick={(e) => e.currentTarget.select()}
+								data-testid="max-image-file-size-input"
+							/>
+							<span>{t("settings:contextManagement.maxImageFileSize.mb")}</span>
+						</div>
+					</div>
+					<div className="text-vscode-descriptionForeground text-sm mt-2">
+						{t("settings:contextManagement.maxImageFileSize.description")}
+					</div>
+				</div>
+
+				<div>
+					<div className="flex flex-col gap-2">
+						<span className="font-medium">{t("settings:contextManagement.maxTotalImageSize.label")}</span>
+						<div className="flex items-center gap-4">
+							<Input
+								type="number"
+								pattern="[0-9]*"
+								className="w-24 bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border px-2 py-1 rounded text-right [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
+								value={maxTotalImageSize ?? 20}
+								min={1}
+								max={500}
+								onChange={(e) => {
+									const newValue = parseInt(e.target.value, 10)
+									if (!isNaN(newValue) && newValue >= 1 && newValue <= 500) {
+										setCachedStateField("maxTotalImageSize", newValue)
+									}
+								}}
+								onClick={(e) => e.currentTarget.select()}
+								data-testid="max-total-image-size-input"
+							/>
+							<span>{t("settings:contextManagement.maxTotalImageSize.mb")}</span>
+						</div>
+					</div>
+					<div className="text-vscode-descriptionForeground text-sm mt-2">
+						{t("settings:contextManagement.maxTotalImageSize.description")}
+					</div>
+				</div>
+
 				<div>
 					<VSCodeCheckbox
 						checked={includeDiagnosticMessages}

+ 6 - 0
webview-ui/src/components/settings/SettingsView.tsx

@@ -168,6 +168,8 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 		showRooIgnoredFiles,
 		remoteBrowserEnabled,
 		maxReadFileLine,
+		maxImageFileSize,
+		maxTotalImageSize,
 		terminalCompressProgressBar,
 		maxConcurrentFileReads,
 		condensingApiConfigId,
@@ -321,6 +323,8 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 			vscode.postMessage({ type: "maxWorkspaceFiles", value: maxWorkspaceFiles ?? 200 })
 			vscode.postMessage({ type: "showRooIgnoredFiles", bool: showRooIgnoredFiles })
 			vscode.postMessage({ type: "maxReadFileLine", value: maxReadFileLine ?? -1 })
+			vscode.postMessage({ type: "maxImageFileSize", value: maxImageFileSize ?? 5 })
+			vscode.postMessage({ type: "maxTotalImageSize", value: maxTotalImageSize ?? 20 })
 			vscode.postMessage({ type: "maxConcurrentFileReads", value: cachedState.maxConcurrentFileReads ?? 5 })
 			vscode.postMessage({ type: "includeDiagnosticMessages", bool: includeDiagnosticMessages })
 			vscode.postMessage({ type: "maxDiagnosticMessages", value: maxDiagnosticMessages ?? 50 })
@@ -667,6 +671,8 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 							maxWorkspaceFiles={maxWorkspaceFiles ?? 200}
 							showRooIgnoredFiles={showRooIgnoredFiles}
 							maxReadFileLine={maxReadFileLine}
+							maxImageFileSize={maxImageFileSize}
+							maxTotalImageSize={maxTotalImageSize}
 							maxConcurrentFileReads={maxConcurrentFileReads}
 							profileThresholds={profileThresholds}
 							includeDiagnosticMessages={includeDiagnosticMessages}

+ 8 - 0
webview-ui/src/context/ExtensionStateContext.tsx

@@ -121,6 +121,10 @@ export interface ExtensionStateContextType extends ExtensionState {
 	setAwsUsePromptCache: (value: boolean) => void
 	maxReadFileLine: number
 	setMaxReadFileLine: (value: number) => void
+	maxImageFileSize: number
+	setMaxImageFileSize: (value: number) => void
+	maxTotalImageSize: number
+	setMaxTotalImageSize: (value: number) => void
 	machineId?: string
 	pinnedApiConfigs?: Record<string, boolean>
 	setPinnedApiConfigs: (value: Record<string, boolean>) => void
@@ -209,6 +213,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		showRooIgnoredFiles: true, // Default to showing .rooignore'd files with lock symbol (current behavior).
 		renderContext: "sidebar",
 		maxReadFileLine: -1, // Default max read file line limit
+		maxImageFileSize: 5, // Default max image file size in MB
+		maxTotalImageSize: 20, // Default max total image size in MB
 		pinnedApiConfigs: {}, // Empty object for pinned API configs
 		terminalZshOhMy: false, // Default Oh My Zsh integration setting
 		maxConcurrentFileReads: 5, // Default concurrent file reads
@@ -455,6 +461,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		setRemoteBrowserEnabled: (value) => setState((prevState) => ({ ...prevState, remoteBrowserEnabled: value })),
 		setAwsUsePromptCache: (value) => setState((prevState) => ({ ...prevState, awsUsePromptCache: value })),
 		setMaxReadFileLine: (value) => setState((prevState) => ({ ...prevState, maxReadFileLine: value })),
+		setMaxImageFileSize: (value) => setState((prevState) => ({ ...prevState, maxImageFileSize: value })),
+		setMaxTotalImageSize: (value) => setState((prevState) => ({ ...prevState, maxTotalImageSize: value })),
 		setPinnedApiConfigs: (value) => setState((prevState) => ({ ...prevState, pinnedApiConfigs: value })),
 		setTerminalCompressProgressBar: (value) =>
 			setState((prevState) => ({ ...prevState, terminalCompressProgressBar: value })),

+ 2 - 0
webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx

@@ -209,6 +209,8 @@ describe("mergeExtensionState", () => {
 			sharingEnabled: false,
 			profileThresholds: {},
 			hasOpenedModeSelector: false, // Add the new required property
+			maxImageFileSize: 5,
+			maxTotalImageSize: 20,
 		}
 
 		const prevState: ExtensionState = {

+ 10 - 0
webview-ui/src/i18n/locales/ca/settings.json

@@ -537,6 +537,16 @@
 			"profileDescription": "Llindar personalitzat només per a aquest perfil (substitueix el per defecte global)",
 			"inheritDescription": "Aquest perfil hereta el llindar per defecte global ({{threshold}}%)",
 			"usesGlobal": "(utilitza global {{threshold}}%)"
+		},
+		"maxImageFileSize": {
+			"label": "Mida màxima d'arxiu d'imatge",
+			"mb": "MB",
+			"description": "Mida màxima (en MB) per a arxius d'imatge que poden ser processats per l'eina de lectura d'arxius."
+		},
+		"maxTotalImageSize": {
+			"label": "Mida total màxima d'imatges",
+			"mb": "MB",
+			"description": "Límit de mida acumulativa màxima (en MB) per a totes les imatges processades en una sola operació read_file. Quan es llegeixen múltiples imatges, la mida de cada imatge s'afegeix al total. Si incloure una altra imatge excediria aquest límit, serà omesa."
 		}
 	},
 	"terminal": {

+ 10 - 0
webview-ui/src/i18n/locales/de/settings.json

@@ -537,6 +537,16 @@
 			"profileDescription": "Benutzerdefinierter Schwellenwert nur für dieses Profil (überschreibt globalen Standard)",
 			"inheritDescription": "Dieses Profil erbt den globalen Standard-Schwellenwert ({{threshold}}%)",
 			"usesGlobal": "(verwendet global {{threshold}}%)"
+		},
+		"maxImageFileSize": {
+			"label": "Maximale Bilddateigröße",
+			"mb": "MB",
+			"description": "Maximale Größe (in MB) für Bilddateien, die vom read file Tool verarbeitet werden können."
+		},
+		"maxTotalImageSize": {
+			"label": "Maximale Gesamtbildgröße",
+			"mb": "MB",
+			"description": "Maximales kumulatives Größenlimit (in MB) für alle Bilder, die in einer einzelnen read_file-Operation verarbeitet werden. Beim Lesen mehrerer Bilder wird die Größe jedes Bildes zur Gesamtsumme addiert. Wenn das Einbeziehen eines weiteren Bildes dieses Limit überschreiten würde, wird es übersprungen."
 		}
 	},
 	"terminal": {

+ 10 - 0
webview-ui/src/i18n/locales/en/settings.json

@@ -512,6 +512,16 @@
 			"lines": "lines",
 			"always_full_read": "Always read entire file"
 		},
+		"maxImageFileSize": {
+			"label": "Max image file size",
+			"mb": "MB",
+			"description": "Maximum size (in MB) for image files that can be processed by the read file tool."
+		},
+		"maxTotalImageSize": {
+			"label": "Max total image size",
+			"mb": "MB",
+			"description": "Maximum cumulative size limit (in MB) for all images processed in a single read_file operation. When reading multiple images, each image's size is added to the total. If including another image would exceed this limit, it will be skipped."
+		},
 		"diagnostics": {
 			"includeMessages": {
 				"label": "Automatically include diagnostics in context",

+ 10 - 0
webview-ui/src/i18n/locales/es/settings.json

@@ -513,6 +513,16 @@
 			"label": "Límite de lecturas simultáneas",
 			"description": "Número máximo de archivos que la herramienta 'read_file' puede procesar simultáneamente. Valores más altos pueden acelerar la lectura de múltiples archivos pequeños pero aumentan el uso de memoria."
 		},
+		"maxImageFileSize": {
+			"label": "Tamaño máximo de archivo de imagen",
+			"mb": "MB",
+			"description": "Tamaño máximo (en MB) para archivos de imagen que pueden ser procesados por la herramienta de lectura de archivos."
+		},
+		"maxTotalImageSize": {
+			"label": "Tamaño total máximo de imágenes",
+			"mb": "MB",
+			"description": "Límite de tamaño acumulativo máximo (en MB) para todas las imágenes procesadas en una sola operación read_file. Al leer múltiples imágenes, el tamaño de cada imagen se suma al total. Si incluir otra imagen excedería este límite, será omitida."
+		},
 		"diagnostics": {
 			"includeMessages": {
 				"label": "Incluir automáticamente diagnósticos en el contexto",

+ 10 - 0
webview-ui/src/i18n/locales/fr/settings.json

@@ -513,6 +513,16 @@
 			"label": "Limite de lectures simultanées",
 			"description": "Nombre maximum de fichiers que l'outil 'read_file' peut traiter simultanément. Des valeurs plus élevées peuvent accélérer la lecture de plusieurs petits fichiers mais augmentent l'utilisation de la mémoire."
 		},
+		"maxImageFileSize": {
+			"label": "Taille maximale des fichiers d'image",
+			"mb": "MB",
+			"description": "Taille maximale (en MB) pour les fichiers d'image qui peuvent être traités par l'outil de lecture de fichier."
+		},
+		"maxTotalImageSize": {
+			"label": "Taille totale maximale des images",
+			"mb": "MB",
+			"description": "Limite de taille cumulée maximale (en MB) pour toutes les images traitées dans une seule opération read_file. Lors de la lecture de plusieurs images, la taille de chaque image est ajoutée au total. Si l'inclusion d'une autre image dépasserait cette limite, elle sera ignorée."
+		},
 		"diagnostics": {
 			"includeMessages": {
 				"label": "Inclure automatiquement les diagnostics dans le contexte",

+ 10 - 0
webview-ui/src/i18n/locales/hi/settings.json

@@ -538,6 +538,16 @@
 			"profileDescription": "केवल इस प्रोफ़ाइल के लिए कस्टम सीमा (वैश्विक डिफ़ॉल्ट को ओवरराइड करता है)",
 			"inheritDescription": "यह प्रोफ़ाइल वैश्विक डिफ़ॉल्ट सीमा को इनहेरिट करता है ({{threshold}}%)",
 			"usesGlobal": "(वैश्विक {{threshold}}% का उपयोग करता है)"
+		},
+		"maxImageFileSize": {
+			"label": "अधिकतम छवि फ़ाइल आकार",
+			"mb": "MB",
+			"description": "छवि फ़ाइलों के लिए अधिकतम आकार (MB में) जो read file tool द्वारा प्रसंस्कृत किया जा सकता है।"
+		},
+		"maxTotalImageSize": {
+			"label": "अधिकतम कुल छवि आकार",
+			"mb": "MB",
+			"description": "एकल read_file ऑपरेशन में संसाधित सभी छवियों के लिए अधिकतम संचयी आकार सीमा (MB में)। कई छवियों को पढ़ते समय, प्रत्येक छवि का आकार कुल में जोड़ा जाता है। यदि किसी अन्य छवि को शामिल करने से यह सीमा पार हो जाएगी, तो उसे छोड़ दिया जाएगा।"
 		}
 	},
 	"terminal": {

+ 10 - 0
webview-ui/src/i18n/locales/id/settings.json

@@ -542,6 +542,16 @@
 			"description": "Roo membaca sejumlah baris ini ketika model menghilangkan nilai start/end. Jika angka ini kurang dari total file, Roo menghasilkan indeks nomor baris dari definisi kode. Kasus khusus: -1 menginstruksikan Roo untuk membaca seluruh file (tanpa indexing), dan 0 menginstruksikannya untuk tidak membaca baris dan hanya menyediakan indeks baris untuk konteks minimal. Nilai yang lebih rendah meminimalkan penggunaan konteks awal, memungkinkan pembacaan rentang baris yang tepat selanjutnya. Permintaan start/end eksplisit tidak dibatasi oleh pengaturan ini.",
 			"lines": "baris",
 			"always_full_read": "Selalu baca seluruh file"
+		},
+		"maxImageFileSize": {
+			"label": "Ukuran file gambar maksimum",
+			"mb": "MB",
+			"description": "Ukuran maksimum (dalam MB) untuk file gambar yang dapat diproses oleh alat baca file."
+		},
+		"maxTotalImageSize": {
+			"label": "Ukuran total gambar maksimum",
+			"mb": "MB",
+			"description": "Batas ukuran kumulatif maksimum (dalam MB) untuk semua gambar yang diproses dalam satu operasi read_file. Saat membaca beberapa gambar, ukuran setiap gambar ditambahkan ke total. Jika menyertakan gambar lain akan melebihi batas ini, gambar tersebut akan dilewati."
 		}
 	},
 	"terminal": {

+ 10 - 0
webview-ui/src/i18n/locales/it/settings.json

@@ -538,6 +538,16 @@
 			"profileDescription": "Soglia personalizzata solo per questo profilo (sovrascrive il predefinito globale)",
 			"inheritDescription": "Questo profilo eredita la soglia predefinita globale ({{threshold}}%)",
 			"usesGlobal": "(usa globale {{threshold}}%)"
+		},
+		"maxImageFileSize": {
+			"label": "Dimensione massima file immagine",
+			"mb": "MB",
+			"description": "Dimensione massima (in MB) per i file immagine che possono essere elaborati dallo strumento di lettura file."
+		},
+		"maxTotalImageSize": {
+			"label": "Dimensione totale massima immagini",
+			"mb": "MB",
+			"description": "Limite di dimensione cumulativa massima (in MB) per tutte le immagini elaborate in una singola operazione read_file. Durante la lettura di più immagini, la dimensione di ogni immagine viene aggiunta al totale. Se l'inclusione di un'altra immagine supererebbe questo limite, verrà saltata."
 		}
 	},
 	"terminal": {

+ 10 - 0
webview-ui/src/i18n/locales/ja/settings.json

@@ -538,6 +538,16 @@
 			"profileDescription": "このプロファイルのみのカスタムしきい値(グローバルデフォルトを上書き)",
 			"inheritDescription": "このプロファイルはグローバルデフォルトしきい値を継承します({{threshold}}%)",
 			"usesGlobal": "(グローバル {{threshold}}% を使用)"
+		},
+		"maxImageFileSize": {
+			"label": "最大画像ファイルサイズ",
+			"mb": "MB",
+			"description": "read fileツールで処理できる画像ファイルの最大サイズ(MB単位)。"
+		},
+		"maxTotalImageSize": {
+			"label": "最大合計画像サイズ",
+			"mb": "MB",
+			"description": "単一のread_file操作で処理されるすべての画像の累積サイズ制限(MB単位)。複数の画像を読み取る際、各画像のサイズが合計に加算されます。別の画像を含めるとこの制限を超える場合、その画像はスキップされます。"
 		}
 	},
 	"terminal": {

+ 10 - 0
webview-ui/src/i18n/locales/ko/settings.json

@@ -538,6 +538,16 @@
 			"profileDescription": "이 프로필만을 위한 사용자 정의 임계값 (글로벌 기본값 재정의)",
 			"inheritDescription": "이 프로필은 글로벌 기본 임계값을 상속합니다 ({{threshold}}%)",
 			"usesGlobal": "(글로벌 {{threshold}}% 사용)"
+		},
+		"maxImageFileSize": {
+			"label": "최대 이미지 파일 크기",
+			"mb": "MB",
+			"description": "read file 도구로 처리할 수 있는 이미지 파일의 최대 크기(MB 단위)입니다."
+		},
+		"maxTotalImageSize": {
+			"label": "최대 총 이미지 크기",
+			"mb": "MB",
+			"description": "단일 read_file 작업에서 처리되는 모든 이미지의 최대 누적 크기 제한(MB 단위)입니다. 여러 이미지를 읽을 때 각 이미지의 크기가 총계에 추가됩니다. 다른 이미지를 포함하면 이 제한을 초과하는 경우 해당 이미지는 건너뜁니다."
 		}
 	},
 	"terminal": {

+ 10 - 0
webview-ui/src/i18n/locales/nl/settings.json

@@ -513,6 +513,16 @@
 			"label": "Limiet gelijktijdige bestandslezingen",
 			"description": "Maximum aantal bestanden dat de 'read_file' tool tegelijkertijd kan verwerken. Hogere waarden kunnen het lezen van meerdere kleine bestanden versnellen maar verhogen het geheugengebruik."
 		},
+		"maxImageFileSize": {
+			"label": "Maximum afbeeldingsbestandsgrootte",
+			"mb": "MB",
+			"description": "Maximale grootte (in MB) voor afbeeldingsbestanden die kunnen worden verwerkt door de read file tool."
+		},
+		"maxTotalImageSize": {
+			"label": "Maximale totale afbeeldingsgrootte",
+			"mb": "MB",
+			"description": "Maximale cumulatieve groottelimiet (in MB) voor alle afbeeldingen die in één read_file-bewerking worden verwerkt. Bij het lezen van meerdere afbeeldingen wordt de grootte van elke afbeelding bij het totaal opgeteld. Als het toevoegen van een andere afbeelding deze limiet zou overschrijden, wordt deze overgeslagen."
+		},
 		"diagnostics": {
 			"includeMessages": {
 				"label": "Automatisch diagnostiek opnemen in context",

+ 10 - 0
webview-ui/src/i18n/locales/pl/settings.json

@@ -538,6 +538,16 @@
 			"profileDescription": "Niestandardowy próg tylko dla tego profilu (zastępuje globalny domyślny)",
 			"inheritDescription": "Ten profil dziedziczy globalny domyślny próg ({{threshold}}%)",
 			"usesGlobal": "(używa globalnego {{threshold}}%)"
+		},
+		"maxImageFileSize": {
+			"label": "Maksymalny rozmiar pliku obrazu",
+			"mb": "MB",
+			"description": "Maksymalny rozmiar (w MB) plików obrazów, które mogą być przetwarzane przez narzędzie do czytania plików."
+		},
+		"maxTotalImageSize": {
+			"label": "Maksymalny całkowity rozmiar obrazów",
+			"mb": "MB",
+			"description": "Maksymalny skumulowany limit rozmiaru (w MB) dla wszystkich obrazów przetwarzanych w jednej operacji read_file. Podczas odczytu wielu obrazów rozmiar każdego obrazu jest dodawany do sumy. Jeśli dołączenie kolejnego obrazu przekroczyłoby ten limit, zostanie on pominięty."
 		}
 	},
 	"terminal": {

+ 10 - 0
webview-ui/src/i18n/locales/pt-BR/settings.json

@@ -538,6 +538,16 @@
 			"profileDescription": "Limite personalizado apenas para este perfil (substitui o padrão global)",
 			"inheritDescription": "Este perfil herda o limite padrão global ({{threshold}}%)",
 			"usesGlobal": "(usa global {{threshold}}%)"
+		},
+		"maxImageFileSize": {
+			"label": "Tamanho máximo do arquivo de imagem",
+			"mb": "MB",
+			"description": "Tamanho máximo (em MB) para arquivos de imagem que podem ser processados pela ferramenta de leitura de arquivos."
+		},
+		"maxTotalImageSize": {
+			"label": "Tamanho total máximo da imagem",
+			"mb": "MB",
+			"description": "Limite máximo de tamanho cumulativo (em MB) para todas as imagens processadas em uma única operação read_file. Ao ler várias imagens, o tamanho de cada imagem é adicionado ao total. Se incluir outra imagem exceder esse limite, ela será ignorada."
 		}
 	},
 	"terminal": {

+ 10 - 0
webview-ui/src/i18n/locales/ru/settings.json

@@ -538,6 +538,16 @@
 			"profileDescription": "Пользовательский порог только для этого профиля (переопределяет глобальный по умолчанию)",
 			"inheritDescription": "Этот профиль наследует глобальный порог по умолчанию ({{threshold}}%)",
 			"usesGlobal": "(использует глобальный {{threshold}}%)"
+		},
+		"maxImageFileSize": {
+			"label": "Максимальный размер файла изображения",
+			"mb": "MB",
+			"description": "Максимальный размер (в МБ) для файлов изображений, которые могут быть обработаны инструментом чтения файлов."
+		},
+		"maxTotalImageSize": {
+			"label": "Максимальный общий размер изображений",
+			"mb": "МБ",
+			"description": "Максимальный совокупный лимит размера (в МБ) для всех изображений, обрабатываемых в одной операции read_file. При чтении нескольких изображений размер каждого изображения добавляется к общему. Если включение другого изображения превысит этот лимит, оно будет пропущено."
 		}
 	},
 	"terminal": {

+ 10 - 0
webview-ui/src/i18n/locales/tr/settings.json

@@ -538,6 +538,16 @@
 			"profileDescription": "Sadece bu profil için özel eşik (küresel varsayılanı geçersiz kılar)",
 			"inheritDescription": "Bu profil küresel varsayılan eşiği miras alır ({{threshold}}%)",
 			"usesGlobal": "(küresel {{threshold}}% kullanır)"
+		},
+		"maxImageFileSize": {
+			"label": "Maksimum görüntü dosyası boyutu",
+			"mb": "MB",
+			"description": "Dosya okuma aracı tarafından işlenebilecek görüntü dosyaları için maksimum boyut (MB cinsinden)."
+		},
+		"maxTotalImageSize": {
+			"label": "Maksimum toplam görüntü boyutu",
+			"mb": "MB",
+			"description": "Tek bir read_file işleminde işlenen tüm görüntüler için maksimum kümülatif boyut sınırı (MB cinsinden). Birden çok görüntü okurken, her görüntünün boyutu toplama eklenir. Başka bir görüntü eklemek bu sınırı aşacaksa, atlanacaktır."
 		}
 	},
 	"terminal": {

+ 10 - 0
webview-ui/src/i18n/locales/vi/settings.json

@@ -538,6 +538,16 @@
 			"profileDescription": "Ngưỡng tùy chỉnh chỉ cho hồ sơ này (ghi đè mặc định toàn cục)",
 			"inheritDescription": "Hồ sơ này kế thừa ngưỡng mặc định toàn cục ({{threshold}}%)",
 			"usesGlobal": "(sử dụng toàn cục {{threshold}}%)"
+		},
+		"maxImageFileSize": {
+			"label": "Kích thước tối đa của tệp hình ảnh",
+			"mb": "MB",
+			"description": "Kích thước tối đa (tính bằng MB) cho các tệp hình ảnh có thể được xử lý bởi công cụ đọc tệp."
+		},
+		"maxTotalImageSize": {
+			"label": "Kích thước tổng tối đa của hình ảnh",
+			"mb": "MB",
+			"description": "Giới hạn kích thước tích lũy tối đa (tính bằng MB) cho tất cả hình ảnh được xử lý trong một thao tác read_file duy nhất. Khi đọc nhiều hình ảnh, kích thước của mỗi hình ảnh được cộng vào tổng. Nếu việc thêm một hình ảnh khác sẽ vượt quá giới hạn này, nó sẽ bị bỏ qua."
 		}
 	},
 	"terminal": {

+ 10 - 0
webview-ui/src/i18n/locales/zh-CN/settings.json

@@ -538,6 +538,16 @@
 			"profileDescription": "仅此配置文件的自定义阈值(覆盖全局默认)",
 			"inheritDescription": "此配置文件继承全局默认阈值({{threshold}}%)",
 			"usesGlobal": "(使用全局 {{threshold}}%)"
+		},
+		"maxImageFileSize": {
+			"label": "最大图像文件大小",
+			"mb": "MB",
+			"description": "read file工具可以处理的图像文件的最大大小(以MB为单位)。"
+		},
+		"maxTotalImageSize": {
+			"label": "图片总大小上限",
+			"mb": "MB",
+			"description": "单次 read_file 操作中处理的所有图片的最大累计大小限制(MB)。读取多张图片时,每张图片的大小会累加到总大小中。如果包含另一张图片会超过此限制,则会跳过该图片。"
 		}
 	},
 	"terminal": {

+ 10 - 0
webview-ui/src/i18n/locales/zh-TW/settings.json

@@ -538,6 +538,16 @@
 			"profileDescription": "僅此檔案的自訂閾值(覆蓋全域預設)",
 			"inheritDescription": "此檔案繼承全域預設閾值({{threshold}}%)",
 			"usesGlobal": "(使用全域 {{threshold}}%)"
+		},
+		"maxImageFileSize": {
+			"label": "最大圖像檔案大小",
+			"mb": "MB",
+			"description": "read file工具可以處理的圖像檔案的最大大小(以MB為單位)。"
+		},
+		"maxTotalImageSize": {
+			"label": "圖片總大小上限",
+			"mb": "MB",
+			"description": "單次 read_file 操作中處理的所有圖片的最大累計大小限制(MB)。讀取多張圖片時,每張圖片的大小會累加到總大小中。如果包含另一張圖片會超過此限制,則會跳過該圖片。"
 		}
 	},
 	"terminal": {

Некоторые файлы не были показаны из-за большого количества измененных файлов