Skip to content

Commit 5863f9e

Browse files
authored
Merge pull request #349 from Ayush7614/feat/image-budget-311
feat(chrome): image budget controls for screenshot quality + per-turn capture (#311)
2 parents a8d5c4d + 712ba41 commit 5863f9e

43 files changed

Lines changed: 1720 additions & 118 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/chrome/src/agent/agent.js

Lines changed: 479 additions & 68 deletions
Large diffs are not rendered by default.

src/chrome/src/agent/tools.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ export const AGENT_TOOLS = [
262262
type: 'function',
263263
function: {
264264
name: 'click',
265-
description: 'Click an element. FOUR ways to use it: (1) CSS selector, (2) visible text, (3) element index from get_interactive_elements, (4) x/y coordinates. For text clicks, default matching is EXACT and case-insensitive. You can opt into broader matching with `textMatch: "prefix"` or `textMatch: "contains"`. Note: jQuery/Playwright pseudo-classes like `:contains()` and `:has-text()` are NOT valid CSS and will fail; use the `text` parameter instead. COORDINATES are CSS pixels; if using visual context, only use CSS-pixel-aligned coordinates. Prefer click_ax({ref_id}) whenever possible because it avoids coordinate drift.',
265+
description: 'Click an element. FOUR ways to use it: (1) CSS selector, (2) visible text, (3) element index from get_interactive_elements, (4) x/y coordinates. For text clicks, default matching is EXACT and case-insensitive. You can opt into broader matching with `textMatch: "prefix"` or `textMatch: "contains"`. Note: jQuery/Playwright pseudo-classes like `:contains()` and `:has-text()` are NOT valid CSS and will fail; use the `text` parameter instead. COORDINATES are CSS pixels; if x/y were read off a screenshot image that was reported as downscaled, pass from_screenshot: true and the image pixels are converted to CSS pixels automatically. Prefer click_ax({ref_id}) whenever possible because it avoids coordinate drift.',
266266
parameters: {
267267
type: 'object',
268268
properties: {
@@ -272,6 +272,7 @@ export const AGENT_TOOLS = [
272272
index: { type: 'number', description: 'Index from get_interactive_elements result' },
273273
x: { type: 'number', description: 'X coordinate to click' },
274274
y: { type: 'number', description: 'Y coordinate to click' },
275+
from_screenshot: { type: 'boolean', description: 'Set true when x/y were read off the most recent screenshot image. If that screenshot was downscaled, coordinates are converted from image pixels to CSS pixels automatically; harmless otherwise.' },
275276
},
276277
},
277278
},

src/chrome/src/background.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,17 @@ async function loadScreenshotRedaction() {
249249
}
250250
const screenshotRedactionReady = loadScreenshotRedaction().catch(() => {});
251251

252+
// Image budget (issue #311): screenshot quality + how many screenshots the
253+
// agent may capture per turn, and the max image dimension. Defaults preserve
254+
// the previous behavior (auto detail, unlimited screenshots, 1568px cap).
255+
async function loadImageBudget() {
256+
const stored = await chrome.storage.local.get(['imageDetail', 'maxScreenshotsPerTurn', 'maxImageDimension']);
257+
agent.applyImageBudgetFromStorage(stored);
258+
}
259+
// Retained so handleMessage can await hydration on a cold SW start — the first
260+
// chat must not race ahead of the persisted image-budget settings (issue #311).
261+
const imageBudgetReady = loadImageBudget().catch(() => {});
262+
252263
async function loadStrictSecretMode() {
253264
const stored = await chrome.storage.local.get('strictSecretMode');
254265
if (stored.strictSecretMode != null) agent.strictSecretMode = !!stored.strictSecretMode;
@@ -831,6 +842,13 @@ chrome.storage.onChanged.addListener((changes) => {
831842
if (changes.screenshotRedaction) {
832843
agent.screenshotRedaction = !!changes.screenshotRedaction.newValue;
833844
}
845+
if (changes.imageDetail || changes.maxScreenshotsPerTurn || changes.maxImageDimension) {
846+
agent.applyImageBudgetFromStorage({
847+
imageDetail: changes.imageDetail ? changes.imageDetail.newValue : undefined,
848+
maxScreenshotsPerTurn: changes.maxScreenshotsPerTurn ? changes.maxScreenshotsPerTurn.newValue : undefined,
849+
maxImageDimension: changes.maxImageDimension ? changes.maxImageDimension.newValue : undefined,
850+
});
851+
}
834852
if (changes[API_MUTATION_OBSERVER_KEY]) {
835853
setApiMutationObserverEnabled(changes[API_MUTATION_OBSERVER_KEY].newValue === true);
836854
}
@@ -1831,6 +1849,7 @@ async function handleMessage(msg, sender) {
18311849
await Promise.all([planBeforeActReady, planReviewReady, customSkillsReady, userMemoryReady]);
18321850
await webMcpEnabledReady;
18331851
await screenshotRedactionReady;
1852+
await imageBudgetReady;
18341853
}
18351854

18361855
switch (msg.action) {

src/chrome/src/ui/locales/ar.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,19 @@ export default {
816816
"st.redaction.toggle.label": "إخفاء المحتوى الحساس في لقطات الشاشة",
817817
"st.redaction.toggle.desc": "قبل إرسال لقطة الشاشة إلى نموذج رؤية، يقوم بتغبيش حقول النماذج والنص الذي يبدو كبريد إلكتروني أو رقم هاتف. يعمل الكشف بالكامل على جهازك — لا يُنقل أي شيء إضافي.",
818818
"st.redaction.warning": "⚠️ هذا تعتيم بأفضل جهد ومن نوع fail-open: إذا تعذّر تشغيل التعتيم على صفحة ما (مثلًا مباشرة بعد التنقل، أو في عارضات PDF، أو على صفحات المتصفح المقيّدة)، فستُرسل لقطة الشاشة كما هي دون تعتيم. يعتمد الكشف فقط على استدلالات DOM — النص المرسوم على عنصر canvas، أو البيانات الشخصية داخل الصور، أو أي شيء لا يُعرف كحقل نموذج أو نص بريد/هاتف قد يفلت من الكشف، كما أن نص الصفحة المُرسل إلى النموذج لا يُعتَّم بهذا الإعداد. هذا ليس ضمانًا أمنيًا. للخصوصية الكاملة، استخدم نموذجًا محليًا/دون اتصال (llama.cpp أو Ollama): عندها لن تغادر لقطات الشاشة جهازك إطلاقًا ولن تعود هناك حاجة للتعتيم.",
819+
"st.imageBudget.heading": "ميزانية الصور",
820+
"st.imageBudget.desc": "يتحكم في حجم لقطات الشاشة وعدد ما يلتقطه الوكيل لكل جولة للرؤية. تقليل التفاصيل والأبعاد يخفض التكلفة والكمون على النقاط الطرفية الصغيرة؛ زيادتها تحافظ على الدقة. القيم الافتراضية تطابق السلوك السابق.",
821+
"st.imageBudget.detail.label": "تفاصيل الصورة",
822+
"st.imageBudget.detail.desc": "تفاصيل صورة الرؤية المُرسلة إلى النموذج. «high» يحافظ على مزيد من التفاصيل (مزيد من الرموز والتكلفة)؛ «low» يرسل صورة أصغر وأرخص؛ «auto» يترك القرار للمزوّد. يعتمد على المزوّد.",
823+
"st.imageBudget.detail.high": "عالية (مزيد من التفاصيل والتكلفة)",
824+
"st.imageBudget.detail.low": "منخفضة (أرخص وأقل تفاصيل)",
825+
"st.imageBudget.detail.auto": "تلقائي (افتراضي المزوّد)",
826+
"st.imageBudget.maxPerTurn.label": "أقصى لقطات لكل جولة",
827+
"st.imageBudget.maxPerTurn.desc": "كم لقطة تلقائية يمكن للوكيل التقاطها للرؤية في جولة واحدة (0 = بلا حد). القيم الأقل تخفض التكلفة؛ عند استنفاد الميزانية تُتخطى اللقطات التلقائية اللاحقة في تلك الجولة.",
828+
"st.imageBudget.maxPerTurn.unlimited": "بلا حد",
829+
"st.imageBudget.maxDimension.label": "أقصى بُعد للصورة",
830+
"st.imageBudget.maxDimension.desc": "أطول ضلع (العرض أو الارتفاع) بالبكسل لأي لقطة تُرسل إلى الرؤية. حد أصغر يصغّر الصور قبل الإرسال فيخفض الرموز والتكلفة. حد أكبر يحافظ على الدقة.",
831+
"st.imageBudget.warning": "⚠️ تنطبق هذه الإعدادات على اللقطات المخصصة للرؤية (لقطة تلقائية، /screenshot، صفحة كاملة، verify_form). الصور المحفوظة يدويًا بدقة كاملة لا تتأثر. «Image detail» تحترمه نقاط النهاية بأسلوب OpenAI؛ قد يتجاهلها مزوّدون آخرون.",
819832
"sp.slash.export_traces": "تصدير سلسلة الأدوات (التتبعات)",
820833
"sp.export_traces.none": "لا توجد تتبعات لهذه المحادثة. فعّل «تسجيل التتبعات» في الإعدادات ثم أعد التشغيل.",
821834
"sp.export_traces.error": "تعذّر تصدير التتبعات.",

src/chrome/src/ui/locales/en.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,23 @@ export default {
710710
'st.transcription.failed': 'Failed: {error}',
711711
'st.transcription.fill_required': 'Fill in Base URL and Model first.',
712712

713+
// Image budget (issue #311): tune screenshot quality + how many
714+
// screenshots the agent may capture/send per turn, and how large each
715+
// image may be. All controls live on the Multimodal tab.
716+
'st.imageBudget.heading': 'Image budget',
717+
'st.imageBudget.desc': 'Control screenshot size and how many the agent captures for vision per turn. Lower detail and dimension cuts cost and latency for smaller endpoints; higher keeps fidelity. Defaults match the previous behavior.',
718+
'st.imageBudget.detail.label': 'Image detail',
719+
'st.imageBudget.detail.desc': 'Vision image detail sent to the model. "high" keeps more detail (more tokens, higher cost); "low" sends a smaller, cheaper image; "auto" lets the provider decide. Provider-dependent.',
720+
'st.imageBudget.detail.high': 'High (more detail, more cost)',
721+
'st.imageBudget.detail.low': 'Low (cheaper, less detail)',
722+
'st.imageBudget.detail.auto': 'Auto (provider default)',
723+
'st.imageBudget.maxPerTurn.label': 'Max screenshots per turn',
724+
'st.imageBudget.maxPerTurn.desc': 'How many auto-screenshots the agent may capture for vision within a single turn (0 = unlimited). Lower values reduce cost; once the budget is spent further auto-screenshots are skipped for that turn.',
725+
'st.imageBudget.maxPerTurn.unlimited': 'Unlimited',
726+
'st.imageBudget.maxDimension.label': 'Max image dimension',
727+
'st.imageBudget.maxDimension.desc': 'Largest side (width or height) in pixels for any screenshot sent to vision. Smaller caps shrink images before they are sent, cutting tokens and cost. Larger caps keep fidelity.',
728+
'st.imageBudget.warning': '⚠️ These settings apply to screenshots captured for vision (auto-screenshot, /screenshot, full-page, verify_form). Manually saved full-resolution images are unaffected. "Image detail" is honored by OpenAI-style endpoints; other providers may ignore it.',
729+
713730
// Screenshot redaction (issue #312): local, best-effort PII blurring that
714731
// runs in-browser before any screenshot reaches a vision model.
715732
'st.redaction.heading': 'Screenshot redaction',

src/chrome/src/ui/locales/es.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,19 @@ export default {
816816
"st.redaction.toggle.label": "Redactar contenido sensible en las capturas de pantalla",
817817
"st.redaction.toggle.desc": "Antes de enviar una captura de pantalla a un modelo de visión, difumina los campos de formulario y el texto que parezca un correo electrónico o un número de teléfono. La detección se ejecuta enteramente en tu dispositivo — no se transmite nada adicional.",
818818
"st.redaction.warning": "⚠️ Es una redacción best-effort y fail-open: si no se puede aplicar en una página (por ejemplo justo después de una navegación, en visores de PDF o en páginas restringidas del navegador), la captura se envía igualmente sin redactar. La detección usa solo heurísticas del DOM — texto dibujado en un canvas, datos personales dentro de imágenes, o cualquier cosa no reconocida como campo de formulario o texto de correo/teléfono puede pasar desapercibida, y el texto de la página enviado al modelo no se redacta con este ajuste. No es una garantía de seguridad. Para privacidad total, usa un modelo local/sin conexión (llama.cpp, Ollama): las capturas nunca saldrán de tu equipo y la redacción deja de ser necesaria.",
819+
"st.imageBudget.heading": "Presupuesto de imágenes",
820+
"st.imageBudget.desc": "Controla el tamaño de las capturas y cuántas toma el agente por turno para visión. Menor detalle y dimensión reducen coste y latencia en endpoints pequeños; mayor valor mantiene la fidelidad. Los valores predeterminados coinciden con el comportamiento anterior.",
821+
"st.imageBudget.detail.label": "Detalle de imagen",
822+
"st.imageBudget.detail.desc": "Detalle de la imagen de visión enviada al modelo. «high» conserva más detalle (más tokens y coste); «low» envía una imagen más pequeña y barata; «auto» deja decidir al proveedor. Depende del proveedor.",
823+
"st.imageBudget.detail.high": "Alto (más detalle, más coste)",
824+
"st.imageBudget.detail.low": "Bajo (más barato, menos detalle)",
825+
"st.imageBudget.detail.auto": "Automático (predeterminado del proveedor)",
826+
"st.imageBudget.maxPerTurn.label": "Máx. capturas por turno",
827+
"st.imageBudget.maxPerTurn.desc": "Cuántas capturas automáticas puede tomar el agente para visión en un solo turno (0 = ilimitado). Valores más bajos reducen el coste; cuando se agota el presupuesto, no se toman más capturas automáticas en ese turno.",
828+
"st.imageBudget.maxPerTurn.unlimited": "Ilimitado",
829+
"st.imageBudget.maxDimension.label": "Dimensión máxima de imagen",
830+
"st.imageBudget.maxDimension.desc": "Lado mayor (ancho o alto) en píxeles de cualquier captura enviada a visión. Un tope más bajo reduce las imágenes antes de enviarlas, cortando tokens y coste. Un tope más alto mantiene la fidelidad.",
831+
"st.imageBudget.warning": "⚠️ Estos ajustes se aplican a capturas para visión (auto-captura, /screenshot, página completa, verify_form). Las imágenes guardadas manualmente a resolución completa no se ven afectadas. «Image detail» lo respetan endpoints estilo OpenAI; otros proveedores pueden ignorarlo.",
819832
"sp.slash.export_traces": "Exportar la cadena de herramientas (trazas)",
820833
"sp.export_traces.none": "No hay trazas para esta conversación. Activa «Registrar trazas» en Ajustes y vuelve a ejecutar.",
821834
"sp.export_traces.error": "No se pudieron exportar las trazas.",

src/chrome/src/ui/locales/fr.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,19 @@ export default {
816816
"st.redaction.toggle.label": "Flouter le contenu sensible dans les captures d'écran",
817817
"st.redaction.toggle.desc": "Avant qu'une capture d'écran soit envoyée à un modèle de vision, floute les champs de formulaire et le texte qui ressemble à un e-mail ou un numéro de téléphone. La détection s'exécute entièrement sur votre appareil — rien de plus n'est transmis.",
818818
"st.redaction.warning": "⚠️ Meilleur effort et fail-open : si le floutage ne peut pas s'exécuter sur une page (par exemple juste après une navigation, dans les visionneuses PDF, ou sur des pages restreintes du navigateur), la capture est quand même envoyée non floutée. La détection utilise uniquement des heuristiques DOM — texte dessiné sur un canvas, données personnelles dans des images, ou tout ce qui n'est pas reconnu comme champ de formulaire ou texte e-mail/téléphone peut passer inaperçu, et le texte de la page envoyé au modèle n'est pas flouté par ce réglage. Ce n'est PAS une garantie de sécurité. Pour une confidentialité totale, utilisez un modèle local/hors ligne (llama.cpp, Ollama) : les captures ne quittent alors jamais votre machine et le floutage devient inutile.",
819+
"st.imageBudget.heading": "Budget d’images",
820+
"st.imageBudget.desc": "Contrôle la taille des captures et le nombre que l’agent prend pour la vision par tour. Moins de détail et une dimension plus petite réduisent le coût et la latence sur les petits endpoints ; plus élevé conserve la fidélité. Les valeurs par défaut reprennent le comportement précédent.",
821+
"st.imageBudget.detail.label": "Détail d’image",
822+
"st.imageBudget.detail.desc": "Détail d’image vision envoyé au modèle. « high » garde plus de détail (plus de tokens et de coût) ; « low » envoie une image plus petite et moins chère ; « auto » laisse le fournisseur décider. Dépend du fournisseur.",
823+
"st.imageBudget.detail.high": "Élevé (plus de détail, plus de coût)",
824+
"st.imageBudget.detail.low": "Faible (moins cher, moins de détail)",
825+
"st.imageBudget.detail.auto": "Auto (défaut du fournisseur)",
826+
"st.imageBudget.maxPerTurn.label": "Captures max. par tour",
827+
"st.imageBudget.maxPerTurn.desc": "Nombre de captures auto que l’agent peut prendre pour la vision dans un tour (0 = illimité). Une valeur plus basse réduit le coût ; une fois le budget épuisé, les captures auto suivantes sont ignorées pour ce tour.",
828+
"st.imageBudget.maxPerTurn.unlimited": "Illimité",
829+
"st.imageBudget.maxDimension.label": "Dimension max. d’image",
830+
"st.imageBudget.maxDimension.desc": "Plus grand côté (largeur ou hauteur) en pixels pour toute capture envoyée à la vision. Un plafond plus bas réduit les images avant envoi, ce qui coupe tokens et coût. Un plafond plus haut conserve la fidélité.",
831+
"st.imageBudget.warning": "⚠️ Ces réglages s’appliquent aux captures pour la vision (auto-capture, /screenshot, page entière, verify_form). Les images enregistrées manuellement en pleine résolution ne sont pas affectées. « Image detail » est respecté par les endpoints de type OpenAI ; d’autres fournisseurs peuvent l’ignorer.",
819832
"sp.slash.export_traces": "Exporter la chaîne d'outils (traces)",
820833
"sp.export_traces.none": "Aucune trace pour cette conversation. Activez « Enregistrer les traces » dans les paramètres, puis relancez.",
821834
"sp.export_traces.error": "Impossible d'exporter les traces.",

0 commit comments

Comments
 (0)