diff --git a/apps/frontend/src/main/insights/insights-executor.ts b/apps/frontend/src/main/insights/insights-executor.ts index a8604045..72550e20 100644 --- a/apps/frontend/src/main/insights/insights-executor.ts +++ b/apps/frontend/src/main/insights/insights-executor.ts @@ -13,7 +13,7 @@ import type { InsightsModelConfig, ImageAttachment } from '../../shared/types'; -import { MODEL_ID_MAP } from '../../shared/constants'; +import { MODEL_ID_MAP, MAX_IMAGES_PER_TASK, MAX_IMAGE_SIZE } from '../../shared/constants'; import { InsightsConfig } from './config'; import { detectRateLimit, createSDKRateLimitInfo } from '../rate-limit-detector'; @@ -120,6 +120,14 @@ export class InsightsExecutor extends EventEmitter { const imagesTempFiles: string[] = []; let imagesManifestFile: string | undefined; + // Defense-in-depth: cap image count and filter oversized images in the executor + if (images && images.length > MAX_IMAGES_PER_TASK) { + images = images.slice(0, MAX_IMAGES_PER_TASK); + } + if (images) { + images = images.filter(img => !img.data || Buffer.byteLength(img.data, 'base64') <= MAX_IMAGE_SIZE); + } + if (images && images.length > 0) { try { const manifest: Array<{ path: string; mimeType: string }> = []; diff --git a/apps/frontend/src/renderer/components/Insights.tsx b/apps/frontend/src/renderer/components/Insights.tsx index f83de152..f8e595d5 100644 --- a/apps/frontend/src/renderer/components/Insights.tsx +++ b/apps/frontend/src/renderer/components/Insights.tsx @@ -526,7 +526,10 @@ export function Insights({ projectId }: InsightsProps) { {/* Image analysis warning */} {pendingImages.length > 0 && ( -

{t('insights.images.analysisUnsupported')}

+
+ + {t('insights.images.analysisUnsupported')} +
)} {/* Image error */} @@ -625,17 +628,20 @@ function MessageBubble({ {/* Image attachments for user messages */} {isUser && message.images && message.images.length > 0 && ( -
- {message.images - .filter(img => img.thumbnail || img.data) - .map((image) => ( - {image.filename} - ))} +
+
+ {message.images + .filter(img => img.thumbnail || img.data) + .map((image) => ( + {image.filename} + ))} +
+

{t('insights.images.notAnalyzed')}

)} diff --git a/apps/frontend/src/shared/i18n/locales/en/common.json b/apps/frontend/src/shared/i18n/locales/en/common.json index 61d3e37d..be33b4e7 100644 --- a/apps/frontend/src/shared/i18n/locales/en/common.json +++ b/apps/frontend/src/shared/i18n/locales/en/common.json @@ -442,7 +442,8 @@ "processFailed": "Failed to process image", "dragOver": "Drop image to attach", "analysisUnsupported": "Note: Image analysis is not yet supported. Images are stored for reference but cannot be analyzed by the model.", - "screenshotTooLarge": "Screenshot is too large ({{size}}MB). Maximum size is {{max}}MB. Consider capturing a smaller area." + "screenshotTooLarge": "Screenshot is too large ({{size}}MB). Maximum size is {{max}}MB. Consider capturing a smaller area.", + "notAnalyzed": "Images were stored for reference but not analyzed by the model." } }, "ideation": { diff --git a/apps/frontend/src/shared/i18n/locales/fr/common.json b/apps/frontend/src/shared/i18n/locales/fr/common.json index 2eb02c10..fa6463d7 100644 --- a/apps/frontend/src/shared/i18n/locales/fr/common.json +++ b/apps/frontend/src/shared/i18n/locales/fr/common.json @@ -442,7 +442,8 @@ "processFailed": "Échec du traitement de l'image", "dragOver": "Déposer l'image pour joindre", "analysisUnsupported": "Note : L'analyse d'images n'est pas encore prise en charge. Les images sont stockées pour référence mais ne peuvent pas être analysées par le modèle.", - "screenshotTooLarge": "La capture d'écran est trop volumineuse ({{size}}Mo). La taille maximale est de {{max}}Mo. Essayez de capturer une zone plus petite." + "screenshotTooLarge": "La capture d'écran est trop volumineuse ({{size}}Mo). La taille maximale est de {{max}}Mo. Essayez de capturer une zone plus petite.", + "notAnalyzed": "Les images ont été stockées pour référence mais n'ont pas été analysées par le modèle." } }, "ideation": {