From df475ebffab62b5aca9cec8e3786af4f7e21e3df Mon Sep 17 00:00:00 2001 From: azghr Date: Thu, 24 Sep 2026 22:05:33 +0500 Subject: [PATCH 1/4] feat: add fal.ai generative media tool to the assistant --- .claude/context.md | 701 +++++++++++++++++++++++ .claude/setup.md | 305 ++++++++++ .gitignore | 2 +- docs/providers-and-models.md | 211 +++---- src/chrome/src/agent/agent.js | 4 + src/chrome/src/agent/fal-media.js | 198 +++++++ src/chrome/src/agent/permission-gate.js | 2 + src/chrome/src/agent/planner.js | 2 + src/chrome/src/agent/tools.js | 15 + src/chrome/src/background.js | 165 +++--- src/chrome/src/ui/locales/ar.js | 8 + src/chrome/src/ui/locales/bn.js | 8 + src/chrome/src/ui/locales/de.js | 8 + src/chrome/src/ui/locales/en.js | 10 + src/chrome/src/ui/locales/es.js | 8 + src/chrome/src/ui/locales/fa.js | 8 + src/chrome/src/ui/locales/fr.js | 8 + src/chrome/src/ui/locales/he.js | 8 + src/chrome/src/ui/locales/hi.js | 8 + src/chrome/src/ui/locales/id.js | 8 + src/chrome/src/ui/locales/ja.js | 8 + src/chrome/src/ui/locales/ko.js | 8 + src/chrome/src/ui/locales/ms.js | 8 + src/chrome/src/ui/locales/nl.js | 8 + src/chrome/src/ui/locales/pl.js | 8 + src/chrome/src/ui/locales/pt.js | 8 + src/chrome/src/ui/locales/ru.js | 8 + src/chrome/src/ui/locales/th.js | 8 + src/chrome/src/ui/locales/tl.js | 8 + src/chrome/src/ui/locales/tr.js | 8 + src/chrome/src/ui/locales/uk.js | 8 + src/chrome/src/ui/locales/vi.js | 8 + src/chrome/src/ui/locales/zh.js | 8 + src/chrome/src/ui/settings.html | 26 + src/chrome/src/ui/settings.js | 94 +++ src/firefox/src/agent/agent.js | 4 + src/firefox/src/agent/fal-media.js | 198 +++++++ src/firefox/src/agent/permission-gate.js | 2 + src/firefox/src/agent/planner.js | 2 + src/firefox/src/agent/tools.js | 15 + src/firefox/src/background.js | 5 + src/firefox/src/ui/locales/ar.js | 8 + src/firefox/src/ui/locales/bn.js | 8 + src/firefox/src/ui/locales/de.js | 8 + src/firefox/src/ui/locales/en.js | 10 + src/firefox/src/ui/locales/es.js | 8 + src/firefox/src/ui/locales/fa.js | 8 + src/firefox/src/ui/locales/fr.js | 8 + src/firefox/src/ui/locales/he.js | 8 + src/firefox/src/ui/locales/hi.js | 8 + src/firefox/src/ui/locales/id.js | 8 + src/firefox/src/ui/locales/ja.js | 8 + src/firefox/src/ui/locales/ko.js | 8 + src/firefox/src/ui/locales/ms.js | 8 + src/firefox/src/ui/locales/nl.js | 8 + src/firefox/src/ui/locales/pl.js | 8 + src/firefox/src/ui/locales/pt.js | 8 + src/firefox/src/ui/locales/ru.js | 8 + src/firefox/src/ui/locales/th.js | 8 + src/firefox/src/ui/locales/tl.js | 8 + src/firefox/src/ui/locales/tr.js | 8 + src/firefox/src/ui/locales/uk.js | 8 + src/firefox/src/ui/locales/vi.js | 8 + src/firefox/src/ui/locales/zh.js | 8 + src/firefox/src/ui/settings.html | 26 + src/firefox/src/ui/settings.js | 92 ++- test/run.js | 96 +++- 67 files changed, 2356 insertions(+), 181 deletions(-) create mode 100644 .claude/context.md create mode 100644 .claude/setup.md create mode 100644 src/chrome/src/agent/fal-media.js create mode 100644 src/firefox/src/agent/fal-media.js diff --git a/.claude/context.md b/.claude/context.md new file mode 100644 index 000000000..0b0a7f5f3 --- /dev/null +++ b/.claude/context.md @@ -0,0 +1,701 @@ +# WebBrain Project Context + +## What Is This Project? + +WebBrain is an **open-source AI browser agent** — a browser extension for Chrome and Firefox that lets users chat with web pages, automate tasks, and run multi-step workflows using their choice of LLM (local or cloud). + +The user types a natural-language instruction in a side panel, and an autonomous agent loop calls the LLM, executes tool calls (click, type, navigate, read page state, etc.), feeds results back to the LLM, and repeats until the task is done. + +## Core Identity + +- **Language**: Vanilla JavaScript (ES modules), no frameworks, no build step for the extension itself +- **License**: GPL-3.0-or-later (was MIT; change effective from 33.0.0. Releases before 33.0.0 remain MIT — see `LICENSES/MIT.txt`) +- **Version**: 36.8.0 +- **Repository**: https://github.com/webbrain-one/webbrain.git +- **Website**: https://webbrain.one + +## Architecture Overview + +There are **two browser builds** that share almost all code but are duplicated so each is self-contained and can be loaded directly without a build step: + +### Chrome Build (`src/chrome/`) + +- **Manifest V3** — service worker background, `chrome.scripting`, `sidePanel` API +- CDP-backed trusted events (real `isTrusted=true` mouse/keyboard events) +- Offscreen document for fetch proxy and tab recording +- Shadow DOM piercing via CDP for closed roots +- Conversation persistence across service worker restarts via `chrome.storage.session` + +### Firefox Build (`src/firefox/`) + +- **Manifest V2** — background page, `browser.tabs.executeScript`, `sidebar_action` +- Synthetic events by default (`el.click()`, `new KeyboardEvent()`); optional `firefox-companion/` gives BiDi trusted input +- No offscreen document, no CDP, no closed shadow root support +- In-memory conversation only (no persistence across background restarts) + +### Shared Components + +- Agent loop, prompts, tools, adapters, providers, loop detection, context management +- Site adapters (138 sites) +- Provider system (local + cloud LLMs) +- Accessibility tree and ref-based interaction +- Trace recording (IndexedDB) + +### MCP Server (`mcp-server/`) + +- `@webbrain/mcp-server` (v0.1.0, TypeScript, MIT) +- Local stdio bridge letting any MCP client (Claude Code, Codex, Cursor, OpenClaw) drive the user's real authenticated browser via the running extension +- Client ↔ `webbrain-mcp` (stdio) ↔ `ws://127.0.0.1:17374/extension` ↔ WebBrain extension +- Chromium only (Firefox build has no offscreen document bridge) +- `src/`: `bridge.ts`, `config.ts`, `index.ts`, `runs.ts`; tests in `test/` (6 `.mjs` suites) + +### Firefox Companion (`firefox-companion/`) + +- Optional local companion providing **WebDriver BiDi trusted-automation** for Firefox (Node ≥22) +- Native-messaging host (`webbrain-bidi`) + `ws://localhost:9222`; only extension ID `webbrain@esokullu.com` may connect +- Gives Firefox CDP-like `isTrusted` input (coordinate clicks with post-validation, char-by-char trusted typing, native selects, sandboxed uploads via `input.setFiles`) without CDP +- Not required for standard Firefox operation; extra files: `host.mjs`, `install.mjs`, `session.mjs` + +### Marketing Site (`web/`) + +- Pure HTML/CSS, built from `web/build/template.html` + `web/build/locales/*.json` +- Multi-language (ar, bn, de, en, es, fa, fr, he, hi, id, ja, ko, ms, nl, pl, pt, ru, th, tl, tr, uk, vi, zh) +- Deployed via Vercel + +### LM Studio Plugin (`lmstudio-plugin/`) + +- TypeScript, standalone tool provider for LM Studio +- Ports `fetch_url` and `research_url` as pure Node tools (no browser dependency) + +## Directory Structure + +``` +webbrain/ +├── src/ +│ ├── chrome/ # Chrome MV3 extension +│ │ ├── manifest.json # MV3 manifest +│ │ ├── skills/ # Packaged default skills (markdown) +│ │ ├── icons/ # Extension icons +│ │ ├── styles/ # Extension CSS +│ │ ├── vendor/ # Third-party libs (pdfjs, turkish-deasciifier) +│ │ └── src/ +│ │ ├── background.js # Service worker (message router, agent lifecycle) +│ │ ├── agent/ # Agent loop, tools, adapters, skills, planner +│ │ │ ├── agent.js # Main agent loop + executeTool() +│ │ │ ├── tools.js # Tool schemas + system prompts +│ │ │ ├── planner.js # Plan-before-Act JSON planner +│ │ │ ├── adapters.js # Per-site guidance (138 adapters) +│ │ │ ├── skills.js # Skill loading, catalog, tool exposure +│ │ │ ├── permission-gate.js # Capability × origin gating +│ │ │ ├── credential-fields.js # Secret detection +│ │ │ ├── user-memory.js # Local preference memory +│ │ │ ├── loop-bucket.js # URL-family loop detection bucketing +│ │ │ ├── loop-detector.js # Extracted loop-detection helpers (repeat/coord/nav) +│ │ │ ├── scheduler.js # Scheduled task management +│ │ │ ├── pdf-tools.js # PDF text extraction +│ │ │ ├── pdf-extraction.js # PDF parsing +│ │ │ ├── pdf-ocr.js # PDF OCR +│ │ │ ├── pdf-stream.js # PDF streaming +│ │ │ ├── captcha-solver.js # CapSolver integration +│ │ │ ├── captcha-gate.js # Frame-aware CAPTCHA detection/gate +│ │ │ ├── captcha-frame-runtime.js # CAPTCHA frame runtime helpers +│ │ │ ├── capsolver-config.js # CapSolver config +│ │ │ ├── screenshot-redaction.js # Privacy redaction +│ │ │ ├── sheets-tools.js # A1 range parsing, TSV roundtrip +│ │ │ ├── progress-ledger.js # Progress tracking +│ │ │ ├── progress-intent.js # Progress intent tracking +│ │ │ ├── json-extract.js # JSON extraction helpers +│ │ │ ├── text-sanitize.js # Text sanitization (canonical) +│ │ │ ├── image-budget.js # Auto-screenshot budget helpers +│ │ │ ├── read-page-window.js # read_page pagination windows +│ │ │ ├── tool-call-parser.js # Text tool-call parsing +│ │ │ ├── tool-arguments.js # Tool argument validation +│ │ │ ├── submit-click-guard.js # Rapid duplicate-submit guard +│ │ │ ├── mutation-tools.js # DOM mutation tools +│ │ │ ├── trace-export.js # Trace to Markdown export +│ │ │ ├── transcribe.js # Audio transcription +│ │ │ ├── cloud-output.js # Cloud run output handling +│ │ │ ├── runtime-context.js # Trusted runtime context +│ │ │ ├── workflows.js # Workflow orchestration +│ │ │ ├── chat-workflow.js # Chat workflow handling +│ │ │ ├── adapter-workflow.js / adapter-workflow-evidence.js # Adapter workflow evidence +│ │ │ ├── completion-invariant.js # Completion invariant checks +│ │ │ ├── conversation-persistence.js # Conversation persistence +│ │ │ ├── message-recipient-guard.js # Recipient guard (Gmail/X/social) +│ │ │ ├── systemone-fast.js / systemone-judge.js / systemone-evidence.js # Jev system +│ │ │ ├── otp-email-tool.js / otp-* # OTP email mailbox reader +│ │ │ ├── research-escalation.js # Research escalation +│ │ │ ├── social-media-downloader.js # Social media content downloading +│ │ │ ├── social-publish-contract.js # Social publication contract +│ │ │ ├── public-media-url.js # Public media URL resolution +│ │ │ ├── offline-*.js # Offline RAG, retrieval, semantic, reranker, stopwords +│ │ │ ├── zim-xapian.js / zim-xapian-runtime.js # Offline Xapian search +│ │ │ ├── apocalypse-mode.js / emergency-*.js # Apocalypse Mode + Emergency Box +│ │ │ ├── teacher-mode.js / rich-text-toolbar-guard.js / rich-text-toolbar-probe.js +│ │ │ ├── model-output-diagnostics.js # Model output analysis +│ │ │ ├── read-completeness.js # Read completeness tracking +│ │ │ ├── archive-opfs-writer-worker.js # OPFS archiving worker +│ │ │ └── observers/ # Site-specific observers (github, mastodon) +│ │ ├── providers/ # LLM provider abstraction +│ │ │ ├── base.js # BaseLLMProvider +│ │ │ ├── manager.js # ProviderManager (registry + config) +│ │ │ ├── provider-catalog.js # Provider catalog/registry +│ │ │ ├── openai.js # OpenAI-compatible providers +│ │ │ ├── anthropic.js # Anthropic Claude +│ │ │ ├── llamacpp.js # llama.cpp local provider +│ │ │ ├── azure-openai.js # Azure OpenAI +│ │ │ ├── aws-bedrock.js # AWS Bedrock +│ │ │ ├── vertex-anthropic.js # Google Vertex AI via Anthropic +│ │ │ ├── context-windows.js # Context window detection/normalization +│ │ │ ├── provider-compatibility.js # Provider feature flags +│ │ │ ├── fetch-with-fallback.js # HTTP fetch with fallback +│ │ │ ├── oauth-claude.js # Claude OAuth flow +│ │ │ ├── oauth-subscriptions.js # OAuth subscription management +│ │ │ ├── deepseek.js / deepseek-config.js # Dedicated DeepSeek provider +│ │ │ ├── vision-capabilities.js # Vision support detection +│ │ │ └── connection-test-assets.js # Connection test fixtures +│ │ ├── content/ # Content scripts (injected into pages) +│ │ │ ├── accessibility-tree.js # AX tree builder + ref_ids +│ │ │ ├── content.js # DOM reader, clicker, typer +│ │ │ ├── agent-visual-indicator.js # Pulsing border + Stop +│ │ │ ├── selection-shortcut.js # Selection handling +│ │ │ ├── redaction-regions.js # PII region collection +│ │ │ ├── file-picker-guard-page.js # File picker guard page +│ │ │ └── ollama-launch-handoff.js # Ollama launch handoff +│ │ ├── network/ # Network tools +│ │ │ └── network-tools.js # fetch_url, downloads, URL validation +│ │ ├── cdp/ # Chrome DevTools Protocol (Chrome only) +│ │ │ ├── cdp-client.js # chrome.debugger wrapper +│ │ │ └── image-utils.js # Image processing +│ │ ├── offscreen/ # Offscreen document (Chrome only) +│ │ ├── recorder/ # Tab/screen recording orchestration +│ │ ├── trace/ # IndexedDB trace recorder +│ │ └── ui/ # Side panel UI +│ │ ├── sidepanel.html / sidepanel.js / sidepanel-window-scope.js +│ │ ├── settings.html / settings.js / settings-tabs.js +│ │ ├── traces.html / traces.js # Trace viewer +│ │ ├── history.html / history.js / history-text.js # Chat history +│ │ ├── i18n.js / locales/ # Internationalization (23 locales) +│ │ ├── markdown-render.js / markdown-link.js / skill-markdown.js +│ │ ├── theme.js / theme-bootstrap.js / ui-scale.js +│ │ ├── recommended-actions.js / store-review-prompt.js / run-error-dedupe.js +│ │ ├── context-menu-prompts.js / chat-history-store.js / tab-chat-persistence.js +│ │ ├── provider-icons.js / message-info.js +│ │ ├── attachment-drop.js / attachment-file.js / staged-screenshot-store.js +│ │ ├── selection-quote.js / watch-command.js / coupon-domains.js +│ │ ├── install.html / install.js / install.css / install-assets/ +│ │ ├── mic-permission.html / mic-permission.js +│ │ ├── pdf-handler.html/js/css # PDF viewer +│ │ ├── wikipedia-*.js/html/css # Wikipedia reader/library +│ │ ├── apocalypse-*.js/html/css # Apocalypse Mode UI +│ │ ├── emergency-*.js/html/css # Emergency Box/comm/PDF/text UI +│ │ ├── offline-rag-readiness.js/css # Offline RAG readiness UI +│ │ ├── safesocial-settings.js # SafeSocial settings +│ │ ├── download-tracker.js/css # Download tracker +│ │ └── utils.js # Shared UI utilities (escapeHtml, etc.) +│ │ ├── cloud-runs.js # Cloud run persistence +│ │ ├── run-capture.js # Run capture (screenshots/recordings) +│ │ ├── run-ui-journal.js # UI journal for runs +│ │ ├── context-menu-storage.js # Context menu storage (claim/ownership races) +│ │ ├── profile-sync.js # Profile synchronization +│ │ ├── run-reconnect.js # Run reconnect helpers +│ │ ├── config-transfer.js # Settings/config transfer +│ │ ├── chrome-protected-pages.js # Chrome restricted-page handling +│ │ ├── chrome-web-store-release.js # Web Store release automation +│ │ ├── download-directory.js # Download directory helpers +│ │ ├── download-result.js # Download result tracking +│ │ ├── error-format.js # Error formatting +│ │ ├── tab-group-preference.js # Tab group preferences +│ │ ├── selection-shortcut-i18n.js # Selection shortcut i18n +│ │ └── ollama-handoff.js # Ollama launch handoff +│ │ +│ └── firefox/ # Firefox MV2 extension (mirrors Chrome structure) +│ ├── manifest.json # MV2 manifest +│ ├── skills/ # Packaged default skills (same as Chrome) +│ ├── icons/ # Extension icons +│ ├── styles/ # Extension CSS +│ ├── vendor/ # Third-party libs (same as Chrome) +│ └── src/ # Same structure as Chrome, minus cdp/, offscreen/, recorder/ +│ # Firefox-exclusive: bidi/ (bind.js, client.js), +│ # background.html, firefox-restricted-domains.js, +│ # shortcut-command.js, watch-alert.js, smd-loader.js, +│ # content/file-picker-guard-loader.js +│ +├── web/ # Marketing site +│ ├── index.html # Generated landing page +│ ├── privacy.html # Privacy policy +│ ├── vercel.json # Vercel config +│ ├── build/ # Build system +│ │ ├── build.mjs # Site generator (pure Node ESM) +│ │ ├── template.html # HTML template with {{t:key}} markers +│ │ ├── locales/ # Locale JSON files +│ │ └── plausible.mjs # Analytics partial +│ ├── assets/ # Site images +│ ├── blog/ # Blog content +│ ├── docs/ # Documentation site +│ └── {locale}/ # Generated locale pages (es, fr, zh, etc.) +│ +├── lmstudio-plugin/ # LM Studio plugin (TypeScript) +│ ├── package.json # @webbrain/lmstudio-web-tools +│ ├── tsconfig.json # TypeScript config +│ ├── src/ # Plugin source +│ └── dist/ # Compiled output +│ +├── mcp-server/ # MCP server (TypeScript, `@webbrain/mcp-server`) +│ ├── package.json # stdio bridge → extension via ws://127.0.0.1:17374 +│ ├── tsconfig.json +│ ├── src/ # bridge.ts, config.ts, index.ts, runs.ts +│ └── test/ # 6 .mjs test suites +│ +├── firefox-companion/ # Firefox WebDriver BiDi trusted-automation companion +│ ├── README.md # Node 22+, native messaging + ws://localhost:9222 +│ ├── host.mjs # webbrain-bidi native-messaging host +│ ├── install.mjs # Companion installer +│ └── session.mjs # BiDi session logic +│ +├── LICENSES/ # Historical license texts (MIT.txt for pre-33.0.0) +│ +├── test/ # Tests (pure Node, no framework) +│ ├── run.js # Main test runner (loads Chrome + Firefox modules) +│ ├── README.md # Test documentation +│ ├── security/ # Security/injection tests +│ ├── fixtures/ # Fixture-based tests +│ ├── anonymous/ # Anonymous usage tests +│ ├── llm/ # LLM scenario tests +│ ├── memory/ # User memory tests +│ ├── smd-tests/ # Social media downloader tests +│ ├── jev/ # Jev classifier tests +│ ├── llm-tiny/ + llm-tiny-v2/ # Small-model test suites +│ ├── vision/ + vision-results/ # Vision probe tests +│ ├── systemone*.mjs # Jev fast-classifier tests +│ ├── safesocial*.mjs # SafeSocial classifier tests +│ ├── firefox-bidi*.mjs # Firefox BiDi companion tests +│ ├── social-publish-contract*.mjs # Social publication contract tests +│ ├── pdf-read.mjs / pdf-selection.mjs / pdf-mime-handler-e2e.mjs # PDF tests +│ ├── provider-model-limits.mjs # Provider model limits +│ ├── attachment-drop.mjs / build-unpacked.mjs / rich-text-toolbar-guard.mjs +│ ├── browser-dialogs*.mjs / agent-lifecycle.mjs / runtime-lifecycle.mjs +│ ├── webmcp-e2e.mjs # WebMCP end-to-end test +│ ├── vision-probe.mjs # Vision probe test +│ ├── manual-permissions.md # Manual permissions testing guide +│ └── manual-screenshot-redaction.md # Manual screenshot redaction testing guide +│ +├── ci/ # CI/CD pipeline +│ ├── run.mjs # CI runner +│ ├── test.mjs # CI test runner +│ ├── README.md # CI documentation +│ ├── cloud-capture.test.mjs # Cloud capture tests +│ ├── lib/ # CI shared libraries +│ │ ├── webbrain-client.mjs # WebBrain client +│ │ ├── grader.mjs # Test grader +│ │ └── suite.mjs # Test suite +│ └── catalog/ # Test catalogs +│ └── scenarios.json # Scenario definitions +│ +├── scripts/ # Build/dev scripts +│ ├── build-blog.mjs # Blog builder +│ ├── build-docs.mjs # Docs builder +│ ├── build-zip.mjs # Release zip builder +│ ├── build-zim-xapian.mjs # ZIM/Xapian index builder +│ ├── build-unpacked.mjs # Unpacked build helper +│ ├── bump-version.mjs # Version bumper +│ ├── update-changelog.mjs # Changelog updater +│ ├── update-coupon-domains.mjs # Coupon domain list updater +│ ├── trace-to-otlp.mjs # Trace → OTLP exporter +│ ├── preview-web.mjs # Web preview server +│ ├── i18n-perm-translations.mjs # i18n permission translations +│ ├── gen-store-promos.py # Store promo generator +│ └── sync-logo-assets.py # Logo sync +│ +├── docs/ # Documentation +│ ├── architecture.md # System architecture +│ ├── adding-a-tool.md # New tool checklist +│ ├── accessibility-tree-and-refs.md # AX refs and page reads +│ ├── site-adapters.md # Adapter guide +│ ├── providers-and-models.md # Provider config +│ ├── localization.md # i18n workflow +│ ├── privacy-and-data-flow.md # Data handling +│ ├── security-model.md # Permissions and risk +│ ├── prompt-injection-defense.md # Injection defense +│ ├── THREAT-MODEL.md # Threat model +│ ├── test-scenarios.md # Test scenarios +│ ├── claude-chrome-comparison.md # Comparison with Claude Chrome +│ ├── agent-tools.md / skills.md / slash-commands.md # Tooling references +│ ├── offline-rag.md / offline-rag-licensing.md / offline-rag-release-checklist.md +│ ├── apocalypse-mode.md / remote-downloads.md / community.md / discord-setup.md +│ ├── export-and-workflow-formats.md / social-publication-contract.md +│ ├── selection-context-verification.md / trace-format-compatibility.md +│ ├── accessibility-tree-benchmark.md / browser-agent-interface-findings.md +│ ├── vision-models/ # Vision model docs +│ └── zh-CN/ # Chinese documentation +│ └── fr/ # French documentation +│ +├── assets/ # Logo and promo assets +├── dist/ # Built release zips +├── package.json # Root package.json +├── AGENT.md # Agent guidelines (MUST READ) +├── README.md # Project README +├── README.es.md # Spanish README +├── README.fr.md # French README +├── README.zh-CN.md # Chinese README +├── CHANGELOG.md # Version history +├── CONTRIBUTING.md # Contribution guide +├── CODE_OF_CONDUCT.md # Code of conduct +├── GOVERNANCE.md # Project governance +├── LICENSE # GPL-3.0-or-later +├── LICENSES/ # Historical licenses (MIT.txt for pre-33.0.0) +├── SECURITY.md # Security policy +├── TODOs.md # Feature backlog +└── .gitattributes # Git attributes +``` + +## Conversation Modes + +WebBrain separates **model tier** from **conversation mode**: + +### Tiers (`compact | mid | full`) + +Control how many normal browser-agent tools a model sees: + +- **Compact**: Reduced tool set + shorter system prompt for smaller local models +- **Mid**: Common task tools, iframe support, downloads, scheduling, form verification +- **Full**: Advanced browser-operation tools (hover, drag-drop, frames, shadow DOM) + +### Modes (`ask | act | dev`) + +Control what kind of task the user is allowing: + +- **Ask**: Read-only. Agent can read, analyze, summarize but never click, type, or navigate +- **Act**: Exposes the selected tier's normal browser-agent tools +- **Dev**: Requires Mid/Full provider. Adds source/style/debug tools, page inspection, execute_js + +## Agent Tools + +Key tools organized by category (full list ~80 tools in `src/chrome/src/agent/tools.js`): + +### Read-only (Ask + all modes) + +- `get_accessibility_tree` — AX tree with ref_ids (preferred reader; auto-slices with continuation metadata, returns structured `pageGate` on rendered login/registration/paywall surfaces, Gmail `conversationRootRefId` support) +- `read_page` — Prose fallback for long-form articles; windowed with `continuationArgs`/`accessState`/`pageGate` +- `read_pdf` — PDF text extraction (pdfjs-dist; `hasExtractableText` flag; paginate with fromPage/toPage) +- `get_window_info` — Page metadata +- `get_interactive_elements` — Interactive elements +- `scroll` — Scroll the page (pane-aware via ref_id/x-y) +- `extract_data` — Structured data extraction +- `get_selection` — Selected text +- `fetch_url` — HTTP requests +- `research_url` — Extract readable article body +- `delegate_research` — Escalate to a research specialist runs +- `find_text` — Locate text on the page +- `gmail_count_results` — Gmail result counts +- `list_webmcp_tools` / `execute_webmcp_tool` — WebMCP tool discovery/execution +- `done` — Complete the task + +### Action (Compact+) + +- `click` / `click_ax` — Click elements +- `type_text` / `type_ax` / `set_field` / `set_checked` — Form interaction +- `press_keys` — Keyboard shortcuts +- `navigate` / `new_tab` / `list_tabs` / `activate_tab` — Navigation and tabs +- `carousel_navigate` — Carousel stepping +- `wait_for_element` / `wait_for_stable` — Wait conditions +- `scratchpad_write` — Write to scratchpad +- `progress_update` / `progress_read` — Progress tracking +- `chat_observe` / `chat_send` — Chat workflow observation + send +- `clarify` — Pause and ask the user (mid-call) +- `beep` — Audible notification +- `upload_file` — File uploads + +### Advanced (Mid+) + +- `go_back` / `go_forward` — Browser history +- `download_files` / `list_downloads` / `read_downloaded_file` / `download_resource_from_page` — Downloads +- `download_social_media` — Social media content download +- `schedule_task` / `schedule_resume` — Scheduled tasks +- `iframe_read` / `iframe_click` / `iframe_type` / `promote_iframe` — iframe interaction +- `solve_captcha` — CAPTCHA solving +- `verify_form` — Form verification +- `resize_window` / `inspect_viewport` — Window/viewport control + +### Full-tier + +- `hover` — Mouse hover (CDP-trusted) +- `drag_drop` — Drag and drop +- `get_shadow_dom` / `shadow_dom_query` — Shadow DOM access +- `get_frames` — Frame enumeration + +### Dev-only + +- `read_page_source` — View page source +- `inspect_element_styles` — Inspect CSS +- `execute_js` — Run JavaScript +- `inject_css` / `remove_injected_css` — Reversible CSS +- `patch_element` / `revert_patch` — Reversible DOM edits +- `highlight_element` — Visual overlay +- `read_console` — Console logs +- `inspect_network_requests` — Network requests +- `inspect_event_listeners` — Event listeners + +## Provider System + +All providers extend `BaseLLMProvider` and normalize to: + +```js +{ content: string, toolCalls: Array|null, usage: Object|null } +``` + +### Local Providers (no API key needed) + +- **llama.cpp** (port 8080) +- **Ollama** (port 11434/v1) +- **LM Studio** (port 1234/v1) +- **Jan** (port 1337/v1) +- **vLLM** (port 8000/v1) +- **SGLang** (port 30000/v1) +- **LocalAI** (port 8080/v1) +- **GPT4All** (local OpenAI-compatible) +- **WebGPU** — in-browser LLM inference (Compass Tiny v2.1, MiniCPM5-2B, LFM2.5, Bonsai 27B, Nanbeige4.2-3B presets); Chrome-only + +### Cloud Providers (API key required) + +- **WebBrain Compass** — Managed cloud (default; formerly "WebBrain Cloud 1.0", renamed 2026-09-01) +- **OpenAI** — GPT-5.6, etc. +- **Anthropic Claude** — Native API +- **Google Gemini**, **Mistral AI**, **DeepSeek** (dedicated provider), **xAI Grok**, **Groq** +- **MiniMax**, **Alibaba Cloud (Qwen)** +- **Cloudflare Workers AI**, **Nvidia NIM** +- **OpenRouter** — 100+ models (default: `openrouter/free`) + routing variants +- **Azure OpenAI**, **AWS Bedrock** +- **Unsloth Studio** (added 2026-08-25), **Pollinations AI** (2026-09-09), **NEAR AI Cloud** (2026-09-17) +- Plus **76 additional provider cards** in `provider-catalog.js` (104 built-in cards total, pre-filled base URLs and defaults — see `docs/providers-and-models.md`) — now with configurable model limits + +## Skills System + +Skills are optional prompt guidance + tool extensions: + +- Stored in `chrome.storage.local` / `browser.storage.local` +- Packaged defaults ship in `src/{chrome,firefox}/skills/` +- Current defaults (12): FreeSkillz.xyz, OTP helper, Disposable email (Mail.tm), Open-Meteo weather, Open Library, Litterbox, Frankfurter FX, Wikipedia, Humanizer, Phonr calls, Turkish deasciifier, Chrome Web Store release +- Skills can expose HTTP tools via `webbrain-tools` manifest +- Skills are loaded per-run based on user intent (routed by planner or model) +- Agent Skills trust boundary enforced (structural plain scalars rejected, frontmatter validated) + +## Site Adapters + +138 site-specific adapters inject guidance into the first user message: + +- Only ONE adapter fires at a time (first match) +- Adapters inject selectors, URL patterns, visible text, traps, success indicators +- 29+ added since 2026-08: India bundle (Swiggy, IRCTC, Paytm, Snapdeal), CIS/MENA (Wildberries, Avito, VK, Noon), LATAM (OLX, Despegar), East Asia/RU (Mercari, Yahoo JP, Naver, Yandex Market), Africa/MENA (Jumia, Kilimall, Careem, Talabat), EU/SEA (Bol, Otto, Willhaben, Tokopedia), baidu-tieba expansion +- Site adapters are high-leverage product work (short, concrete notes) + +## Key Subsystems + +### Plan before Act + +- Optional action-mode planning gate +- Runs before first browser tool call +- Returns structured JSON with summary, steps, skill_ids, risks +- User approves/rejects before execution + +### Loop Detection + +Three independent detectors: + +1. **General repeat** — Last 6 tool calls by (name + args hash + outcome) +2. **Coordinate click** — 5px-bucketed clicks +3. **Navigation** — URL snapshot before/after actions + +### Context Management + +- **Auto-compaction** — Summarizes older turns when nearing context window +- **Emergency trim** — Keeps only last 6 messages on overflow +- **Image pruning** — Strips base64 images from all but last 4 messages +- **Tool result cap** — 8KB per result + +### User Memory + +- Local, user-stated durable preferences +- Stored in `wb_user_memory_v1` +- Injected into system prompt as bounded block +- Optional auto-learning (off by default) + +### Scheduling + +- Deferred work using browser `alarms` API +- Job kinds: `resume` (continue conversation) and `task` (standalone prompt) +- Supports one-shot and recurring schedules +- Jobs persist in `chrome.storage.local` + +### Offline RAG + Xapian (Apocalypse Mode) + +- Offline-first question answering over locally-synced corpora (Wikipedia etc.) +- Semantic runtime, retrieval + reranker, query stopwords, ZIM/Xapian index (`scripts/build-zim-xapian.mjs`) +- Apocalypse Mode + Emergency Box UIs; teacher mode; OPFS archiving worker +- `docs/offline-rag.md` for details + +### Jev Fast-Classifier System (systemone) + +- Uses small/fast classifiers ("safe-stop", "systemone") to decide when an agent run should stop/pause before expensive slow-path inference +- TypeSafe scheduler judge → Assistive Models; fast-flow screenshot handoffs + event-driven wakeups +- `test/systemone*.mjs` suites (Some require Playwright browsers) + +### Social Recipient Guard + +- Pre-send recipient verification for Gmail/X/social DM composition +- X group DM recipient binding, message-history delivery proofs; generic-first guard on any site +- User-granted send authorization, fail-open guard, silent-reply recovery +- `message-recipient-guard.js` + site adapters + +### PDF Tooling + +- `read_pdf` agent tool (pdfjs-dist, per-page extraction, vision fallback note) +- PDF extraction/OCR/stream modules; PDF viewer + Wikipedia reader UI in the sidepanel + +## Security Model + +- MV3 manifest permissions: `sidePanel`, `activeTab`, `contextMenus`, `tabs`, `tabGroups`, `scripting`, `storage`, `notifications`, `webNavigation`, `webRequest`, `debugger`, `downloads`, `alarms`, `unlimitedStorage`, `offscreen`, `privateNetworkAccess`, `tabCapture`, `clipboardWrite/Read` + `` +- Ask is read-only; Act/Dev are action modes +- Plan before Act can require human approval +- `/allow-api` flag gates destructive HTTP methods +- Tool results capped at 8KB to limit injection surface +- `strictSecretMode` prevents credential quoting +- Trace data is local-only (IndexedDB) +- Untrusted content wrapped with `_wrapUntrusted` boundaries +- Screenshot redaction + URL redaction hardening for PII +- Social recipient guard: pre-send verification for Gmail/X/social DMs + +## Build System + +### Extension + +**No build step** — load directly from `src/chrome/` or `src/firefox/` + +`npm run build:chrome|build:firefox|build:all` — optional `scripts/build-unpacked.mjs` packaging + +### Marketing Site + +```bash +npm run build:web # Build landing page + blog + docs +npm run build:web:landing # Build landing page only +npm run build:blog # Build blog only +npm run build:docs # Build docs only +npm run preview:web # Preview site +``` + +### Release + +```bash +npm run build:zip # Build release zip +npm run bump # Bump version +npm run release # Bump + release +``` + +## Testing + +- `npm test` — Full suite (systemone, firefox-bidi, lifecycle, provider-limits, benchmark, toolbar-guard, pdf, social, safesocial, build-unpacked, attachment-drop, `test/run.js`, selection-scope, security); several suites need Playwright browsers (`npx playwright install`) +- `node test/run.js` — Core regression suite (pure Node, no framework, no chrome.\* APIs) — **2396 tests** +- `npm run test:security` — Security/injection tests +- `npm run test:injection-bench` — Prompt injection benchmarks +- `npm run test:safety-report` — Safety report +- `npm run test:fixtures` — Fixture tests +- `npm run test:anonymous` — Anonymous usage tests +- `npm run test:systemone[:fast|:ui]` — Jev classifier suite (+ DOM/UI variants) +- `npm run test:safesocial[:ui|:extension]` — SafeSocial suite +- `npm run test:firefox-bidi` / `:e2e` — Firefox BiDi companion tests +- `npm run test:social-contract[:dom]` — Social publication contract tests +- `npm run test:provider-limits` — Provider model limits +- `npm run test:pdf-read` / `test:pdf-selection` — PDF tooling tests +- `npm run test:attachment-drop` — Drag-and-drop attachment tests +- `npm run test:vision` / `test:webmcp` — Vision / WebMCP tests +- `node --check ` — Syntax-only JS check on touched files + +Tests import Chrome and Firefox modules directly (no browser needed) and verify: + +- Tool classifications and parity +- Provider config and compatibility +- Adapter matching +- Loop detection +- Permission gating +- Skills catalog and loading +- Markdown rendering +- Version bumping +- Changelog management +- And more + +## Code Conventions + +- **No frameworks** — Vanilla JS/CSS unless very strong reason +- **No build step for extension** — Direct load for development +- **Mirror changes** — Changes should be mirrored across Chrome and Firefox unless platform makes parity impossible +- **Keep tool schemas narrow** — Stable tool names, arguments, result shapes +- **Comments explain why** — Browser quirks, prompt rules, permission gates, context choices +- **Adapter notes imperative** — Name selectors, visible text, URL patterns, traps, success indicators +- **No broad dependencies** — Extension is intentionally simple to load and inspect + +## Key Files to Read First + +1. `AGENT.md` — Agent guidelines and goals +2. `docs/architecture.md` — System overview +3. `docs/adding-a-tool.md` — New tool checklist +4. `docs/site-adapters.md` — Adapter guide +5. `docs/providers-and-models.md` — Provider config +6. `docs/prompt-injection-defense.md` — Injection defense +7. `docs/security-model.md` — Permissions and risk +8. `docs/offline-rag.md` — Offline RAG/Apocalypse Mode +9. `docs/skills.md` / `docs/slash-commands.md` — Skills and slash commands +10. `src/chrome/src/agent/tools.js` — Tool definitions +11. `src/chrome/src/agent/agent.js` — Agent loop +12. `src/chrome/src/providers/manager.js` — Provider registry + +## Current Status (as of 2026-09-22) + +### Test Suite + +- **2395 passed, 1 failed** (2396 total) via `node test/run.js` +- Failing test: + 1. `version 33-and-later licensing boundary is consistent across project metadata and FAQ copy` — **active regression**: commit `0b37aab73` (2026-09-21) removed the "releases before 33.0.0 remain MIT" paragraph from `LICENSE` that this test greps for. Fix either test or LICENSE/FAQ copy. +- Note: `npm test` runs additional Playwright suites (`test:systemone`, etc.) that need `npx playwright install` — environment, not code. + +### Code Health + +- **0 syntax errors** across key files (verified via `node --check`) +- **0 TODO/FIXME/HACK/BUG comments** in source `.js` files +- **11 engineering TODOs** tracked in `TODOs.md` (3 resolved, several partially resolved, rest open) +- Full issues report: `.claude/issues-report.md` + +### Recent Changes + +- **Licensing**: MIT → GPL-3.0-or-later effective 33.0.0; `LICENSES/MIT.txt` retains historical MIT text; README (incl. fr/zh-CN) license statements synced (commit `0b37aab73`) +- **Recipient guard / social-messaging hardening** (~30 commits, dominant theme): X group DM recipient binding, message-history delivery proofs, LinkedIn post composer allowance + localized controls, generic-first recipient guard on any site, fail-open guard + user-granted send authorization, silent-reply recovery, screenshot budget, shadow-host `aria-hidden` traversal fix in `_hasVisibleBox` +- **SafeSocial**: Instagram classifier + settings export (Chrome+Firefox), opt-in Jev classifiers with guarded browser decisions — v36.8.0 +- **Jev fast-classifier system** (systemone): TypeSafe scheduler judge moved to Assistive Models, fast-flow screenshot handoffs, event-driven wakeups, malformed-response/reconciliation fixes +- **MCP server** (`mcp-server/`): stdio bridge so Claude Code/Codex/Cursor/OpenClaw can drive the running extension (`ws://127.0.0.1:17374`); made browser-optional and Chromium-focused +- **Firefox BiDi companion** (`firefox-companion/`): WebDriver BiDi native-messaging host giving Firefox trusted `isTrusted` input (clicks, typing, selects, uploads) +- **Providers**: dedicated DeepSeek provider; Unsloth Studio, Pollinations AI, NEAR AI Cloud added; WebGPU provider restored (Compass Tiny v2.1, MiniCPM5-2B, LFM2.5, Bonsai 27B, Nanbeige4.2-3B presets); managed default renamed WebBrain Cloud 1.0 → **WebBrain Compass**; configurable model limits +- **Offline RAG + Xapian**: offline retrieval/semantic/reranker/runtime, ZIM/Xapian index builder (`scripts/build-zim-xapian.mjs`, `zim-xapian.js`, `zim-xapian-runtime.js`), offline stopword handling +- **PDF viewer + OCR**: `pdf-extraction.js`, `pdf-ocr.js`, `pdf-stream.js`, PDF read/selection test suites, OPFS archiver worker +- **Theme/content**: Apocalypse Mode + Emergency Box, teacher mode, rich-text toolbar guard/probe, OTP skill-gated mailbox reader (`otp-email-tool.js`), drag-and-drop attachments, localized tool-action labels, URL redaction hardening +- **Site adapters**: +29 since 2026-08 → **138 total** (India, CIS/MENA, LATAM, East Asia/RU, Africa/MENA, EU/SEA bundles, baidu-tieba expansion) +- **Skills**: Humanizer and Phonr calls added to packaged defaults (now 12) +- Releases: v33.0.0 (2026-08-20) … v35.0.0, v36.0.0 (2026-09-09) … **v36.8.0 (2026-09-20)**, current dev version 36.8.0 + +### Known Firefox Parity Gaps + +- `upload_file` — available in Firefox (user-picker resolve path) +- `full_page_screenshot` — retired as an agent tool in both builds (`RETIRED_AGENT_TOOL_NAMES`); `/screenshot --full-page` unsupported in Firefox (no scroll-and-stitch fallback) +- `get_shadow_dom` — dispatched in Firefox with open roots only ("Closed shadow roots are not accessible in Firefox" per tool schema); `shadow_dom_query` — **not implemented** in Firefox (no tool def, no content dispatch); still referenced in dead sets (`COMPLETION_DOCUMENT_OBSERVATION_TOOLS`, `WORKFLOW_CONTENT_READ_TOOLS`). `firefox-companion/` BiDi layer mitigates other CDP gaps. + +### Chrome/Firefox Source Drift (verified 2026-09-22) + +- **Chrome-only agent files:** `offline-answer-copy.js`, `offline-retrieval-offscreen.js`, `pdf-extraction.js`, `transcribe.js` (Chrome agent dir 80 files vs Firefox 77) +- **Firefox-only agent files:** `smd-loader.js` +- **Chrome-only infra:** `cdp/`, `offscreen/*`, `cloud-runs.js`, `recorder/host.js`, `providers/webgpu.js` +- **Firefox-only:** `bidi/` (bind.js, client.js), `background.html`, `firefox-restricted-domains.js`, `shortcut-command.js`, `watch-alert.js`, `content/file-picker-guard-loader.js` +- **Size drift:** `agent.js` Chrome 44,081 vs Firefox 36,714 (+20%), `content.js` Chrome 8,345 vs Firefox 7,291, `oauth-claude.js` Chrome +35% + +### Prompt System TODO + +- Compact-vs-full prompt contradiction is **partially resolved** (compact routing is per-provider opt-in); remaining work is prompt quality / model-tier selection +- Proposed: three-tier system (frontier/mid/small) instead of binary +- See `TODOs.md` item #1 for detailed analysis diff --git a/.claude/setup.md b/.claude/setup.md new file mode 100644 index 000000000..ccd53f483 --- /dev/null +++ b/.claude/setup.md @@ -0,0 +1,305 @@ +# WebBrain Setup & Run Flow + +## Prerequisites + +- **Node.js** >= 18 (for tests, scripts, web build, lmstudio-plugin) +- **Chrome** or **Firefox** browser +- **LLM access** (one of): + - Local: llama.cpp, Ollama, LM Studio, Jan, vLLM, SGLang, LocalAI, or GPT4All + - Cloud: API key for OpenAI, Anthropic, Google, OpenRouter, etc. + - Or use WebBrain Compass (managed cloud, no setup needed — the default provider) + +--- + +## 1. Clone the Repository + +```bash +git clone https://github.com/webbrain-one/webbrain.git +cd webbrain +``` + +## 2. Install Dependencies + +```bash +npm install +``` + +This installs `playwright` (dev dependency for tests). The extension itself has zero dependencies. + +For the LM Studio plugin (optional): + +```bash +cd lmstudio-plugin +npm install +cd .. +``` + +--- + +## 3. Load the Extension + +### Chrome (Recommended) + +1. Open Chrome → `chrome://extensions/` +2. Enable **Developer mode** (top right toggle) +3. Click **Load unpacked** +4. Select the `src/chrome` folder from the cloned repo +5. The WebBrain icon appears in the toolbar + +### Firefox + +1. Open Firefox → `about:debugging#/runtime/this-firefox` +2. Click **Load Temporary Add-on** +3. Navigate to `src/firefox/` and select `manifest.json` +4. The WebBrain icon appears in the toolbar + +> Note: Firefox temporary add-ons are removed on restart. For permanent installation, the extension needs signing via addons.mozilla.org. + +--- + +## 4. Set Up an LLM Provider + +### Option A: WebBrain Cloud (No Setup) + +- Default provider, no API key needed +- Works immediately after loading the extension + +### Option B: Local Model + +Start your local LLM server: + +```bash +# llama.cpp +llama-server -m your-model.gguf --port 8080 + +# Ollama +ollama serve +# Then in WebBrain settings, set base URL to http://localhost:11434/v1 + +# LM Studio +# Start LM Studio's local API server (default port 1234) + +# Jan +# Start Jan's local API server (default port 1337) + +# GPT4All +# Start GPT4All's local server (OpenAI-compatible) + +# vLLM +vllm serve your-model --port 8000 + +# SGLang +python -m sglang.launch_server --model-path your-model --port 30000 +``` + +Then in WebBrain settings: + +1. Click the gear icon (or extension Options page) +2. Select your provider (llama.cpp, Ollama, etc.) +3. The base URL is pre-filled for known providers +4. Click **Test Connection** to verify + +> Context window: Use a model with at least 16k tokens for reliable agent runs. 8k works with Compact mode. 4k is too small. + +### Option C: Cloud Provider + +1. Click the gear icon in WebBrain +2. Select your provider (OpenAI, Anthropic, etc.) +3. Enter your API key +4. Select a model +5. Click **Test Connection** + +--- + +## 5. Use WebBrain + +1. Click the WebBrain icon → the side panel opens +2. Type a message like: + - "Summarize this page" + - "Find all links about pricing" + - "Fill in the search box with 'AI agents' and click Search" + - "Navigate to github.com and find trending repositories" + +### Conversation Modes + +- **Ask** (default) — Read-only, safe by default +- **Act** — Browser actions (click, type, navigate) +- **Dev** — Page debugging and HTML/CSS inspection + +### Slash Commands + +Type `/` in the input to see available commands: + +- `/help` — Show all commands +- `/ask` — Switch to Ask mode +- `/act` — Switch to Act mode +- `/dev` — Switch to Dev mode +- `/compact` — Force context compaction +- `/reset` — Clear conversation +- `/memory` — Manage user memory +- `/export` — Download conversation + +### Keyboard Shortcuts + +- `Ctrl+/` or `Cmd+/` — Focus input +- `Ctrl+Shift+A` or `Cmd+Shift+A` — Ask mode +- `Ctrl+Shift+X` or `Cmd+Shift+X` — Act mode +- `Ctrl+Shift+D` or `Cmd+Shift+D` — Dev mode +- `Escape` — Stop active run + +--- + +## 6. Run Tests + +```bash +# Main regression suite (pure Node, no browser needed) +npm test + +# Security/injection tests +npm run test:security + +# Prompt injection benchmarks +npm run test:injection-bench + +# Safety report +npm run test:safety-report + +# Fixture tests +npm run test:fixtures + +# Anonymous usage tests +npm run test:anonymous + +# Syntax-only check on a specific file +node --check src/chrome/src/agent/agent.js +``` + +The test runner (`test/run.js`) imports Chrome and Firefox modules directly under Node (no chrome.\* APIs needed for most tests) and verifies parity between browsers. + +--- + +## 7. Build the Marketing Site + +After editing files in `web/`: + +```bash +# Build everything (landing page + blog + docs) +npm run build:web + +# Build landing page only +npm run build:web:landing + +# Build blog only +npm run build:blog + +# Build docs only +npm run build:docs +``` + +The site generator reads `web/build/template.html` + `web/build/locales/*.json` and writes generated HTML files. **Do not hand-edit** `web/index.html` or `web/*/index.html` directly — the next build will overwrite them. + +--- + +## 8. Build the LM Studio Plugin (Optional) + +```bash +cd lmstudio-plugin +npm install +npm run build # Compiles TypeScript to dist/ +npm run dev # Watch mode +cd .. +``` + +--- + +## 9. Release Process + +```bash +# Bump version in package.json, manifest.json, etc. +npm run bump + +# Build release zip for Chrome Web Store +npm run build:zip + +# Bump + release +npm run release +``` + +--- + +## 10. Development Workflow + +### Making Changes + +1. Edit files in `src/chrome/` or `src/firefox/` +2. Mirror changes across browsers unless platform makes parity impossible +3. Reload the extension in the browser: + - Chrome: `chrome://extensions/` → click refresh icon on WebBrain card + - Firefox: `about:debugging` → click "Reload" +4. Test manually in the browser + +### Adding a New Tool + +1. Read `docs/adding-a-tool.md` for the checklist +2. Add tool schema in `src/*/src/agent/tools.js` +3. Add implementation in `src/*/src/agent/agent.js` or content script +4. Mirror across Chrome and Firefox +5. Add tests in `test/run.js` +6. Run `npm test` to verify + +### Adding a New Provider + +1. Create a new class extending `BaseLLMProvider` in `src/*/src/providers/` +2. Implement `chat()` and optionally `chatStream()` +3. Register it in `src/*/src/providers/manager.js` +4. Mirror across Chrome and Firefox + +### Adding a Site Adapter + +1. Read `docs/site-adapters.md` for the guide +2. Add adapter in `src/*/src/agent/adapters.js` +3. Test on the target site + +--- + +## Quick Reference + +| Task | Command | +| ---------------------- | ----------------------------------------------------------------------- | +| Load Chrome extension | `chrome://extensions/` → Load unpacked → `src/chrome` | +| Load Firefox extension | `about:debugging` → Load Temporary Add-on → `src/firefox/manifest.json` | +| Run tests | `npm test` | +| Build web | `npm run build:web` | +| Build LM Studio plugin | `cd lmstudio-plugin && npm run build` | +| Build release zip | `npm run build:zip` | +| Bump version | `npm run bump` | +| Syntax check | `node --check ` | + +--- + +## Troubleshooting + +### Extension won't load + +- Ensure Developer mode is enabled (Chrome) +- Check browser console for errors +- Verify you selected the correct folder (`src/chrome` or `src/firefox`) + +### Local model not connecting + +- Ensure the local server is running +- Check the port matches (default ports listed above) +- Click "Test Connection" in WebBrain settings +- Verify CORS settings if using Firefox (local providers need CORS headers) + +### Tests failing + +- Ensure Node.js >= 18 is installed +- Run `npm install` first +- Check if the failing test is browser-specific (Chrome-only or Firefox-only) + +### Web build not working + +- Ensure you edited files in `web/build/` (not `web/index.html` directly) +- Run `npm run build:web` after changes +- Check `web/build/locales/*.json` for translation issues diff --git a/.gitignore b/.gitignore index 1d7725c51..70eb110cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -.claude/ +# .claude/ .DS_Store node_modules/ test/anonymous/.test-profile/ diff --git a/docs/providers-and-models.md b/docs/providers-and-models.md index d5b6d3db0..2f34534e7 100644 --- a/docs/providers-and-models.md +++ b/docs/providers-and-models.md @@ -6,13 +6,28 @@ **Settings → Providers** selects the main model for conversation, planning and final replies. **Settings → Assistive Models** groups Vision (including screenshot -limits and redaction), Speech to text, Jev (TypeSafe), and SafeSocial. Configuring an assistive +limits and redaction), Speech to text, Generative media (fal.ai, powering the +`generate_image` agent tool), Jev (TypeSafe), and SafeSocial. Configuring an assistive model does not replace the active provider. Jev is outside the dynamic provider list; its verification, fast-classification and experimental browser switches are independent opt-ins. See [the settings guide](https://webbrain.one/docs/settings/#multimodal) and [data flow](privacy-and-data-flow.md#optional-jev-typesafe-scheduled-task-verification) for setup and disclosure details. Existing `#multimodal` settings links still work. +### Generative media (fal.ai) + +The `generate_image` agent tool submits a text prompt to fal.ai's queue API +(`agent/fal-media.js`) and returns the hosted media URL. Configure the API key +and model under **Settings → Assistive Models → Generative media (fal.ai)**. +The settings card offers **Test Connection** (a cheap auth probe that never +generates media) and per-card **Save / Clear** controls. The tool is a full-tier +Act-mode tool: it is **not** offered in Ask mode or on the compact/mid normal +tool surface, so use a Full-tier provider in Act mode to exercise it. + +The Act system prompt and the planner tool catalog tell the model to call +`generate_image` whenever the user asks to generate media, rather than +navigating to third-party image sites. + ### SafeSocial image classifier (experimental) **Settings → Assistive Models → SafeSocial** optionally filters Instagram images @@ -81,39 +96,39 @@ class BaseLLMProvider { ## Built-in Providers -| Provider ID | Type | Category | Default Model | Vision | -|---|---|---|---|---| -| `webbrain_cloud` | `openai` | cloud | `webbrain-cloud 1.0` | Yes | -| `llamacpp` | `llamacpp` | local | (loaded model) | Auto metadata / override | -| `ollama` | `openai` | local | (loaded model) | Auto via `/api/show` / override | -| `lmstudio` | `openai` | local | (loaded model) | Auto metadata / override | -| `jan` | `openai` | local | (loaded model) | Yes (default on) | -| `vllm` | `openai` | local | (loaded model) | Yes (default on) | -| `sglang` | `openai` | local | (loaded model) | Yes (default on) | -| `localai` | `openai` | local | (loaded model) | Auto metadata / override | -| `gpt4all` | `openai` | local | (loaded model) | Yes (default on) | -| `local_openai_proxy` | `openai` | local | (required) | Off / manual toggle | -| `unsloth` | `openai` | local | (required) | Off / manual toggle | -| `webgpu` (Chromium) | `webgpu` | local | Compass Tiny v2.1 (only preset); experimental custom HF ONNX repos | No | -| `azure_openai` | `azure_openai` | cloud | (deployment) | Manual toggle | -| `aws_bedrock` | `aws_bedrock` | cloud | (model id) | No | -| `openai` | `openai` | cloud | `gpt-5.6-terra` | Model-name regex | -| `anthropic` | `anthropic` | cloud | `claude-sonnet-4-6` | Model-name regex | -| `gemini` | `openai` | cloud | `gemini-3.1-flash` | Model-name regex | -| `cloudflare` | `openai` | router | `@cf/zai-org/glm-5.2` | Model-name regex | -| `mistral` | `openai` | cloud | `mistral-large-latest` | Model-name regex | -| `deepseek` | `openai` | cloud | `deepseek-flash` | Model-name regex | -| `xai` (Grok) | `openai` | cloud | `grok-4.3` | Model-name regex | -| `nvidia` (NIM) | `openai` | router | `meta/llama-3.1-8b-instruct` | Model-name regex | -| `groq` | `openai` | router | `llama-3.3-70b-versatile` | Model-name regex | -| `minimax` | `openai` | cloud | `minimax-m2.7` | Model-name regex | -| `kimi` | `openai` | cloud | `kimi-k2.5` | Model-name regex | -| `alibaba` (Qwen) | `openai` | cloud | `qwen-max` | Model-name regex | -| `together` | `openai` | router | `meta-llama/Llama-3.3-70B-Instruct-Turbo` | Model-name regex | -| `openrouter` | `openai` | router | `openrouter/free` | Model-name regex | -| `huggingface` | `openai` | router | `zai-org/GLM-5.2` | Model-name regex | -| `fireworks` | `openai` | router | `accounts/fireworks/models/llama-v3p3-70b-instruct` | Model-name regex | -| `z_ai` | `openai` | cloud | `glm-5.2` | Model-name regex | +| Provider ID | Type | Category | Default Model | Vision | +| -------------------- | -------------- | -------- | ------------------------------------------------------------------ | ------------------------------- | +| `webbrain_cloud` | `openai` | cloud | `webbrain-cloud 1.0` | Yes | +| `llamacpp` | `llamacpp` | local | (loaded model) | Auto metadata / override | +| `ollama` | `openai` | local | (loaded model) | Auto via `/api/show` / override | +| `lmstudio` | `openai` | local | (loaded model) | Auto metadata / override | +| `jan` | `openai` | local | (loaded model) | Yes (default on) | +| `vllm` | `openai` | local | (loaded model) | Yes (default on) | +| `sglang` | `openai` | local | (loaded model) | Yes (default on) | +| `localai` | `openai` | local | (loaded model) | Auto metadata / override | +| `gpt4all` | `openai` | local | (loaded model) | Yes (default on) | +| `local_openai_proxy` | `openai` | local | (required) | Off / manual toggle | +| `unsloth` | `openai` | local | (required) | Off / manual toggle | +| `webgpu` (Chromium) | `webgpu` | local | Compass Tiny v2.1 (only preset); experimental custom HF ONNX repos | No | +| `azure_openai` | `azure_openai` | cloud | (deployment) | Manual toggle | +| `aws_bedrock` | `aws_bedrock` | cloud | (model id) | No | +| `openai` | `openai` | cloud | `gpt-5.6-terra` | Model-name regex | +| `anthropic` | `anthropic` | cloud | `claude-sonnet-4-6` | Model-name regex | +| `gemini` | `openai` | cloud | `gemini-3.1-flash` | Model-name regex | +| `cloudflare` | `openai` | router | `@cf/zai-org/glm-5.2` | Model-name regex | +| `mistral` | `openai` | cloud | `mistral-large-latest` | Model-name regex | +| `deepseek` | `openai` | cloud | `deepseek-flash` | Model-name regex | +| `xai` (Grok) | `openai` | cloud | `grok-4.3` | Model-name regex | +| `nvidia` (NIM) | `openai` | router | `meta/llama-3.1-8b-instruct` | Model-name regex | +| `groq` | `openai` | router | `llama-3.3-70b-versatile` | Model-name regex | +| `minimax` | `openai` | cloud | `minimax-m2.7` | Model-name regex | +| `kimi` | `openai` | cloud | `kimi-k2.5` | Model-name regex | +| `alibaba` (Qwen) | `openai` | cloud | `qwen-max` | Model-name regex | +| `together` | `openai` | router | `meta-llama/Llama-3.3-70B-Instruct-Turbo` | Model-name regex | +| `openrouter` | `openai` | router | `openrouter/free` | Model-name regex | +| `huggingface` | `openai` | router | `zai-org/GLM-5.2` | Model-name regex | +| `fireworks` | `openai` | router | `accounts/fireworks/models/llama-v3p3-70b-instruct` | Model-name regex | +| `z_ai` | `openai` | cloud | `glm-5.2` | Model-name regex | ### Extended provider catalog @@ -124,23 +139,23 @@ their official API documentation. Together with the original cards, Settings contains **110 built-in providers on Chromium** and **109 on Firefox**; the difference is the Chromium-only in-browser WebGPU runtime. -| IDs | -|---| +| IDs | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `302ai`, `abacus`, `aihubmix`, `alibaba-coding-plan`, `alibaba-coding-plan-cn`, `azure-cognitive-services`, `bailing`, `baseten`, `berget`, `cerebras`, `chutes`, `clarifai`, `cloudferro-sherlock`, `cohere`, `cortecs`, `deepinfra`, `digitalocean`, `dinference`, `drun`, `evroc`, `fastrouter`, `friendli` | -| `google-vertex`, `google-vertex-anthropic`, `helicone`, `iflowcn`, `inception`, `inference`, `io-net`, `jiekou`, `kilo`, `kimi-for-coding`, `kuae-cloud-coding-plan`, `llama`, `lucidquery`, `meganova`, `minimax-cn-coding-plan`, `minimax-coding-plan`, `moark`, `modelscope`, `morph` | -| `nano-gpt`, `nearai`, `nebius`, `nova`, `novita-ai`, `ollama-cloud`, `opencode`, `opencode-go`, `orcarouter`, `ovhcloud`, `perplexity`, `perplexity-agent`, `poe`, `pollinations`, `privatemode-ai`, `qihang-ai`, `qiniu-ai`, `requesty`, `scaleway`, `siliconflow`, `siliconflow-cn`, `stackit` | -| `stepfun`, `submodel`, `synthetic`, `tencent-coding-plan`, `upstage`, `v0`, `venice`, `vercel`, `vivgrid`, `vultr`, `wandb`, `xiaomi`, `zai-coding-plan`, `zenmux`, `zhipuai`, `zhipuai-coding-plan` | +| `google-vertex`, `google-vertex-anthropic`, `helicone`, `iflowcn`, `inception`, `inference`, `io-net`, `jiekou`, `kilo`, `kimi-for-coding`, `kuae-cloud-coding-plan`, `llama`, `lucidquery`, `meganova`, `minimax-cn-coding-plan`, `minimax-coding-plan`, `moark`, `modelscope`, `morph` | +| `nano-gpt`, `nearai`, `nebius`, `nova`, `novita-ai`, `ollama-cloud`, `opencode`, `opencode-go`, `orcarouter`, `ovhcloud`, `perplexity`, `perplexity-agent`, `poe`, `pollinations`, `privatemode-ai`, `qihang-ai`, `qiniu-ai`, `requesty`, `scaleway`, `siliconflow`, `siliconflow-cn`, `stackit` | +| `stepfun`, `submodel`, `synthetic`, `tencent-coding-plan`, `upstage`, `v0`, `venice`, `vercel`, `vivgrid`, `vultr`, `wandb`, `xiaomi`, `zai-coding-plan`, `zenmux`, `zhipuai`, `zhipuai-coding-plan` | Most use the OpenAI-compatible Chat Completions contract and bearer API keys. The exceptions are: -| Provider | Authentication / protocol | -|---|---| -| Azure AI Foundry | Resource name plus `api-key`; model is the deployed model name | -| Google Vertex AI | Project, location, and a Google authorization key sent as `x-goog-api-key`; `global` uses `aiplatform.googleapis.com` | -| Google Vertex AI (Anthropic) | Vertex `rawPredict` / `streamRawPredict` with the same authorization-key fields; `us` and `eu` use their multi-region hosts | -| Perplexity Agent | OpenAI Responses-compatible `/v1/responses` | -| Cloudflare | Existing card supports Workers AI plus an optional AI Gateway ID; blank IDs use Cloudflare's `default` gateway for `@cf/` models | +| Provider | Authentication / protocol | +| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| Azure AI Foundry | Resource name plus `api-key`; model is the deployed model name | +| Google Vertex AI | Project, location, and a Google authorization key sent as `x-goog-api-key`; `global` uses `aiplatform.googleapis.com` | +| Google Vertex AI (Anthropic) | Vertex `rawPredict` / `streamRawPredict` with the same authorization-key fields; `us` and `eu` use their multi-region hosts | +| Perplexity Agent | OpenAI Responses-compatible `/v1/responses` | +| Cloudflare | Existing card supports Workers AI plus an optional AI Gateway ID; blank IDs use Cloudflare's `default` gateway for `@cf/` models | Morph and standard Perplexity Sonar are text-only integrations in the agent and advertise `supportsTools: false`. New provider cards remain inactive until @@ -356,26 +371,26 @@ Provider tier and conversation mode are separate knobs: `provider.promptTier` resolves the active tier. Cloud providers are forced to Full. Local providers default to Mid. OpenRouter/router providers default to Full unless explicitly changed. Existing configs that still set the legacy `useCompactPrompt` boolean map to Compact. -| Tier | Intended model class | Normal tool surface | -|---|---|---| -| `compact` | very small/local models | Shortest prompt and a small normal Act tool set. No scheduling, iframe, download-resource, or advanced DOM/UI fallback tools. | -| `mid` | capable local models | Balanced prompt and common task tools: downloads, scheduling, iframe tools, form verification, and `download_resource_from_page`, while excluding Full-only advanced UI/DOM fallbacks. | -| `full` | frontier/cloud or large local models | Full normal Act prompt and advanced fallbacks such as hover, drag-drop, frames, and shadow DOM. | +| Tier | Intended model class | Normal tool surface | +| --------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `compact` | very small/local models | Shortest prompt and a small normal Act tool set. No scheduling, iframe, download-resource, or advanced DOM/UI fallback tools. | +| `mid` | capable local models | Balanced prompt and common task tools: downloads, scheduling, iframe tools, form verification, and `download_resource_from_page`, while excluding Full-only advanced UI/DOM fallbacks. | +| `full` | frontier/cloud or large local models | Full normal Act prompt and advanced fallbacks such as hover, drag-drop, frames, and shadow DOM. | Ask mode ignores provider tier and stays read-only. Act mode uses the selected tier's normal tools. Dev mode requires Mid or Full, uses the selected Act prompt, appends `SYSTEM_PROMPT_DEV_APPENDIX`, and adds Dev-only source/style tools plus Dev-extended shadow/frame inspection for Mid-tier debugging. Compact Dev is blocked before an LLM request is sent. ### Vision Detection -| Provider | Mechanism | -|---|---| -| OpenAI-compatible | Regex against model name (`gpt-4o`, `gpt-5`, `claude-3`, `claude-sonnet-4`, `gemini-2.0-flash`, etc.) | -| DeepSeek | The `deepseek-flash` family (including the retired `deepseek-v4-flash` aliases) is multimodal; `deepseek-v4-pro` and the V3-era ids are text-only | -| Anthropic | `claude-(3\|sonnet-4\|opus-4)` patterns | -| Ollama | `POST /api/show` `capabilities`, with legacy projector / `.vision.` metadata fallbacks; Auto / Force on / Off | -| llama.cpp | `GET /props` → `modalities.vision`, with Auto / Force on / Off | -| LM Studio | `GET /api/v1/models` → `capabilities.vision`; legacy `/api/v0/models` `type`, with overrides | -| LocalAI | `GET /v1/models/capabilities` → `input_modalities` / `capabilities`, with overrides | -| Jan / vLLM / SGLang | Explicit `supportsVision` config toggle (via OpenAI provider) | +| Provider | Mechanism | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| OpenAI-compatible | Regex against model name (`gpt-4o`, `gpt-5`, `claude-3`, `claude-sonnet-4`, `gemini-2.0-flash`, etc.) | +| DeepSeek | The `deepseek-flash` family (including the retired `deepseek-v4-flash` aliases) is multimodal; `deepseek-v4-pro` and the V3-era ids are text-only | +| Anthropic | `claude-(3\|sonnet-4\|opus-4)` patterns | +| Ollama | `POST /api/show` `capabilities`, with legacy projector / `.vision.` metadata fallbacks; Auto / Force on / Off | +| llama.cpp | `GET /props` → `modalities.vision`, with Auto / Force on / Off | +| LM Studio | `GET /api/v1/models` → `capabilities.vision`; legacy `/api/v0/models` `type`, with overrides | +| LocalAI | `GET /v1/models/capabilities` → `input_modalities` / `capabilities`, with overrides | +| Jan / vLLM / SGLang | Explicit `supportsVision` config toggle (via OpenAI provider) | Auto results are keyed by provider, exact selected model, and canonical base URL. Concurrent checks share one request, and a late response from an older @@ -386,12 +401,12 @@ dedicated vision provider continues to use the existing split-provider path. When the active provider is Anthropic, the agent converts OpenAI-format messages: -| OpenAI format | Anthropic format | -|---|---| -| `system` message | `system` field (top-level) | +| OpenAI format | Anthropic format | +| -------------------------- | --------------------------------------- | +| `system` message | `system` field (top-level) | | `assistant` + `tool_calls` | `assistant` + `tool_use` content blocks | -| `tool` role | `user` + `tool_result` content blocks | -| `image_url` (data URL) | `image` source block | +| `tool` role | `user` + `tool_result` content blocks | +| `image_url` (data URL) | `image` source block | ### DeepSeek @@ -402,16 +417,16 @@ output, image input). Every other DeepSeek id — including the retired `deepseek-v4-pro` — stays on a conservative profile (64K context, 8K output, text-only) rather than inheriting capacities it may not have. -| Aspect | Behaviour | -|---|---| -| Wire format | Chat Completions by default (`apiFormat: 'auto'`); the Responses API is an opt-in from the Advanced panel | -| Thinking | Top-level `thinking` object plus `reasoning_effort`; disabling thinking omits `reasoning_effort` entirely. The shared UI ladder maps `minimal`→`low` and `medium`/`xhigh`→`high` | -| Reasoning replay | `reasoning_content` is replayed across turns because DeepSeek returns 400 when a tool-carrying follow-up drops it | -| Streaming | `stream_options.include_usage` on every request; the parser ignores DeepSeek's SSE `: keep-alive` comments | -| Structured output | Chat Completions uses JSON Object mode; the Responses API uses `text.format` JSON Schema for the planner | -| Images | `deepseek-flash` accepts `image_url` data URLs and public URLs in `user` messages | -| Cost | Off-peak list price converted at 1 USD = 7.1 CNY (1 input, 0.02 cached input, 4 output; peak 2 / 0.04 / 8). Cache hits arrive as the top-level `prompt_cache_hit_tokens` counter and are priced at the cache-read rate | -| Anthropic endpoint | `https://api.deepseek.com/anthropic` works with the built-in `anthropic` card by overriding its base URL | +| Aspect | Behaviour | +| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Wire format | Chat Completions by default (`apiFormat: 'auto'`); the Responses API is an opt-in from the Advanced panel | +| Thinking | Top-level `thinking` object plus `reasoning_effort`; disabling thinking omits `reasoning_effort` entirely. The shared UI ladder maps `minimal`→`low` and `medium`/`xhigh`→`high` | +| Reasoning replay | `reasoning_content` is replayed across turns because DeepSeek returns 400 when a tool-carrying follow-up drops it | +| Streaming | `stream_options.include_usage` on every request; the parser ignores DeepSeek's SSE `: keep-alive` comments | +| Structured output | Chat Completions uses JSON Object mode; the Responses API uses `text.format` JSON Schema for the planner | +| Images | `deepseek-flash` accepts `image_url` data URLs and public URLs in `user` messages | +| Cost | Off-peak list price converted at 1 USD = 7.1 CNY (1 input, 0.02 cached input, 4 output; peak 2 / 0.04 / 8). Cache hits arrive as the top-level `prompt_cache_hit_tokens` counter and are priced at the cache-read rate | +| Anthropic endpoint | `https://api.deepseek.com/anthropic` works with the built-in `anthropic` card by overriding its base URL | The contract lives in `providers/deepseek-config.js` (pure helpers and constants) and `providers/deepseek.js` (`DeepSeekProvider`). The shared @@ -429,15 +444,15 @@ Manages provider lifecycle: ```js const pm = new ProviderManager(); -await pm.load(); // Load from chrome.storage.local -await pm.save(); // Persist to chrome.storage.local -pm.getActive(); // Get the active provider instance -await pm.setActive('openai'); // Switch active provider -await pm.updateProvider('openai', { model: 'gpt-5' }); // Update config -await pm.duplicateProvider('openai'); // Create openai__duplicate -await pm.removeDuplicateProvider('openai__duplicate'); // Remove it -pm.getAll(); // All provider configs (for Settings UI) -await pm.testProvider('openai'); // Test connection +await pm.load(); // Load from chrome.storage.local +await pm.save(); // Persist to chrome.storage.local +pm.getActive(); // Get the active provider instance +await pm.setActive("openai"); // Switch active provider +await pm.updateProvider("openai", { model: "gpt-5" }); // Update config +await pm.duplicateProvider("openai"); // Create openai__duplicate +await pm.removeDuplicateProvider("openai__duplicate"); // Remove it +pm.getAll(); // All provider configs (for Settings UI) +await pm.testProvider("openai"); // Test connection ``` Each non-WebBrain provider config includes a persisted `configured` flag. An @@ -491,19 +506,19 @@ Those rates are editable in the provider card so custom model pricing can be adj The user can configure a separate vision provider for screenshot description. The agent sub-calls this provider to get a text description of the viewport, then feeds only the description (not the raw image) to the main planning provider. This reduces token costs when the main provider is text-only: -| Aspect | Separate vision model + text planner | Single multimodal planner | -|---|---|---| -| Processing flow | The vision model describes the screenshot, then the text planner reasons over that description and chooses tools. | One model sees the screenshot, reasons about the task, and chooses tools in the same call. | -| Access to raw pixels | Only the vision model sees the image; the planner receives text. | The planner retains direct access to the image while deciding what to do. | -| Visual information loss | The description is a lossy handoff and may omit small text, spatial relationships, colors, icons, or state cues. | No intermediate description is required, so the model can revisit visual details during reasoning. | -| Planning and tool calls | The vision model is observation-only; the text planner owns all action and tool decisions. | The same model performs visual interpretation and tool planning. | -| Specialist-model advantage | Perception and planning can use models selected independently for their strongest capability. | One model must be strong at both multimodal perception and browser-tool use. | -| Visual grounding and coordinates | Text descriptions can weaken the relationship between an element and its exact visual position; accessibility-tree `ref_id` targets remain preferable. | Image and coordinate context stay together, although semantic `ref_id` targets are still safer than coordinate clicks. | -| Latency | Usually requires two sequential inference calls. | Usually requires one inference call. | -| Cost | Pays for the vision call plus the planner call, but can keep expensive image tokens away from the planner. | Pays for one multimodal call, whose image-token cost depends on the provider and image detail. | -| Prompt-injection boundary | The observation model receives no agent tools, creating a stronger separation between screenshot content and actions. | The model that sees screenshot content can also choose tools, so multimodal prompt-injection defenses carry more responsibility. | -| Failure characteristics | Adds a sidecar timeout or transcription-failure point; a text-only planner may have to continue without visual enrichment. | Removes the handoff failure, but the entire turn depends on one multimodal endpoint and its combined capabilities. | -| Best fit | Strong text/tool planner paired with a specialist vision model, especially when most actions use DOM or accessibility evidence. | A model that is already strong at both vision and tool use, especially for tasks requiring fine visual detail or tight visual reasoning. | +| Aspect | Separate vision model + text planner | Single multimodal planner | +| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | +| Processing flow | The vision model describes the screenshot, then the text planner reasons over that description and chooses tools. | One model sees the screenshot, reasons about the task, and chooses tools in the same call. | +| Access to raw pixels | Only the vision model sees the image; the planner receives text. | The planner retains direct access to the image while deciding what to do. | +| Visual information loss | The description is a lossy handoff and may omit small text, spatial relationships, colors, icons, or state cues. | No intermediate description is required, so the model can revisit visual details during reasoning. | +| Planning and tool calls | The vision model is observation-only; the text planner owns all action and tool decisions. | The same model performs visual interpretation and tool planning. | +| Specialist-model advantage | Perception and planning can use models selected independently for their strongest capability. | One model must be strong at both multimodal perception and browser-tool use. | +| Visual grounding and coordinates | Text descriptions can weaken the relationship between an element and its exact visual position; accessibility-tree `ref_id` targets remain preferable. | Image and coordinate context stay together, although semantic `ref_id` targets are still safer than coordinate clicks. | +| Latency | Usually requires two sequential inference calls. | Usually requires one inference call. | +| Cost | Pays for the vision call plus the planner call, but can keep expensive image tokens away from the planner. | Pays for one multimodal call, whose image-token cost depends on the provider and image detail. | +| Prompt-injection boundary | The observation model receives no agent tools, creating a stronger separation between screenshot content and actions. | The model that sees screenshot content can also choose tools, so multimodal prompt-injection defenses carry more responsibility. | +| Failure characteristics | Adds a sidecar timeout or transcription-failure point; a text-only planner may have to continue without visual enrichment. | Removes the handoff failure, but the entire turn depends on one multimodal endpoint and its combined capabilities. | +| Best fit | Strong text/tool planner paired with a specialist vision model, especially when most actions use DOM or accessibility evidence. | A model that is already strong at both vision and tool use, especially for tasks requiring fine visual detail or tight visual reasoning. | ```js const vision = await providerManager.getVisionProvider(); diff --git a/src/chrome/src/agent/agent.js b/src/chrome/src/agent/agent.js index 03864d006..56ec3eec7 100644 --- a/src/chrome/src/agent/agent.js +++ b/src/chrome/src/agent/agent.js @@ -124,6 +124,7 @@ import { visionGenerationOptions, } from '../providers/provider-compatibility.js'; import { resolveMaxOutputTokens } from '../providers/context-windows.js'; +import { generateImage } from './fal-media.js'; import { extractFirstJsonObject } from './json-extract.js'; import { repairAssistantDisplayText, sanitizeText as sanitizePlannerText } from './text-sanitize.js'; import { emptyOutputFailureMessage, modelOutputDiagnostics } from './model-output-diagnostics.js'; @@ -35601,6 +35602,9 @@ If the user has already named or confirmed this exact recipient, do NOT ask agai if (name === 'fetch_url') { return await fetchUrl(args.url, args, { tabId, signal: executionContext?._contentActionAbortSignal }); } + if (name === 'generate_image') { + return await generateImage(args); + } if (name === 'read_page_source') { return await readPageSource(args.url, args, { tabId, signal: executionContext?._contentActionAbortSignal }); } diff --git a/src/chrome/src/agent/fal-media.js b/src/chrome/src/agent/fal-media.js new file mode 100644 index 000000000..8478d290f --- /dev/null +++ b/src/chrome/src/agent/fal-media.js @@ -0,0 +1,198 @@ +// fal.ai generative media (assistive model). +// +// Configured in Settings → Assistive Models → "Generative media (fal.ai)" and +// stored in chrome.storage.local under `imageGenModel = { apiKey, model }`. +// Consumed by the `generate_image` agent tool. +// +// fal.ai uses a queue API (not OpenAI chat-completions): submit a prompt to +// https://queue.fal.run/{model-id}, poll status_url, then fetch response_url. +// Auth header format is `Authorization: Key `. + +export const IMAGE_GEN_MODEL_KEY = 'imageGenModel'; +export const FAL_QUEUE_BASE = 'https://queue.fal.run'; +const FAL_STATUS_POLL_INTERVAL_MS = 2000; +const FAL_STATUS_TIMEOUT_MS = 120000; + +/** + * Normalize a fal.ai model id ("fal-ai/flux/schnell"). Rejects path traversal + * — the id is interpolated into the queue URL. + */ +export function normalizeFalModelId(model) { + const id = String(model || '').trim().replace(/^\/+|\/+$/g, ''); + if (!id) return ''; + if (!/^[A-Za-z0-9][A-Za-z0-9._\-/]*$/.test(id) || id.includes('..')) return ''; + return id; +} + +export function isImageGenConfigured(cfg) { + return !!(cfg && cfg.apiKey && cfg.model); +} + +export function falQueueSubmitUrl(model) { + return `${FAL_QUEUE_BASE}/${model}`; +} + +/** + * Probe URL used by Test Connection: a request id that cannot exist. A valid + * key gets 404 (not found); an invalid key gets 401/403. Avoids generating a + * paid image just to check the key. + */ +export function falQueueProbeUrl(model) { + return `${FAL_QUEUE_BASE}/${model}/requests/00000000-0000-0000-0000-000000000000/status`; +} + +/** + * Extract a usable media URL from a fal.ai queue response payload. Different + * model families return different shapes (images[], image, videos[], video, + * audio, or a plain URL string). + */ +export function extractFalMediaUrl(payload) { + if (!payload || typeof payload !== 'object') return ''; + if (typeof payload.url === 'string' && /^https:\/\//.test(payload.url)) return payload.url; + for (const listKey of ['images', 'videos', 'audio']) { + const list = payload[listKey]; + if (Array.isArray(list) && typeof list[0]?.url === 'string' && /^https:\/\//.test(list[0].url)) { + return list[0].url; + } + } + for (const objKey of ['image', 'video', 'audio']) { + const obj = payload[objKey]; + if (obj && typeof obj.url === 'string' && /^https:\/\//.test(obj.url)) return obj.url; + } + return ''; +} + +async function falAuthHeaders(apiKey) { + return { 'Authorization': `Key ${apiKey}`, 'Content-Type': 'application/json' }; +} + +/** + * Run a queued fal.ai generation: submit → poll → fetch result. + * `fetchImpl` is injectable for tests. + */ +export async function runFalGeneration({ prompt, config, fetchImpl = fetch, timeoutMs = FAL_STATUS_TIMEOUT_MS }) { + const model = normalizeFalModelId(config?.model); + if (!model) throw new Error('Invalid fal.ai model id.'); + if (!config?.apiKey) throw new Error('fal.ai API key not configured.'); + const text = String(prompt || '').trim(); + if (!text) throw new Error('prompt is required.'); + + const headers = await falAuthHeaders(config.apiKey); + const submitRes = await fetchImpl(falQueueSubmitUrl(model), { + method: 'POST', + headers, + body: JSON.stringify({ prompt: text }), + }); + if (!submitRes.ok) { + let body = ''; + try { body = (await submitRes.text()).slice(0, 300); } catch { /* ignore */ } + throw new Error(`fal.ai submit failed (HTTP ${submitRes.status}): ${body || submitRes.statusText}`); + } + let queued; + try { + queued = await submitRes.json(); + } catch (e) { + throw new Error(`fal.ai submit returned invalid JSON: ${e.message}`); + } + const statusUrl = typeof queued?.status_url === 'string' ? queued.status_url : ''; + const responseUrl = typeof queued?.response_url === 'string' ? queued.response_url : ''; + if (!statusUrl || !responseUrl) { + throw new Error('fal.ai submit response missing status_url/response_url.'); + } + + const deadline = Date.now() + timeoutMs; + let status = 'IN_QUEUE'; + while (Date.now() < deadline) { + await new Promise((resolve) => setTimeout(resolve, FAL_STATUS_POLL_INTERVAL_MS)); + const statusRes = await fetchImpl(statusUrl, { headers }); + if (!statusRes.ok) { + throw new Error(`fal.ai status check failed (HTTP ${statusRes.status}).`); + } + let statusPayload; + try { + statusPayload = await statusRes.json(); + } catch (e) { + throw new Error(`fal.ai status returned invalid JSON: ${e.message}`); + } + status = String(statusPayload?.status || '').toUpperCase(); + if (status === 'COMPLETED') { + const resultRes = await fetchImpl(responseUrl, { headers }); + if (!resultRes.ok) { + throw new Error(`fal.ai result fetch failed (HTTP ${resultRes.status}).`); + } + let payload; + try { + payload = await resultRes.json(); + } catch (e) { + throw new Error(`fal.ai result returned invalid JSON: ${e.message}`); + } + const url = extractFalMediaUrl(payload); + if (!url) throw new Error('fal.ai result contained no media URL.'); + return { url, model, status }; + } + if (status === 'FAILED' || status === 'ERROR') { + const errText = typeof statusPayload?.error === 'string' ? statusPayload.error : 'unknown error'; + throw new Error(`fal.ai generation failed: ${errText}`); + } + } + throw new Error('fal.ai generation timed out.'); +} + +/** + * Agent tool entry point. Reads the assistive-model config from storage. + */ +export async function generateImage(args, fetchImpl = fetch) { + let cfg; + const api = (typeof browser !== 'undefined' && browser?.storage) ? browser + : (typeof chrome !== 'undefined' ? chrome : null); + try { + const stored = await api.storage.local.get([IMAGE_GEN_MODEL_KEY]); + cfg = stored?.[IMAGE_GEN_MODEL_KEY]; + } catch (e) { + return { success: false, error: 'Failed to read generative media config: ' + e.message }; + } + if (!isImageGenConfigured(cfg)) { + return { success: false, error: 'Generative media is not configured. Set up fal.ai in Settings → Assistive Models.' }; + } + try { + const result = await runFalGeneration({ prompt: args?.prompt, config: cfg, fetchImpl }); + return { success: true, url: result.url, model: result.model }; + } catch (e) { + return { success: false, error: e.message }; + } +} + +/** + * Settings "Test Connection" probe — verifies the key authenticates without + * generating anything (expects 404 for a nonexistent request id). + */ +export async function testImageGenProvider(fetchImpl = fetch) { + let cfg; + const api = (typeof browser !== 'undefined' && browser?.storage) ? browser + : (typeof chrome !== 'undefined' ? chrome : null); + try { + const stored = await api.storage.local.get([IMAGE_GEN_MODEL_KEY]); + cfg = stored?.[IMAGE_GEN_MODEL_KEY]; + } catch (e) { + return { ok: false, error: 'Failed to read generative media config: ' + e.message }; + } + if (!isImageGenConfigured(cfg)) { + return { ok: false, error: 'Generative media not configured (API Key and Model are required).' }; + } + const model = normalizeFalModelId(cfg.model); + if (!model) return { ok: false, error: 'Invalid fal.ai model id.' }; + try { + const res = await fetchImpl(falQueueProbeUrl(model), { headers: await falAuthHeaders(cfg.apiKey) }); + if (res.status === 401 || res.status === 403) { + return { ok: false, error: 'fal.ai rejected the API key (HTTP ' + res.status + ').' }; + } + // fal.ai also answers 405 for a nonexistent request id on some model + // routes; auth is checked before routing, so 405 still proves the key. + if (res.status === 404 || res.status === 405 || res.ok || res.status === 422) { + return { ok: true, model }; + } + return { ok: false, error: `Unexpected response from fal.ai (HTTP ${res.status}).` }; + } catch (e) { + return { ok: false, error: e.message }; + } +} diff --git a/src/chrome/src/agent/permission-gate.js b/src/chrome/src/agent/permission-gate.js index 452ded469..819c156c7 100644 --- a/src/chrome/src/agent/permission-gate.js +++ b/src/chrome/src/agent/permission-gate.js @@ -419,6 +419,8 @@ const TOOL_CAPABILITY = { download_social_media: Capability.DOWNLOAD, schedule_resume: Capability.SCHEDULE, schedule_task: Capability.SCHEDULE, + // generate_image spends the user's fal.ai credits via a paid network call. + generate_image: Capability.NETWORK, }; /** diff --git a/src/chrome/src/agent/planner.js b/src/chrome/src/agent/planner.js index d4f4db262..875e430e2 100644 --- a/src/chrome/src/agent/planner.js +++ b/src/chrome/src/agent/planner.js @@ -376,6 +376,7 @@ ${PLANNER_RESPONSE_LANGUAGE_RULES} read: get_accessibility_tree, read_page, extract_data, fetch_url, research_url interact: click_ax, set_checked, type_ax, set_field, find_text, press_keys, scroll, navigate, gmail_count_results, carousel_navigate, promote_iframe wait: wait_for_element, wait_for_stable + media: generate_image (create an image/video/audio directly from a text prompt via the user's fal.ai generative-media model — use when the user asks to GENERATE media, not to browse an image site) memory: scratchpad_write, progress_update, progress_read schedule: schedule_task (future/recurring work the user explicitly asked for), schedule_resume (pause CURRENT run blocked on external event) user input: clarify (pause and ask one concise question when a required value remains missing after relevant inspection) @@ -464,6 +465,7 @@ ${PLANNER_RESPONSE_ONLY_RULES} - Canonical summary, steps, and risks must be English. localized fields must use the requested wbLocale. ${PLANNER_RESPONSE_LANGUAGE_RULES} - For execute, keep the compact plan to 1–4 steps. For plan_only, provide 2–8 useful steps. For respond and clarify, steps may be empty. +- When the user asks to generate an image/video/audio, plan one generate_image step (WebBrain's built-in fal.ai media tool). Do not plan steps to visit image-generation sites or to check whether the current page supports image generation. - clarify pauses execution to ask one concise question for a required value. done is terminal and must never be used to request information needed to continue. - press_keys supports only unmodified Escape, Tab, Enter, arrow keys, and ; (semicolon, for page shortcuts such as Gmail Expand all). Never plan modifier combinations or browser UI shortcuts; use find_text to select one page-text match instead of Ctrl/Cmd+F. Each call replaces the previous selection and cannot create simultaneous highlights or browser Find UI. - For Instagram /p// carousel enumeration, use strictly increasing carousel_navigate indexes unless the latest user request explicitly asks for reverse traversal, in which case use strictly decreasing indexes; never use arrow keys, coordinate clicks, Previous/Next, or go_back to traverse slides. diff --git a/src/chrome/src/agent/tools.js b/src/chrome/src/agent/tools.js index c176eab01..d324511af 100644 --- a/src/chrome/src/agent/tools.js +++ b/src/chrome/src/agent/tools.js @@ -1244,6 +1244,20 @@ export const AGENT_TOOLS = [ }, }, }, + { + type: 'function', + function: { + name: 'generate_image', + description: 'Generate media (usually an image) from a text prompt using the user\'s configured fal.ai assistive model (Settings → Assistive Models → Generative media). Runs on fal.ai\'s queue API and may take up to a minute. Returns the hosted media URL on success. Not available in Ask mode.', + parameters: { + type: 'object', + properties: { + prompt: { type: 'string', description: 'Text prompt describing the media to generate.' }, + }, + required: ['prompt'], + }, + }, + }, ]; /** @@ -1952,6 +1966,7 @@ ${BROWSER_TAB_LIMITATION} - scratchpad_write: Pin a note in context that survives summarization (use on long tasks to remember download IDs, file paths, plans) - progress_update / progress_read: Structured app-owned ledger for the active repeated item/action task. Use it for per-user/per-item status and collected fields; close pending/acted rows before done. - download_public_media (if enabled by a skill) / download_social_media: One-shot image/video download from public social sites. Prefer the enabled skill tool for public media URLs; otherwise use download_social_media. Single call — no need to inspect the DOM yourself. +- generate_image: Create media (usually an image, sometimes video/audio) directly from a text prompt through the user's configured fal.ai generative-media model. When the user asks to GENERATE media ("generate an image of a red apple", "make a logo", "create a video clip"), call this tool — do NOT navigate to third-party image sites (Midjourney, DALL·E, Bing Images, etc.). Requires Settings → Assistive Models → Generative media. Not available in Ask mode. - Recording is user-driven only. If the user asks to record, do NOT call tools; tell them to type \`/record\` for current-tab recording or \`/record --full-screen\` for screen/window recording; add \`--transcribe\` to either form if they want a Whisper transcript after stop. If they ask to stop a recording, tell them to press Escape twice in WebBrain/browser surfaces or use Chrome's Stop sharing control. - hover: CDP-trusted hover over a ref_id. Use ONLY for menus/tooltips that REVEAL on hover (GitHub three-dot menus, Linear card actions, nav menus with reveal-on-hover children). Re-read the tree after to find the newly-visible items. Do NOT call hover before every click — most things are clickable directly. - drag_drop: Drag one ref_id onto another via CDP-trusted pointer events. Use for Trello/Linear/Notion-style card reordering, file-tree node moves, image-crop handles, slider thumbs. Pass \`steps: 15–20\` if the first attempt doesn't trigger the drop indicator on momentum-tracking dnd. Verify by re-reading the tree. diff --git a/src/chrome/src/background.js b/src/chrome/src/background.js index 879769a33..afc3105e5 100644 --- a/src/chrome/src/background.js +++ b/src/chrome/src/background.js @@ -41,6 +41,7 @@ import { } from './providers/oauth-claude.js'; import { getBalance as capsolverGetBalance } from './agent/captcha-solver.js'; import { isCapsolverEnabled } from './agent/capsolver-config.js'; +import { testImageGenProvider } from './agent/fal-media.js'; import { createSystemOneJudge } from './agent/systemone-judge.js'; import { cloudSafeScheduledJob, createCloudRunController } from './cloud-runs.js'; import { ensureOffscreen } from './offscreen/ensure.js'; @@ -141,7 +142,7 @@ const VISION_OFFSCREEN_URL = chrome.runtime.getURL('src/offscreen/offscreen.html // eviction registers its in-memory state first; the Traces page can also // request an immediate scan via WB_TRACE_REPAIR_STALE_RUNS. const TRACE_REPAIR_STARTUP_DELAY_MS = 15_000; -setTimeout(() => { void workflowTrace.repairStaleRuns().catch(() => {}); }, TRACE_REPAIR_STARTUP_DELAY_MS); +setTimeout(() => { void workflowTrace.repairStaleRuns().catch(() => { }); }, TRACE_REPAIR_STARTUP_DELAY_MS); function normalizeVisionDownloadState(state) { return { @@ -181,7 +182,7 @@ async function startExplicitVisionModelDownload() { total: 0, error: String(result?.error || 'The local vision model download could not be started.').slice(0, 500), updatedAt: Date.now(), - }).catch(() => {}); + }).catch(() => { }); return result; } @@ -227,7 +228,7 @@ agent.setConversationScopeChangeListener((tabId, state) => { tabId, type: 'conversation_scope', data: state, - }).catch(() => {}); + }).catch(() => { }); }); const userMemoryStore = createUserMemoryStore(chrome.storage.local); const savedWorkflowStore = createSavedWorkflowStore(chrome.storage.local); @@ -272,7 +273,7 @@ const scheduler = new ScheduledJobManager({ tabId, type, data, - }).catch(() => {}); + }).catch(() => { }); maybeFlashScheduledTerminalEvent(tabId, type, data); }, showIndicator: (tabId) => sendIndicatorMessage(tabId, 'WB_SHOW_AGENT_INDICATORS'), @@ -304,7 +305,7 @@ const cloudRunController = createCloudRunController({ }); alwaysAllowApiMutationsReady .then(() => cloudRunController.syncBridge()) - .catch(() => {}); + .catch(() => { }); const MAX_AGENT_STEPS_DEFAULT = 130; const MAX_AGENT_STEPS_UNLIMITED_SENTINEL = 200; @@ -421,7 +422,7 @@ const selectionShortcutLocaleReady = chrome.storage.local.get({ wbLocale: '' }) .then((stored) => { selectionShortcutLocale = resolveStoredSelectionShortcutLocale(stored?.wbLocale); }) - .catch(() => {}); + .catch(() => { }); function getContextMenuPromptStore() { return chrome.storage?.session || chrome.storage?.local || null; @@ -457,7 +458,7 @@ async function createContextMenus() { console.warn('[WebBrain] Failed to create context menu:', err.message || err); } if (item.id === CONTEXT_MENU_OPEN_PDF_VIEWER_ID) { - syncPdfContextMenuForActiveTab().catch(() => {}); + syncPdfContextMenuForActiveTab().catch(() => { }); } }); }; @@ -538,7 +539,7 @@ async function loadClarifyTimeout() { updates.clarifyTimeoutSec = CLARIFY_TIMEOUT_OFF_SLIDER; stored.clarifyTimeoutSec = CLARIFY_TIMEOUT_OFF_SLIDER; } - await chrome.storage.local.set(updates).catch(() => {}); + await chrome.storage.local.set(updates).catch(() => { }); } agent.clarifyTimeoutSec = normalizeClarifyTimeoutSec( stored.clarifyTimeoutSec != null ? stored.clarifyTimeoutSec : 60, @@ -563,7 +564,7 @@ async function loadResearchEscalation() { agent.researchEscalationEnabled = stored.researchEscalationEnabled === true; agent.researchEscalationEngine = String(stored.researchEscalationEngine || 'chatgpt'); } -const researchEscalationReady = loadResearchEscalation().catch(() => {}); +const researchEscalationReady = loadResearchEscalation().catch(() => { }); // Local screenshot redaction (issue #312): when on, screenshots are pixelated // over DOM-detected PII (form fields + email/phone text) BEFORE leaving the @@ -572,7 +573,7 @@ async function loadScreenshotRedaction() { const stored = await chrome.storage.local.get('screenshotRedaction'); if (stored.screenshotRedaction != null) agent.screenshotRedaction = !!stored.screenshotRedaction; } -const screenshotRedactionReady = loadScreenshotRedaction().catch(() => {}); +const screenshotRedactionReady = loadScreenshotRedaction().catch(() => { }); // Image budget (issue #311): screenshot quality + how many screenshots the // agent may capture per turn, and the max image dimension. Defaults preserve @@ -583,19 +584,19 @@ async function loadImageBudget() { } // Retained so handleMessage can await hydration on a cold SW start — the first // chat must not race ahead of the persisted image-budget settings (issue #311). -const imageBudgetReady = loadImageBudget().catch(() => {}); +const imageBudgetReady = loadImageBudget().catch(() => { }); async function loadStrictSecretMode() { const stored = await chrome.storage.local.get('strictSecretMode').catch(() => ({})); agent.strictSecretMode = stored?.strictSecretMode === true; } -const strictSecretModeReady = loadStrictSecretMode().catch(() => {}); +const strictSecretModeReady = loadStrictSecretMode().catch(() => { }); async function loadWebMCPEnabled() { const stored = await chrome.storage.local.get('webMcpEnabled'); agent.setWebMCPEnabled(stored.webMcpEnabled === true); } -const webMcpEnabledReady = loadWebMCPEnabled().catch(() => {}); +const webMcpEnabledReady = loadWebMCPEnabled().catch(() => { }); // Profile auto-fill: user-provided text (name, email, etc.) that gets // appended to the system prompt when enabled. Plaintext in storage — @@ -624,7 +625,7 @@ async function syncAgentUserMemoryFromStorage() { }); return store; } -const userMemoryReady = syncAgentUserMemoryFromStorage().catch(() => {}); +const userMemoryReady = syncAgentUserMemoryFromStorage().catch(() => { }); const USER_MEMORY_EXTRACTION_MAX_QUEUE = 10; const USER_MEMORY_EXTRACTION_DELAY_MS = 1200; @@ -658,7 +659,7 @@ function recordClarificationMemoryCandidate(tabId, question, answer) { if (!normalizedAnswer) return; const normalizedQuestion = normalizeUserMemoryText(question, 500); if (looksLikeSensitiveMemoryText(normalizedAnswer) - || (normalizedQuestion && looksLikeSensitiveMemoryText(normalizedQuestion))) { + || (normalizedQuestion && looksLikeSensitiveMemoryText(normalizedQuestion))) { return; } const context = getUserMemoryTurnContext(tabId); @@ -745,7 +746,7 @@ async function isUserMemoryFormCaptureEnabled() { async function withUserMemoryExtractionQueueLock(task) { const run = userMemoryExtractionQueueLock.then(task, task); - userMemoryExtractionQueueLock = run.catch(() => {}); + userMemoryExtractionQueueLock = run.catch(() => { }); return run; } @@ -811,13 +812,13 @@ async function markUserMemoryExtractionJobFailed(jobId) { async function withUserMemoryStoreLock(task) { const run = userMemoryStoreLock.then(task, task); - userMemoryStoreLock = run.catch(() => {}); + userMemoryStoreLock = run.catch(() => { }); return run; } async function withSavedWorkflowStoreLock(task) { const run = savedWorkflowStoreLock.then(task, task); - savedWorkflowStoreLock = run.catch(() => {}); + savedWorkflowStoreLock = run.catch(() => { }); return run; } @@ -866,7 +867,7 @@ function notifyUserMemoryCreated() { chrome.runtime.sendMessage({ target: 'sidepanel', action: 'user_memory_created', - }).catch(() => {}); + }).catch(() => { }); } function scheduleUserMemoryExtractionDrain(delayMs = USER_MEMORY_EXTRACTION_DELAY_MS) { @@ -1172,8 +1173,8 @@ chrome.runtime.onInstalled.addListener(async (details) => { await providerManager.load(); await loadMaxSteps(); await loadClarifyTimeout(); - await syncAgentUserMemoryFromStorage().catch(() => {}); - await cloudRunController.syncBridge().catch(() => {}); + await syncAgentUserMemoryFromStorage().catch(() => { }); + await cloudRunController.syncBridge().catch(() => { }); // Chrome registers the manifest handler with enabled=true after install. // Reconcile now and once more after registration settles so the native // default cannot overwrite the extension's opt-in default. The in-handler @@ -1191,8 +1192,8 @@ chrome.runtime.onStartup?.addListener(async () => { await providerManager.load(); await loadMaxSteps(); await loadClarifyTimeout(); - await syncAgentUserMemoryFromStorage().catch(() => {}); - await cloudRunController.syncBridge().catch(() => {}); + await syncAgentUserMemoryFromStorage().catch(() => { }); + await cloudRunController.syncBridge().catch(() => { }); scheduleUserMemoryExtractionDrain(5000); }); @@ -1205,12 +1206,12 @@ chrome.storage.onChanged.addListener((changes, areaName) => { } if (changes.wbLocale) { selectionShortcutLocale = normalizeSelectionShortcutLocale(changes.wbLocale.newValue); - createContextMenus().catch(() => {}); + createContextMenus().catch(() => { }); } - if (PROFILE_SYNC_DATA_KEYS.some((key) => changes[key])) profileSync.noteChanges(changes).catch(() => {}); - if (changes.providers || changes.activeProvider || changes.helpImproveWebBrain) providerManager.load().catch(() => {}); + if (PROFILE_SYNC_DATA_KEYS.some((key) => changes[key])) profileSync.noteChanges(changes).catch(() => { }); + if (changes.providers || changes.activeProvider || changes.helpImproveWebBrain) providerManager.load().catch(() => { }); if (changes.webbrainCloudBridgeEnabled || changes.webbrainCloudBridgeUrl) { - cloudRunController.syncBridge().catch(() => {}); + cloudRunController.syncBridge().catch(() => { }); } if (changes.maxAgentSteps) { agent.maxSteps = normalizeMaxAgentSteps(changes.maxAgentSteps.newValue); @@ -1415,7 +1416,7 @@ async function loadPanelTabs() { } catch (e) { /* session storage not available */ } } function savePanelTabs() { - chrome.storage.session?.set({ [PANEL_TABS_KEY]: Array.from(panelTabs) }).catch(() => {}); + chrome.storage.session?.set({ [PANEL_TABS_KEY]: Array.from(panelTabs) }).catch(() => { }); } loadPanelTabs(); @@ -1448,11 +1449,11 @@ async function loadWebBrainGroups() { function saveWebBrainGroups() { chrome.storage.session?.set({ [WB_GROUPS_KEY]: Array.from(webBrainGroupByWindow.entries()), - }).catch(() => {}); + }).catch(() => { }); } loadWebBrainGroups(); -chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: false }).catch(() => {}); +chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: false }).catch(() => { }); // Panel visibility model — opt-in per tab (Cmd+T no longer leaks the panel). // @@ -1558,8 +1559,8 @@ chrome.runtime.onMessage.addListener((msg, sender) => { if (tab?.url !== installGuideUrl) return; panelTabs.add(tab.id); savePanelTabs(); - ensureWebBrainGroup(tab).catch(() => {}); - }).catch(() => {}); + ensureWebBrainGroup(tab).catch(() => { }); + }).catch(() => { }); }); // Tracks the pending 250 ms retry timer per tab so it can be cancelled if the @@ -1575,10 +1576,10 @@ function notifySidePanelOfContextMenuPrompt(payload) { prompt: payload, }; clearTimeout(pendingContextMenuNotifications.get(tabId)); - chrome.runtime.sendMessage(msg).catch(() => {}); + chrome.runtime.sendMessage(msg).catch(() => { }); const timerId = setTimeout(() => { pendingContextMenuNotifications.delete(tabId); - chrome.runtime.sendMessage(msg).catch(() => {}); + chrome.runtime.sendMessage(msg).catch(() => { }); }, 250); pendingContextMenuNotifications.set(tabId, timerId); } @@ -1593,7 +1594,7 @@ function openSidePanelForContextMenu(tab) { enabled: true, }); chrome.sidePanel.open({ tabId: tab.id }); - ensureWebBrainGroup(tab).catch(() => {}); + ensureWebBrainGroup(tab).catch(() => { }); } async function handleContextMenuAsk(info, tab) { @@ -1641,12 +1642,12 @@ async function handleContextMenuAsk(info, tab) { openSidePanelForContextMenu(tab); try { await contextMenuStorage.save(tab.id, payload); - } catch {} + } catch { } notifySidePanelOfContextMenuPrompt(payload); } chrome.contextMenus?.onClicked?.addListener?.((info, tab) => { - handleContextMenuAsk(info, tab).catch(() => {}); + handleContextMenuAsk(info, tab).catch(() => { }); }); // Only this instance knows which runs are live in memory, so it owns the @@ -1704,7 +1705,7 @@ function queueSelectionShortcutPrompt(msg, tab, sendResponse) { (async () => { try { await contextMenuStorage.save(tab.id, payload); - } catch {} + } catch { } notifySidePanelOfContextMenuPrompt(payload); return { ok: true, queued: true, requiresManualOpen: false }; })().then(sendResponse).catch((error) => { @@ -1738,7 +1739,7 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { // now (stub is enough for setOptions/open), then verify the live tab. try { openSidePanelForContextMenu({ id: tabId }); - } catch {} + } catch { } chrome.tabs.get(tabId) .then(tab => queueSelectionShortcutPrompt(msg, tab, sendResponse)) .catch(error => sendResponse({ @@ -1798,8 +1799,8 @@ function startIndicatorHeartbeat(tabId) { sendIndicatorMessage(tabId, 'WB_SHOW_AGENT_INDICATORS'); } }) - .catch(() => {}); - } catch {} + .catch(() => { }); + } catch { } }, INDICATOR_HEARTBEAT_INTERVAL_MS); indicatorHeartbeatTimers.set(tabId, timer); } @@ -1835,7 +1836,7 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo) => { // discard the entry it just recorded. trackPdfResponse() already replaces // the entry on the next main-frame response, and onRemoved clears it. if (changeInfo?.url || changeInfo?.status) { - syncPdfContextMenuForActiveTab().catch(() => {}); + syncPdfContextMenuForActiveTab().catch(() => { }); } if (changeInfo?.status === 'complete') { reassertIndicatorIfActive(tabId); @@ -1845,7 +1846,7 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo) => { chrome.tabs.onRemoved.addListener((tabId) => { activeIndicatorTabs.delete(tabId); stopIndicatorHeartbeat(tabId); - withTeacherSessionStoreLock(() => teacherSessionStore.clear(tabId)).catch(() => {}); + withTeacherSessionStoreLock(() => teacherSessionStore.clear(tabId)).catch(() => { }); }); const RUN_UI_PREFIX = 'runUi:'; @@ -1885,7 +1886,7 @@ function persistRunUiSnapshot(tabId, snapshot) { runUiPersistenceQueues.set(tabId, write); write.finally(() => { if (runUiPersistenceQueues.get(tabId) === write) runUiPersistenceQueues.delete(tabId); - }).catch(() => {}); + }).catch(() => { }); return write; } @@ -2004,7 +2005,7 @@ async function getRunUiSnapshot(tabId) { if (snapshot && typeof snapshot === 'object') { return runUiJournal.restore(tabId, snapshot); } - } catch {} + } catch { } return null; } @@ -2013,11 +2014,11 @@ function clearRunUiSnapshot(tabId) { runUiJournal.clear(tabId); runUiPersistenceFailures.delete(tabId); const previous = runUiPersistenceQueues.get(tabId) || Promise.resolve(); - const removal = previous.catch(() => {}).then(() => chrome.storage.session?.remove(RUN_UI_PREFIX + tabId)); + const removal = previous.catch(() => { }).then(() => chrome.storage.session?.remove(RUN_UI_PREFIX + tabId)); runUiPersistenceQueues.set(tabId, removal); removal.finally(() => { if (runUiPersistenceQueues.get(tabId) === removal) runUiPersistenceQueues.delete(tabId); - }).catch(() => {}); + }).catch(() => { }); } function sendAgentUpdate(tabId, requestId, type, data) { @@ -2032,7 +2033,7 @@ function sendAgentUpdate(tabId, requestId, type, data) { seq: event?.seq || null, type, data: event?.data ?? data, - }).catch(() => {}); + }).catch(() => { }); } function assertNoActiveTabRun(tabId) { @@ -2124,7 +2125,7 @@ async function stopActiveRunBeforeConversationClear(tabId) { // first leaves the per-tab run guard active while the UI already looks like // a fresh chat, so the next send fails with "run already in progress". if (activeStart?.promise) { - await activeStart.promise.catch(() => {}); + await activeStart.promise.catch(() => { }); } // Direct chat/chat_stream callers do not have a detached-start promise. // Do not clear their conversation until processMessage's finally block has @@ -2151,8 +2152,8 @@ function acquireRunKeepalive() { let released = false; const touch = () => { try { - chrome.runtime.getPlatformInfo().catch(() => {}); - } catch {} + chrome.runtime.getPlatformInfo().catch(() => { }); + } catch { } }; touch(); const timer = setInterval(touch, RUN_KEEPALIVE_INTERVAL_MS); @@ -2238,7 +2239,7 @@ async function sendAgentRunComplete(tabId, snapshot = null) { flashTabAttention({ tabId, success: liveStatus === 'completed' && snapshot.runSucceeded === true, - }).catch(() => {}); + }).catch(() => { }); } const submittedTurnDurable = snapshot.kind === 'continue' || await agent.hasDurableSubmittedTurn( @@ -2274,7 +2275,7 @@ async function sendAgentRunComplete(tabId, snapshot = null) { submittedTurnDurable, attachmentDeliveryState, }, - }).catch(() => {}); + }).catch(() => { }); } // Stop button on the page → abort the agent run for that tab. Mirrors @@ -2316,7 +2317,7 @@ chrome.action.onClicked.addListener((tab) => { // before the user can switch tabs. Async — we already lost the user- // gesture window for sidePanel.open, but ensureWebBrainGroup doesn't // need it. - ensureWebBrainGroup(tab).catch(() => {}); + ensureWebBrainGroup(tab).catch(() => { }); }); // (Was: chrome.tabs.onActivated + chrome.tabs.onUpdated listeners that @@ -2354,11 +2355,11 @@ chrome.tabs.onRemoved.addListener((tabId) => { clearTimeout(pendingContextMenuNotifications.get(tabId)); pendingContextMenuNotifications.delete(tabId); contextMenuStorage.cleanup(tabId); - tabChatHandoff.clear(tabId).catch(() => {}); - clearStagedScreenshots(chrome.storage.local, tabId).catch(() => {}); + tabChatHandoff.clear(tabId).catch(() => { }); + clearStagedScreenshots(chrome.storage.local, tabId).catch(() => { }); savePanelTabs(); - scheduler.cancelForTab(tabId).catch(() => {}); - agent.clearDevCssPatchesForTab(tabId).catch(() => {}); + scheduler.cancelForTab(tabId).catch(() => { }); + agent.clearDevCssPatchesForTab(tabId).catch(() => { }); try { agent._cleanupTab(tabId); } catch { /* ignore */ } }); @@ -2373,7 +2374,7 @@ function invalidateContextMenuForTab(tabId) { target: 'sidepanel', action: 'context_menu_tab_navigated', tabId, - }).catch(() => {}); + }).catch(() => { }); } // SPA navigation tracking. Many sites change route via History API without @@ -2392,7 +2393,7 @@ function recordNav(tabId, type, url, { resetTypeIdentity = true } = {}) { } function recordTeacherNavigation(tabId, url, options) { - teacherRunInterlock.navigation(tabId, url, options).catch(() => {}); + teacherRunInterlock.navigation(tabId, url, options).catch(() => { }); } const TEACHER_EXPLICIT_NAVIGATION_TYPES = new Set([ @@ -2401,26 +2402,26 @@ const TEACHER_EXPLICIT_NAVIGATION_TYPES = new Set([ chrome.webNavigation?.onHistoryStateUpdated?.addListener((details) => { if (details.frameId !== 0) return; - agent.observeCloudflareManagedChallengeNavigation(details).catch(() => {}); + agent.observeCloudflareManagedChallengeNavigation(details).catch(() => { }); recordNav(details.tabId, 'history', details.url); recordTeacherNavigation(details.tabId, details.url); invalidateContextMenuForTab(details.tabId); }); chrome.webNavigation?.onReferenceFragmentUpdated?.addListener((details) => { if (details.frameId !== 0) return; - agent.observeCloudflareManagedChallengeNavigation(details).catch(() => {}); + agent.observeCloudflareManagedChallengeNavigation(details).catch(() => { }); recordNav(details.tabId, 'fragment', details.url); invalidateContextMenuForTab(details.tabId); }); chrome.webNavigation?.onCommitted?.addListener((details) => { if (details.frameId !== 0) return; - agent.observeCloudflareManagedChallengeNavigation(details).catch(() => {}); + agent.observeCloudflareManagedChallengeNavigation(details).catch(() => { }); recordNav(details.tabId, 'committed', details.url); recordTeacherNavigation(details.tabId, details.url, { force: TEACHER_EXPLICIT_NAVIGATION_TYPES.has(details.transitionType), }); invalidateContextMenuForTab(details.tabId); - agent.clearDevCssPatchesForTab(details.tabId).catch(() => {}); + agent.clearDevCssPatchesForTab(details.tabId).catch(() => { }); }); chrome.webNavigation?.onCompleted?.addListener((details) => { if (details.frameId === 0) { @@ -2434,11 +2435,11 @@ chrome.webNavigation?.onCompleted?.addListener((details) => { // detection and embedded widgets may use the same managed endpoint. const observeCloudflareManagedChallengeResponse = details => { trackPdfResponse(details); - syncPdfContextMenuForActiveTab().catch(() => {}); - agent.observeCloudflareManagedChallengeResponse(details).catch(() => {}); + syncPdfContextMenuForActiveTab().catch(() => { }); + agent.observeCloudflareManagedChallengeResponse(details).catch(() => { }); }; const observeCloudflareChallengePlatformRequest = details => { - agent.observeCloudflareChallengePlatformRequest(details).catch(() => {}); + agent.observeCloudflareChallengePlatformRequest(details).catch(() => { }); }; chrome.webRequest?.onHeadersReceived?.addListener?.( observeCloudflareManagedChallengeResponse, @@ -2500,7 +2501,7 @@ function extractApiReplayBody(requestBody) { const text = params.toString(); return text.length <= API_REPLAY_BODY_LIMIT ? text : null; } - } catch (_) {} + } catch (_) { } return null; } @@ -2667,7 +2668,7 @@ async function showCompletionNotification(tabId, success) { if (!notificationId) return resolve(); setTimeout(() => { completionNotificationFocusHandlers.delete(notificationId); - chrome.notifications.clear(notificationId, () => {}); + chrome.notifications.clear(notificationId, () => { }); }, COMPLETION_NOTIFICATION_VISIBLE_MS); if (Number.isInteger(tabId)) { completionNotificationFocusHandlers.set(notificationId, async () => { @@ -2686,11 +2687,11 @@ async function showCompletionNotification(tabId, success) { chrome.tabs.onActivated.addListener(({ tabId } = {}) => { flashedBadgeTabs.delete(tabId); - syncPdfContextMenuForActiveTab().catch(() => {}); + syncPdfContextMenuForActiveTab().catch(() => { }); // Clear unconditionally: the Set only lives in service-worker memory, but // a tab-scoped badge survives MV3 worker suspension/restarts. Resetting // the per-tab override is idempotent and restores any global badge. - chrome.action.setBadgeText({ tabId, text: '' }).catch(() => {}); + chrome.action.setBadgeText({ tabId, text: '' }).catch(() => { }); }); // Focusing a window does not fire tabs.onActivated for its already-active @@ -2880,7 +2881,7 @@ async function handleMessage(msg, sender) { chrome.runtime.sendMessage({ type: 'apocalypse-mode-state', enabled: snapshot.enabled === true, - }).catch(() => {}); + }).catch(() => { }); if (msg.enabled === true) { const textModel = await providerManager.enableAndStartWebgpuTextDownload(); return { ...snapshot, textModel }; @@ -3172,9 +3173,9 @@ async function handleMessage(msg, sender) { const compiled = draft?.conversationId === conversationId ? finalizeSavedWorkflowDraft(draft, { name: msg.name }) : await compileLatestSuccessfulWorkflow(workflowTrace, { - conversationId, - name: msg.name, - }); + conversationId, + name: msg.name, + }); if (!compiled.workflow) return { ok: false, ...compiled }; const saved = await withSavedWorkflowStoreLock(() => savedWorkflowStore.put(compiled.workflow)); return { ok: saved.changed, workflow: saved.workflow, warnings: compiled.warnings, reason: saved.reason || '' }; @@ -3602,7 +3603,7 @@ async function handleMessage(msg, sender) { handoffOwnerId: tabChatClearResult.handoffOwnerId, handoffGeneration: tabChatClearResult.handoffGeneration, clearedContextMenuPromptId, - }).catch(() => {}); + }).catch(() => { }); } return { ok: true, clearedContextMenuPromptId }; } @@ -3831,7 +3832,7 @@ async function handleMessage(msg, sender) { tabId, handoffOwnerId: result.handoffOwnerId, handoffGeneration: result.handoffGeneration, - }).catch(() => {}); + }).catch(() => { }); } return result; } @@ -3845,7 +3846,7 @@ async function handleMessage(msg, sender) { const tabId = msg.tabId || sender.tab?.id || null; let tab = null; if (tabId != null) { - try { tab = await chrome.tabs.get(tabId); } catch {} + try { tab = await chrome.tabs.get(tabId); } catch { } } return await scheduler.createTaskJob({ tabId, @@ -3861,7 +3862,7 @@ async function handleMessage(msg, sender) { const tabId = msg.tabId || sender.tab?.id || null; let tab = null; if (tabId != null) { - try { tab = await chrome.tabs.get(tabId); } catch {} + try { tab = await chrome.tabs.get(tabId); } catch { } } return await scheduler.createWatchJob({ args: msg.watch || msg.args || {}, @@ -3953,7 +3954,7 @@ async function handleMessage(msg, sender) { await providerManager.load(); } catch (error) { if (previousEnabled !== msg.enabled) { - await chrome.storage.local.set({ helpImproveWebBrain: previousEnabled }).catch(() => {}); + await chrome.storage.local.set({ helpImproveWebBrain: previousEnabled }).catch(() => { }); } throw error; } @@ -4074,6 +4075,10 @@ async function handleMessage(msg, sender) { return await providerManager.testTranscriptionProvider(); } + case 'test_image_gen_provider': { + return await testImageGenProvider(); + } + case 'test_system_one': { await strictSecretModeReady; try { diff --git a/src/chrome/src/ui/locales/ar.js b/src/chrome/src/ui/locales/ar.js index e891eeaab..3896910d4 100644 --- a/src/chrome/src/ui/locales/ar.js +++ b/src/chrome/src/ui/locales/ar.js @@ -609,6 +609,14 @@ export default { "st.transcription.connected": "تم الاتصال! النموذج: {model}", "st.transcription.failed": "فشل: {error}", "st.transcription.fill_required": "املأ عنوان API الأساسي والنموذج أولًا.", + "st.imagegen.heading": "الوسائط التوليدية (fal.ai)", + "st.imagegen.desc": "تُستخدَم بواسطة أداة generate_image لإنشاء الصور وغيرها من الوسائط من وصف نصي عبر واجهة قائمة الانتظار في fal.ai. احصل على مفتاح من fal.ai/dashboard/keys. لا تُستخدم للمحادثة.", + "st.imagegen.saved": "تم الحفظ!", + "st.imagegen.cleared": "تم المسح.", + "st.imagegen.testing": "جارٍ الاختبار...", + "st.imagegen.connected": "تم الاتصال! النموذج: {model}", + "st.imagegen.failed": "فشل: {error}", + "st.imagegen.fill_required": "املأ مفتاح API والنموذج أولًا.", "st.captcha.desc_html": "دع الوكيل يحلّ اختبارات CAPTCHA تلقائيًا عبر واجهة CapSolver. يدعم reCAPTCHA v2/v3 وhCaptcha وCloudflare Turnstile. يؤدي حفظ مفتاح API صالح إلى تمكين CapSolver تلقائيًا؛ وبدونه، يتوقّف الوكيل ويطلب منك حلّ الاختبار بنفسك. تتقاضى CapSolver رسومًا لكل عملية حلّ (~$0.001–$0.003)؛ تستخدم حسابك ومفتاح API الخاص بك.", "st.captcha.enabled.label": "تفعيل CapSolver", "st.captcha.enabled.desc": "عندما يصادف الوكيل اختبار CAPTCHA فإنه يستدعي CapSolver مرّة واحدة قبل اللجوء إلى سؤالك. يتطلّب مفتاح API أدناه.", diff --git a/src/chrome/src/ui/locales/bn.js b/src/chrome/src/ui/locales/bn.js index 1eafc9f59..fdfde1068 100644 --- a/src/chrome/src/ui/locales/bn.js +++ b/src/chrome/src/ui/locales/bn.js @@ -898,6 +898,14 @@ export default { 'st.transcription.connected': "সংযুক্ত ! মডেল: {model}", 'st.transcription.failed': "ব্যর্থ হয়েছে: {error}", 'st.transcription.fill_required': "প্রথমে বেস URL এবং মডেল পূরণ করুন।", + "st.imagegen.heading": "জেনারেটিভ মিডিয়া (fal.ai)", + "st.imagegen.desc": "generate_image এজেন্ট টুল দ্বারা fal.ai-র কিউ API-র মাধ্যমে টেক্সট প্রম্পট থেকে ছবি ও অন্যান্য মিডিয়া তৈরিতে ব্যবহৃত হয়। fal.ai/dashboard/keys থেকে কী নিন। চ্যাটের জন্য ব্যবহৃত হয় না।", + "st.imagegen.saved": "সংরক্ষিত হয়েছে!", + "st.imagegen.cleared": "মুছে ফেলা হয়েছে।", + "st.imagegen.testing": "পরীক্ষা চলছে...", + "st.imagegen.connected": "সংযুক্ত! মডেল: {model}", + "st.imagegen.failed": "ব্যর্থ: {error}", + "st.imagegen.fill_required": "প্রথমে API কী এবং মডেল পূরণ করুন।", 'st.imageBudget.heading': "ছবির বাজেট", 'st.imageBudget.desc': "স্ক্রিনশটের আকার এবং প্রতি টার্নে ভিশনের জন্য এজেন্ট কতটি স্ক্রিনশট নেবে তা নিয়ন্ত্রণ করুন। কম বিস্তারিত ও ছোট মাত্রা ছোট এন্ডপয়েন্টে খরচ ও বিলম্ব কমায়; বেশি মান বিশ্বস্ততা বজায় রাখে। ডিফল্ট মান আগের আচরণের সঙ্গে মেলে।", diff --git a/src/chrome/src/ui/locales/de.js b/src/chrome/src/ui/locales/de.js index dda2f21a6..29fb55146 100644 --- a/src/chrome/src/ui/locales/de.js +++ b/src/chrome/src/ui/locales/de.js @@ -838,6 +838,14 @@ export default { 'st.transcription.connected': 'Verbunden! Modell: {model}', 'st.transcription.failed': 'Fehlgeschlagen: {error}', 'st.transcription.fill_required': 'Füllen Sie zuerst die Basis-URL und das Modell aus.', + "st.imagegen.heading": "Generative Medien (fal.ai)", + "st.imagegen.desc": "Wird vom Agent-Tool generate_image verwendet, um aus einem Textprompt Bilder und andere Medien über die Queue-API von fal.ai zu erzeugen. Schlüssel unter fal.ai/dashboard/keys erhalten. Wird nicht für Chat verwendet.", + "st.imagegen.saved": "Gespeichert!", + "st.imagegen.cleared": "Gelöscht.", + "st.imagegen.testing": "Wird getestet...", + "st.imagegen.connected": "Verbunden! Modell: {model}", + "st.imagegen.failed": "Fehlgeschlagen: {error}", + "st.imagegen.fill_required": "Bitte zuerst API-Schlüssel und Modell ausfüllen.", // --- Image budget settings --- 'st.imageBudget.heading': 'Bildbudget', diff --git a/src/chrome/src/ui/locales/en.js b/src/chrome/src/ui/locales/en.js index 416efc5e1..cf4cc329d 100644 --- a/src/chrome/src/ui/locales/en.js +++ b/src/chrome/src/ui/locales/en.js @@ -900,6 +900,16 @@ export default { 'st.transcription.failed': 'Failed: {error}', 'st.transcription.fill_required': 'Fill in Base URL and Model first.', + // Generative media (fal.ai) — Assistive Models section + 'st.imagegen.heading': 'Generative media (fal.ai)', + 'st.imagegen.desc': 'Used by the generate_image agent tool to create images and other media from a text prompt via fal.ai\'s queue API. Get a key at fal.ai/dashboard/keys. Not used for chat.', + 'st.imagegen.saved': 'Saved!', + 'st.imagegen.cleared': 'Cleared.', + 'st.imagegen.testing': 'Testing...', + 'st.imagegen.connected': 'Connected! Model: {model}', + 'st.imagegen.failed': 'Failed: {error}', + 'st.imagegen.fill_required': 'Fill in API Key and Model first.', + // Image budget (issue #311): tune screenshot quality + how many // screenshots the agent may capture/send per turn, and how large each // image may be. All controls live on the Assistive Models tab. diff --git a/src/chrome/src/ui/locales/es.js b/src/chrome/src/ui/locales/es.js index effab9548..92f686c62 100644 --- a/src/chrome/src/ui/locales/es.js +++ b/src/chrome/src/ui/locales/es.js @@ -609,6 +609,14 @@ export default { "st.transcription.connected": "¡Conectado! Modelo: {model}", "st.transcription.failed": "Falló: {error}", "st.transcription.fill_required": "Rellena primero la URL base y el modelo.", + "st.imagegen.heading": "Medios generativos (fal.ai)", + "st.imagegen.desc": "Usada por la herramienta de agente generate_image para crear imágenes y otros medios a partir de un prompt de texto mediante la API de cola de fal.ai. Obtén una clave en fal.ai/dashboard/keys. No se usa para chat.", + "st.imagegen.saved": "¡Guardado!", + "st.imagegen.cleared": "Borrado.", + "st.imagegen.testing": "Probando...", + "st.imagegen.connected": "¡Conectado! Modelo: {model}", + "st.imagegen.failed": "Error: {error}", + "st.imagegen.fill_required": "Rellena primero la clave de API y el modelo.", "st.captcha.desc_html": "Deja que el agente resuelva CAPTCHAs automáticamente mediante la API de CapSolver. Admite reCAPTCHA v2/v3, hCaptcha y Cloudflare Turnstile. Al guardar una clave de API válida, CapSolver se activa automáticamente; sin una, el agente se detiene y te pide que resuelvas el captcha tú mismo. CapSolver cobra por cada resolución (~$0.001–$0.003); utilizas tu propia cuenta y clave de API.", "st.captcha.enabled.label": "Activar CapSolver", "st.captcha.enabled.desc": "Cuando el agente se encuentra con un CAPTCHA, llamará a CapSolver una vez antes de recurrir a pedírtelo a ti. Requiere una clave de API más abajo.", diff --git a/src/chrome/src/ui/locales/fa.js b/src/chrome/src/ui/locales/fa.js index bf7c62420..1f0dfb630 100644 --- a/src/chrome/src/ui/locales/fa.js +++ b/src/chrome/src/ui/locales/fa.js @@ -898,6 +898,14 @@ export default { 'st.transcription.connected': "متصل است! مدل: {model}", 'st.transcription.failed': "ناموفق: {error}", 'st.transcription.fill_required': "ابتدا Base URL و Model را پر کنید.", + "st.imagegen.heading": "رسانه‌های مولد (fal.ai)", + "st.imagegen.desc": "ابزار ایجنت generate_image برای ساخت تصویر و سایر رسانه‌ها از یک پرامپت متنی از طریق API صف fal.ai استفاده می‌کند. کلید را از fal.ai/dashboard/keys بگیرید. برای چت استفاده نمی‌شود.", + "st.imagegen.saved": "ذخیره شد!", + "st.imagegen.cleared": "پاک شد.", + "st.imagegen.testing": "در حال آزمایش...", + "st.imagegen.connected": "متصل شد! مدل: {model}", + "st.imagegen.failed": "خطا: {error}", + "st.imagegen.fill_required": "ابتدا کلید API و مدل را پر کنید.", 'st.imageBudget.heading': "بودجه تصویر", 'st.imageBudget.desc': "اندازه اسکرین‌شات و تعداد تصاویری را که عامل در هر نوبت برای بینایی می‌گیرد کنترل کنید. جزئیات و ابعاد کمتر، هزینه و تأخیر را برای نقاط پایانی کوچک‌تر کاهش می‌دهد؛ مقادیر بیشتر وفاداری را حفظ می‌کند. پیش‌فرض‌ها با رفتار قبلی مطابقت دارند.", diff --git a/src/chrome/src/ui/locales/fr.js b/src/chrome/src/ui/locales/fr.js index 1fc9147f9..610f94533 100644 --- a/src/chrome/src/ui/locales/fr.js +++ b/src/chrome/src/ui/locales/fr.js @@ -609,6 +609,14 @@ export default { "st.transcription.connected": "Connecté ! Modèle : {model}", "st.transcription.failed": "Échec : {error}", "st.transcription.fill_required": "Remplissez d'abord l'URL de base et le modèle.", + "st.imagegen.heading": "Médias génératifs (fal.ai)", + "st.imagegen.desc": "Utilisée par l'outil d'agent generate_image pour créer des images et d'autres médias à partir d'un prompt texte via l'API de file d'attente de fal.ai. Obtenez une clé sur fal.ai/dashboard/keys. Non utilisée pour le chat.", + "st.imagegen.saved": "Enregistré !", + "st.imagegen.cleared": "Effacé.", + "st.imagegen.testing": "Test en cours...", + "st.imagegen.connected": "Connecté ! Modèle : {model}", + "st.imagegen.failed": "Échec : {error}", + "st.imagegen.fill_required": "Remplissez d'abord la clé API et le modèle.", "st.captcha.desc_html": "Laissez l'agent résoudre les CAPTCHA automatiquement via l'API CapSolver. Prend en charge reCAPTCHA v2/v3, hCaptcha et Cloudflare Turnstile. L'enregistrement d'une clé API valide active automatiquement CapSolver ; sans clé, l'agent s'arrête et vous demande de résoudre le CAPTCHA vous-même. CapSolver facture chaque résolution (~$0.001–$0.003) ; vous utilisez votre propre compte et votre propre clé API.", "st.captcha.enabled.label": "Activer CapSolver", "st.captcha.enabled.desc": "Lorsque l'agent rencontre un CAPTCHA, il appellera CapSolver une fois avant de se rabattre sur une demande de votre part. Nécessite une clé d'API ci-dessous.", diff --git a/src/chrome/src/ui/locales/he.js b/src/chrome/src/ui/locales/he.js index 567c0c8d9..9550c0f49 100644 --- a/src/chrome/src/ui/locales/he.js +++ b/src/chrome/src/ui/locales/he.js @@ -819,6 +819,14 @@ export default { "st.transcription.connected": "מחובר! מודל: {model}", "st.transcription.failed": "נכשל: {error}", "st.transcription.fill_required": "יש למלא תחילה כתובת URL בסיסית ומודל.", + "st.imagegen.heading": "מדיה גנרטיבית (fal.ai)", + "st.imagegen.desc": "בשימוש על ידי כלי הסוכן generate_image ליצירת תמונות ומדיה אחרת מתיאור טקסטואלי דרך ה-Queue API של fal.ai. אפשר לקבל מפתח ב-fal.ai/dashboard/keys. אינו משמש לצ׳אט.", + "st.imagegen.saved": "נשמר!", + "st.imagegen.cleared": "נמחק.", + "st.imagegen.testing": "בודק...", + "st.imagegen.connected": "מחובר! דגם: {model}", + "st.imagegen.failed": "נכשל: {error}", + "st.imagegen.fill_required": "מלא קודם מפתח API ודגם.", "st.profile.desc_html": "שמור ביוגרפיה קצרה שבה הסוכן יכול להשתמש למילוי טופסי הרשמה בלי לשאול בכל פעם — השם שלך, כתובת דוא״ל לעבודה, החברה וסיסמה ייעודית להרשמות בעלות סיכון נמוך. כאשר האפשרות מופעלת, הטקסט שלהלן מצורף להנחיית המערכת של הסוכן בכל שיחה.", "st.profile.enabled.label": "הפעל מילוי אוטומטי של פרופיל", "st.profile.enabled.desc": "הכנס את טקסט הפרופיל להנחיית המערכת של הסוכן. מושבת = הסוכן לעולם לא רואה את הטקסט הזה.", diff --git a/src/chrome/src/ui/locales/hi.js b/src/chrome/src/ui/locales/hi.js index 1add7d0a6..1f0ccd962 100644 --- a/src/chrome/src/ui/locales/hi.js +++ b/src/chrome/src/ui/locales/hi.js @@ -898,6 +898,14 @@ export default { 'st.transcription.connected': "जुड़ा हुआ! मॉडल: {model}", 'st.transcription.failed': "विफल: {error}", 'st.transcription.fill_required': "सबसे पहले बेस यूआरएल और मॉडल भरें।", + "st.imagegen.heading": "जनरेटिव मीडिया (fal.ai)", + "st.imagegen.desc": "generate_image एजेंट टूल द्वारा fal.ai की क्यू API के ज़रिए टेक्स्ट प्रॉम्प्ट से इमेज और अन्य मीडिया बनाने के लिए उपयोग किया जाता है। fal.ai/dashboard/keys से की लें। चैट के लिए उपयोग नहीं होता।", + "st.imagegen.saved": "सहेजा गया!", + "st.imagegen.cleared": "साफ़ कर दिया गया।", + "st.imagegen.testing": "जाँच जारी...", + "st.imagegen.connected": "कनेक्ट हो गया! मॉडल: {model}", + "st.imagegen.failed": "विफल: {error}", + "st.imagegen.fill_required": "पहले API की और मॉडल भरें।", 'st.imageBudget.heading': "छवि बजट", 'st.imageBudget.desc': "स्क्रीनशॉट का आकार और हर टर्न में विज़न के लिए एजेंट द्वारा लिए जाने वाले स्क्रीनशॉट की संख्या नियंत्रित करें। कम विवरण और छोटे आयाम छोटे एंडपॉइंट के लिए लागत और विलंबता घटाते हैं; अधिक मान गुणवत्ता बनाए रखते हैं। डिफ़ॉल्ट पिछले व्यवहार से मेल खाते हैं।", diff --git a/src/chrome/src/ui/locales/id.js b/src/chrome/src/ui/locales/id.js index c9ff08e87..caf37b9c6 100644 --- a/src/chrome/src/ui/locales/id.js +++ b/src/chrome/src/ui/locales/id.js @@ -609,6 +609,14 @@ export default { "st.transcription.connected": "Terhubung! Model: {model}", "st.transcription.failed": "Gagal: {error}", "st.transcription.fill_required": "Isi URL dasar dan Model terlebih dahulu.", + "st.imagegen.heading": "Media generatif (fal.ai)", + "st.imagegen.desc": "Digunakan oleh alat agen generate_image untuk membuat gambar dan media lain dari prompt teks melalui API antrean fal.ai. Dapatkan kunci di fal.ai/dashboard/keys. Tidak digunakan untuk obrolan.", + "st.imagegen.saved": "Tersimpan!", + "st.imagegen.cleared": "Dihapus.", + "st.imagegen.testing": "Menguji...", + "st.imagegen.connected": "Terhubung! Model: {model}", + "st.imagegen.failed": "Gagal: {error}", + "st.imagegen.fill_required": "Isi Kunci API dan Model terlebih dahulu.", "st.captcha.desc_html": "Biarkan agen menyelesaikan CAPTCHA secara otomatis melalui API CapSolver. Mendukung reCAPTCHA v2/v3, hCaptcha, dan Cloudflare Turnstile. Menyimpan kunci API yang valid akan mengaktifkan CapSolver secara otomatis; tanpa kunci, agen berhenti dan meminta Anda menyelesaikan CAPTCHA sendiri. CapSolver mengenakan biaya per penyelesaian (~$0.001–$0.003); Anda menggunakan akun dan kunci API sendiri.", "st.captcha.enabled.label": "Aktifkan CapSolver", "st.captcha.enabled.desc": "Saat agen menemui CAPTCHA, ia akan memanggil CapSolver sekali sebelum kembali bertanya kepada Anda. Memerlukan kunci API di bawah.", diff --git a/src/chrome/src/ui/locales/ja.js b/src/chrome/src/ui/locales/ja.js index 0c39f25f7..0c47eff92 100644 --- a/src/chrome/src/ui/locales/ja.js +++ b/src/chrome/src/ui/locales/ja.js @@ -609,6 +609,14 @@ export default { "st.transcription.connected": "接続しました!モデル: {model}", "st.transcription.failed": "失敗: {error}", "st.transcription.fill_required": "先にベース URL とモデルを入力してください。", + "st.imagegen.heading": "生成メディア (fal.ai)", + "st.imagegen.desc": "generate_image エージェントツールが fal.ai のキュー API を通じてテキストプロンプトから画像などのメディアを生成するために使用します。キーは fal.ai/dashboard/keys で取得できます。チャットには使用されません。", + "st.imagegen.saved": "保存しました!", + "st.imagegen.cleared": "クリアしました。", + "st.imagegen.testing": "テスト中...", + "st.imagegen.connected": "接続しました!モデル: {model}", + "st.imagegen.failed": "失敗: {error}", + "st.imagegen.fill_required": "先に API キーとモデルを入力してください。", "st.captcha.desc_html": "CapSolver API を使って、エージェントに CAPTCHA を自動で解かせます。reCAPTCHA v2/v3、hCaptcha、Cloudflare Turnstile に対応しています。有効な API キーを保存すると CapSolver が自動的に有効になります。キーがない場合、エージェントは停止し、CAPTCHA を手動で解くよう求めます。CapSolver は解決ごとに課金します(約 ~$0.001–$0.003)。ご自身のアカウントと API キーを使用します。", "st.captcha.enabled.label": "CapSolver を有効化", "st.captcha.enabled.desc": "エージェントが CAPTCHA に遭遇すると、あなたに尋ねる前にまず CapSolver を 1 回呼び出します。下記の API キーが必要です。", diff --git a/src/chrome/src/ui/locales/ko.js b/src/chrome/src/ui/locales/ko.js index 34b1596a6..ce4a77539 100644 --- a/src/chrome/src/ui/locales/ko.js +++ b/src/chrome/src/ui/locales/ko.js @@ -609,6 +609,14 @@ export default { "st.transcription.connected": "연결됨! 모델: {model}", "st.transcription.failed": "실패: {error}", "st.transcription.fill_required": "먼저 기본 URL과 모델을 입력하세요.", + "st.imagegen.heading": "생성 미디어 (fal.ai)", + "st.imagegen.desc": "generate_image 에이전트 도구가 fal.ai 큐 API를 통해 텍스트 프롬프트에서 이미지 등 미디어를 생성하는 데 사용됩니다. fal.ai/dashboard/keys에서 키를 발급받으세요. 채팅에는 사용되지 않습니다.", + "st.imagegen.saved": "저장되었습니다!", + "st.imagegen.cleared": "지워졌습니다.", + "st.imagegen.testing": "테스트 중...", + "st.imagegen.connected": "연결됨! 모델: {model}", + "st.imagegen.failed": "실패: {error}", + "st.imagegen.fill_required": "먼저 API 키와 모델을 입력하세요.", "st.captcha.desc_html": "에이전트가 CapSolver API를 통해 CAPTCHA를 자동으로 풀게 합니다. reCAPTCHA v2/v3, hCaptcha, Cloudflare Turnstile을 지원합니다. 유효한 API 키를 저장하면 CapSolver가 자동으로 활성화됩니다. 키가 없으면 에이전트가 멈추고 CAPTCHA를 직접 풀어 달라고 요청합니다. CapSolver는 풀이당 요금을 부과하며(~$0.001–$0.003), 본인의 계정과 API 키를 사용합니다.", "st.captcha.enabled.label": "CapSolver 사용", "st.captcha.enabled.desc": "에이전트가 CAPTCHA를 만나면 사용자에게 묻기로 폴백하기 전에 CapSolver를 한 번 호출합니다. 아래에 API 키가 필요합니다.", diff --git a/src/chrome/src/ui/locales/ms.js b/src/chrome/src/ui/locales/ms.js index fce0711ad..043f9be7d 100644 --- a/src/chrome/src/ui/locales/ms.js +++ b/src/chrome/src/ui/locales/ms.js @@ -609,6 +609,14 @@ export default { "st.transcription.connected": "Tersambung! Model: {model}", "st.transcription.failed": "Gagal: {error}", "st.transcription.fill_required": "Isi URL Asas dan Model terlebih dahulu.", + "st.imagegen.heading": "Media generatif (fal.ai)", + "st.imagegen.desc": "Digunakan oleh alat ejen generate_image untuk mencipta imej dan media lain daripada geseran teks melalui API baris giliran fal.ai. Dapatkan kunci di fal.ai/dashboard/keys. Tidak digunakan untuk sembang.", + "st.imagegen.saved": "Disimpan!", + "st.imagegen.cleared": "Dibersihkan.", + "st.imagegen.testing": "Menguji...", + "st.imagegen.connected": "Berjaya disambung! Model: {model}", + "st.imagegen.failed": "Gagal: {error}", + "st.imagegen.fill_required": "Isi Kunci API dan Model dahulu.", "st.captcha.desc_html": "Biarkan ejen menyelesaikan CAPTCHA secara automatik melalui API CapSolver. Menyokong reCAPTCHA v2/v3, hCaptcha dan Cloudflare Turnstile. Menyimpan kunci API yang sah akan mengaktifkan CapSolver secara automatik; tanpa kunci, ejen berhenti dan meminta anda menyelesaikan CAPTCHA sendiri. CapSolver mengenakan bayaran bagi setiap penyelesaian (~$0.001–$0.003); anda menggunakan akaun dan kunci API sendiri.", "st.captcha.enabled.label": "Dayakan CapSolver", "st.captcha.enabled.desc": "Apabila ejen menemui CAPTCHA, ia akan memanggil CapSolver sekali sebelum berundur untuk bertanya kepada anda. Memerlukan kunci API di bawah.", diff --git a/src/chrome/src/ui/locales/nl.js b/src/chrome/src/ui/locales/nl.js index e0c11bfa3..f8fb73d29 100644 --- a/src/chrome/src/ui/locales/nl.js +++ b/src/chrome/src/ui/locales/nl.js @@ -806,6 +806,14 @@ export default { 'st.transcription.connected': 'Verbonden! Model: {model}', 'st.transcription.failed': 'Mislukt: {error}', 'st.transcription.fill_required': 'Vul eerst basis-URL en model in.', + "st.imagegen.heading": "Generatieve media (fal.ai)", + "st.imagegen.desc": "Wordt gebruikt door de agenttool generate_image om afbeeldingen en andere media te maken vanuit een tekstprompt via de queue-API van fal.ai. Vraag een sleutel aan op fal.ai/dashboard/keys. Niet voor chat.", + "st.imagegen.saved": "Opgeslagen!", + "st.imagegen.cleared": "Wissen gelukt.", + "st.imagegen.testing": "Testen...", + "st.imagegen.connected": "Verbonden! Model: {model}", + "st.imagegen.failed": "Mislukt: {error}", + "st.imagegen.fill_required": "Vul eerst de API-sleutel en het model in.", 'st.imageBudget.heading': 'Afbeeldingsbudget', 'st.imageBudget.desc': 'Beheer schermafbeeldingsgrootte en hoeveel de agent vastlegt voor visie per beurt...', 'st.imageBudget.detail.label': 'Afbeeldingsdetail', diff --git a/src/chrome/src/ui/locales/pl.js b/src/chrome/src/ui/locales/pl.js index 4cd6460ec..8d1219e65 100644 --- a/src/chrome/src/ui/locales/pl.js +++ b/src/chrome/src/ui/locales/pl.js @@ -636,6 +636,14 @@ export default { 'st.transcription.connected': 'Połączono! Model: {model}', 'st.transcription.failed': 'Niepowodzenie: {error}', 'st.transcription.fill_required': 'Najpierw wypełnij bazowy adres URL i model.', + "st.imagegen.heading": "Media generatywne (fal.ai)", + "st.imagegen.desc": "Używane przez narzędzie agenta generate_image do tworzenia obrazów i innych mediów z promptu tekstowego przez API kolejki fal.ai. Klucz uzyskasz na fal.ai/dashboard/keys. Nieużywane do czatu.", + "st.imagegen.saved": "Zapisano!", + "st.imagegen.cleared": "Wyczyszczono.", + "st.imagegen.testing": "Testowanie...", + "st.imagegen.connected": "Połączono! Model: {model}", + "st.imagegen.failed": "Niepowodzenie: {error}", + "st.imagegen.fill_required": "Najpierw wypełnij klucz API i model.", 'st.profile.desc_html': 'Przechowuj krótki opis, którego agent może użyć do wypełniania formularzy rejestracji bez pytania za każdym razem — Twoje imię, służbowy e-mail, firmę i jednorazowe hasło do mało istotnych rejestracji. Po włączeniu poniższy tekst jest dołączany do promptu systemowego agenta w każdej rozmowie.', 'st.profile.enabled.label': 'Włącz automatyczne wypełnianie profilu', 'st.profile.enabled.desc': 'Wstrzykuj tekst profilu do promptu systemowego agenta. Wyłączone = agent nigdy nie widzi tego tekstu.', diff --git a/src/chrome/src/ui/locales/pt.js b/src/chrome/src/ui/locales/pt.js index 2a263cb27..e55cd1c80 100644 --- a/src/chrome/src/ui/locales/pt.js +++ b/src/chrome/src/ui/locales/pt.js @@ -898,6 +898,14 @@ export default { 'st.transcription.connected': "Conectado! Modelo: {model}", 'st.transcription.failed': "Falha: {error}", 'st.transcription.fill_required': "Preencha primeiro o URL base e o modelo.", + "st.imagegen.heading": "Mídia generativa (fal.ai)", + "st.imagegen.desc": "Usada pela ferramenta de agente generate_image para criar imagens e outras mídias a partir de um prompt de texto pela API de fila da fal.ai. Obtenha uma chave em fal.ai/dashboard/keys. Não usada para chat.", + "st.imagegen.saved": "Salvo!", + "st.imagegen.cleared": "Limpo.", + "st.imagegen.testing": "Testando...", + "st.imagegen.connected": "Conectado! Modelo: {model}", + "st.imagegen.failed": "Falhou: {error}", + "st.imagegen.fill_required": "Preencha primeiro a chave de API e o modelo.", 'st.imageBudget.heading': "Orçamento de imagens", 'st.imageBudget.desc': "Controle o tamanho das capturas de tela e quantas o agente faz para visão por turno. Menos detalhes e dimensões menores reduzem custo e latência em endpoints menores; valores maiores preservam a fidelidade. Os padrões correspondem ao comportamento anterior.", diff --git a/src/chrome/src/ui/locales/ru.js b/src/chrome/src/ui/locales/ru.js index 59b3f3ca5..63a82c979 100644 --- a/src/chrome/src/ui/locales/ru.js +++ b/src/chrome/src/ui/locales/ru.js @@ -609,6 +609,14 @@ export default { "st.transcription.connected": "Подключено! Модель: {model}", "st.transcription.failed": "Сбой: {error}", "st.transcription.fill_required": "Сначала заполните базовый URL и модель.", + "st.imagegen.heading": "Генеративные медиа (fal.ai)", + "st.imagegen.desc": "Используется инструментом агента generate_image для создания изображений и других медиа из текстового запроса через очередь API fal.ai. Ключ можно получить на fal.ai/dashboard/keys. Не используется для чата.", + "st.imagegen.saved": "Сохранено!", + "st.imagegen.cleared": "Очищено.", + "st.imagegen.testing": "Проверка...", + "st.imagegen.connected": "Подключено! Модель: {model}", + "st.imagegen.failed": "Ошибка: {error}", + "st.imagegen.fill_required": "Сначала заполните ключ API и модель.", "st.captcha.desc_html": "Позвольте агенту автоматически решать CAPTCHA через API CapSolver. Поддерживает reCAPTCHA v2/v3, hCaptcha и Cloudflare Turnstile. Сохранение действительного API-ключа автоматически включает CapSolver; без ключа агент останавливается и просит вас решить CAPTCHA самостоятельно. CapSolver берёт плату за каждое решение (~$0.001–$0.003); используется ваш аккаунт и API-ключ.", "st.captcha.enabled.label": "Включить CapSolver", "st.captcha.enabled.desc": "Когда агент сталкивается с CAPTCHA, он один раз обращается к CapSolver, прежде чем перейти к запросу к вам. Требуется API-ключ ниже.", diff --git a/src/chrome/src/ui/locales/th.js b/src/chrome/src/ui/locales/th.js index 3d6d2a371..9c3c19132 100644 --- a/src/chrome/src/ui/locales/th.js +++ b/src/chrome/src/ui/locales/th.js @@ -609,6 +609,14 @@ export default { "st.transcription.connected": "เชื่อมต่อแล้ว! โมเดล: {model}", "st.transcription.failed": "ล้มเหลว: {error}", "st.transcription.fill_required": "กรอก URL ฐานและโมเดลก่อน", + "st.imagegen.heading": "สื่อเชิงสร้างสรรค์ (fal.ai)", + "st.imagegen.desc": "เครื่องมือเอเจนต์ generate_image ใช้สร้างรูปภาพและสื่ออื่น ๆ จากพรอมต์ข้อความผ่านคิว API ของ fal.ai รับคีย์ได้ที่ fal.ai/dashboard/keys ไม่ได้ใช้สำหรับแชท", + "st.imagegen.saved": "บันทึกแล้ว!", + "st.imagegen.cleared": "ล้างแล้ว.", + "st.imagegen.testing": "กำลังทดสอบ...", + "st.imagegen.connected": "เชื่อมต่อแล้ว! โมเดล: {model}", + "st.imagegen.failed": "ล้มเหลว: {error}", + "st.imagegen.fill_required": "กรอกคีย์ API และโมเดลก่อน", "st.captcha.desc_html": "ให้เอเจนต์แก้ CAPTCHA โดยอัตโนมัติผ่าน API ของ CapSolver รองรับ reCAPTCHA v2/v3, hCaptcha และ Cloudflare Turnstile การบันทึกคีย์ API ที่ถูกต้องจะเปิดใช้ CapSolver โดยอัตโนมัติ หากไม่มีคีย์ เอเจนต์จะหยุดและขอให้คุณแก้ CAPTCHA เอง CapSolver คิดค่าบริการต่อการแก้หนึ่งครั้ง (~$0.001–$0.003) โดยใช้บัญชีและคีย์ API ของคุณเอง", "st.captcha.enabled.label": "เปิดใช้งาน CapSolver", "st.captcha.enabled.desc": "เมื่อเอเจนต์เจอ CAPTCHA มันจะเรียก CapSolver หนึ่งครั้งก่อนถอยไปถามคุณ ต้องมีคีย์ API ด้านล่าง", diff --git a/src/chrome/src/ui/locales/tl.js b/src/chrome/src/ui/locales/tl.js index 6f9e3ced8..923a62fa1 100644 --- a/src/chrome/src/ui/locales/tl.js +++ b/src/chrome/src/ui/locales/tl.js @@ -609,6 +609,14 @@ export default { "st.transcription.connected": "Konektado! Modelo: {model}", "st.transcription.failed": "Nabigo: {error}", "st.transcription.fill_required": "Punan muna ang Base URL at Modelo.", + "st.imagegen.heading": "Generative media (fal.ai)", + "st.imagegen.desc": "Ginagamit ng generate_image agent tool para gumawa ng mga larawan at iba pang media mula sa text prompt sa pamamagitan ng fal.ai queue API. Kumuha ng key sa fal.ai/dashboard/keys. Hindi ginagamit sa chat.", + "st.imagegen.saved": "Na-save!", + "st.imagegen.cleared": "Na-clear.", + "st.imagegen.testing": "Sinusuri...", + "st.imagegen.connected": "Nakakonekta! Model: {model}", + "st.imagegen.failed": "Nabigo: {error}", + "st.imagegen.fill_required": "Punan muna ang API Key at Model.", "st.captcha.desc_html": "Hayaan ang ahente na awtomatikong lutasin ang mga CAPTCHA sa pamamagitan ng CapSolver API. Sinusuportahan ang reCAPTCHA v2/v3, hCaptcha, at Cloudflare Turnstile. Awtomatikong pinapagana ang CapSolver kapag nag-save ka ng wastong API key; kung walang key, hihinto ang ahente at hihilingin sa iyong lutasin mismo ang CAPTCHA. Naniningil ang CapSolver sa bawat solve (~$0.001–$0.003); ginagamit mo ang sarili mong account at API key.", "st.captcha.enabled.label": "I-enable ang CapSolver", "st.captcha.enabled.desc": "Kapag may naabot na CAPTCHA ang ahente, tatawag ito sa CapSolver nang isang beses bago bumalik sa pagtatanong sa iyo. Kailangan ng API key sa ibaba.", diff --git a/src/chrome/src/ui/locales/tr.js b/src/chrome/src/ui/locales/tr.js index df5ecc570..e8e11a923 100644 --- a/src/chrome/src/ui/locales/tr.js +++ b/src/chrome/src/ui/locales/tr.js @@ -649,6 +649,14 @@ export default { "st.transcription.connected": "Bağlandı! Model: {model}", "st.transcription.failed": "Başarısız: {error}", "st.transcription.fill_required": "Önce Temel URL ve Model alanlarını doldur.", + "st.imagegen.heading": "Üretken medya (fal.ai)", + "st.imagegen.desc": "generate_image ajan aracı tarafından metin isteminden fal.ai'nin kuyruk API'si üzerinden görsel ve diğer medyaları oluşturmak için kullanılır. Anahtar alın: fal.ai/dashboard/keys. Sohbet için kullanılmaz.", + "st.imagegen.saved": "Kaydedildi!", + "st.imagegen.cleared": "Temizlendi.", + "st.imagegen.testing": "Test ediliyor...", + "st.imagegen.connected": "Bağlandı! Model: {model}", + "st.imagegen.failed": "Başarısız: {error}", + "st.imagegen.fill_required": "Önce API anahtarını ve modeli doldurun.", "st.captcha.desc_html": "Aracının CAPTCHA'ları CapSolver API'si üzerinden otomatik çözmesine izin ver. reCAPTCHA v2/v3, hCaptcha ve Cloudflare Turnstile desteklenir. Geçerli bir API anahtarı kaydedildiğinde CapSolver otomatik olarak etkinleşir; anahtar yoksa aracı durur ve CAPTCHA'yı senin çözmeni ister. CapSolver her çözüm için ücret alır (~$0.001–$0.003); kendi hesabını ve API anahtarını kullanırsın.", "st.captcha.enabled.label": "CapSolver'ı etkinleştir", "st.captcha.enabled.desc": "Aracı bir CAPTCHA ile karşılaştığında, sana sormaya geri dönmeden önce bir kez CapSolver'ı çağırır. Aşağıda bir API anahtarı gerektirir.", diff --git a/src/chrome/src/ui/locales/uk.js b/src/chrome/src/ui/locales/uk.js index c0304feed..e3cd9b0ba 100644 --- a/src/chrome/src/ui/locales/uk.js +++ b/src/chrome/src/ui/locales/uk.js @@ -609,6 +609,14 @@ export default { "st.transcription.connected": "Підключено! Модель: {model}", "st.transcription.failed": "Збій: {error}", "st.transcription.fill_required": "Спочатку заповніть базовий URL і модель.", + "st.imagegen.heading": "Генеративні медіа (fal.ai)", + "st.imagegen.desc": "Використовується інструментом агента generate_image для створення зображень та інших медіа з текстового запиту через API черги fal.ai. Ключ можна отримати на fal.ai/dashboard/keys. Не використовується для чату.", + "st.imagegen.saved": "Збережено!", + "st.imagegen.cleared": "Очищено.", + "st.imagegen.testing": "Перевірка...", + "st.imagegen.connected": "Підключено! Модель: {model}", + "st.imagegen.failed": "Помилка: {error}", + "st.imagegen.fill_required": "Спочатку заповніть ключ API і модель.", "st.captcha.desc_html": "Дозвольте агенту автоматично розв'язувати CAPTCHA через API CapSolver. Підтримує reCAPTCHA v2/v3, hCaptcha та Cloudflare Turnstile. Збереження дійсного API-ключа автоматично вмикає CapSolver; без ключа агент зупиняється й просить вас розв'язати CAPTCHA самостійно. CapSolver стягує плату за кожне розв'язання (~$0.001–$0.003); ви використовуєте власний акаунт і API-ключ.", "st.captcha.enabled.label": "Увімкнути CapSolver", "st.captcha.enabled.desc": "Коли агент натрапляє на CAPTCHA, він один раз викличе CapSolver, перш ніж повернутися до запиту до вас. Потрібен API-ключ нижче.", diff --git a/src/chrome/src/ui/locales/vi.js b/src/chrome/src/ui/locales/vi.js index 616367a71..758535407 100644 --- a/src/chrome/src/ui/locales/vi.js +++ b/src/chrome/src/ui/locales/vi.js @@ -898,6 +898,14 @@ export default { 'st.transcription.connected': "Đã kết nối! Model: {model}", 'st.transcription.failed': "Không thành công: {error}", 'st.transcription.fill_required': "Trước tiên hãy điền URL cơ sở và Mô hình.", + "st.imagegen.heading": "Media tạo sinh (fal.ai)", + "st.imagegen.desc": "Được công cụ tác nhân generate_image sử dụng để tạo hình ảnh và phương tiện khác từ lời nhắc văn bản thông qua API hàng đợi của fal.ai. Lấy khóa tại fal.ai/dashboard/keys. Không dùng cho trò chuyện.", + "st.imagegen.saved": "Đã lưu!", + "st.imagegen.cleared": "Đã xóa.", + "st.imagegen.testing": "Đang kiểm tra...", + "st.imagegen.connected": "Đã kết nối! Mô hình: {model}", + "st.imagegen.failed": "Thất bại: {error}", + "st.imagegen.fill_required": "Điền Khóa API và Mô hình trước.", 'st.imageBudget.heading': "Ngân sách hình ảnh", 'st.imageBudget.desc': "Kiểm soát kích thước ảnh chụp màn hình và số ảnh tác nhân chụp cho thị giác trong mỗi lượt. Mức chi tiết và kích thước thấp hơn giúp giảm chi phí và độ trễ cho các endpoint nhỏ; mức cao hơn giữ độ trung thực. Giá trị mặc định khớp với hành vi trước đây.", diff --git a/src/chrome/src/ui/locales/zh.js b/src/chrome/src/ui/locales/zh.js index ee50cec3f..86b893118 100644 --- a/src/chrome/src/ui/locales/zh.js +++ b/src/chrome/src/ui/locales/zh.js @@ -609,6 +609,14 @@ export default { "st.transcription.connected": "连接成功!模型:{model}", "st.transcription.failed": "失败:{error}", "st.transcription.fill_required": "请先填写基础 URL 和模型。", + "st.imagegen.heading": "生成式媒体 (fal.ai)", + "st.imagegen.desc": "generate_image 代理工具通过 fal.ai 的队列 API 根据文本提示生成图片等媒体。请前往 fal.ai/dashboard/keys 获取密钥。不用于聊天。", + "st.imagegen.saved": "已保存!", + "st.imagegen.cleared": "已清除。", + "st.imagegen.testing": "测试中...", + "st.imagegen.connected": "已连接!模型:{model}", + "st.imagegen.failed": "失败:{error}", + "st.imagegen.fill_required": "请先填写 API 密钥和模型。", "st.captcha.desc_html": "让代理通过 CapSolver API 自动解决 CAPTCHA。支持 reCAPTCHA v2/v3、hCaptcha 和 Cloudflare Turnstile。保存有效的 API 密钥后会自动启用 CapSolver;没有密钥时,代理会停止并请你自行解决 CAPTCHA。CapSolver 按每次解决计费(约 ~$0.001–$0.003);使用你自己的账号和 API 密钥。", "st.captcha.enabled.label": "启用 CapSolver", "st.captcha.enabled.desc": "当代理遇到 CAPTCHA 时,会先调用一次 CapSolver,然后再回退到询问你。需要下方的 API 密钥。", diff --git a/src/chrome/src/ui/settings.html b/src/chrome/src/ui/settings.html index f3f40e4fd..266becad5 100644 --- a/src/chrome/src/ui/settings.html +++ b/src/chrome/src/ui/settings.html @@ -1987,6 +1987,32 @@

+

+ + +

+
+
+
+ + +
+
+ + +
+
+ + + +
+
+
+

ייעודית להרשמות בעלות סיכון נמוך. כאשר האפשרות מופעלת, הטקסט שלהלן מצורף להנחיית המערכת של הסוכן בכל שיחה.", "st.profile.enabled.label": "הפעל מילוי אוטומטי של פרופיל", "st.profile.enabled.desc": "הכנס את טקסט הפרופיל להנחיית המערכת של הסוכן. מושבת = הסוכן לעולם לא רואה את הטקסט הזה.", diff --git a/src/firefox/src/ui/locales/hi.js b/src/firefox/src/ui/locales/hi.js index 9905ecca7..adbe69001 100644 --- a/src/firefox/src/ui/locales/hi.js +++ b/src/firefox/src/ui/locales/hi.js @@ -877,6 +877,14 @@ export default { 'st.transcription.connected': "जुड़ा हुआ! मॉडल: {model}", 'st.transcription.failed': "विफल: {error}", 'st.transcription.fill_required': "सबसे पहले बेस यूआरएल और मॉडल भरें।", + "st.imagegen.heading": "जनरेटिव मीडिया (fal.ai)", + "st.imagegen.desc": "generate_image एजेंट टूल द्वारा fal.ai की क्यू API के ज़रिए टेक्स्ट प्रॉम्प्ट से इमेज और अन्य मीडिया बनाने के लिए उपयोग किया जाता है। fal.ai/dashboard/keys से की लें। चैट के लिए उपयोग नहीं होता।", + "st.imagegen.saved": "सहेजा गया!", + "st.imagegen.cleared": "साफ़ कर दिया गया।", + "st.imagegen.testing": "जाँच जारी...", + "st.imagegen.connected": "कनेक्ट हो गया! मॉडल: {model}", + "st.imagegen.failed": "विफल: {error}", + "st.imagegen.fill_required": "पहले API की और मॉडल भरें।", 'st.imageBudget.heading': "छवि बजट", 'st.imageBudget.desc': "स्क्रीनशॉट का आकार और हर टर्न में विज़न के लिए एजेंट द्वारा लिए जाने वाले स्क्रीनशॉट की संख्या नियंत्रित करें। कम विवरण और छोटे आयाम छोटे एंडपॉइंट के लिए लागत और विलंबता घटाते हैं; अधिक मान गुणवत्ता बनाए रखते हैं। डिफ़ॉल्ट पिछले व्यवहार से मेल खाते हैं।", diff --git a/src/firefox/src/ui/locales/id.js b/src/firefox/src/ui/locales/id.js index ffb29b091..58ec1cc67 100644 --- a/src/firefox/src/ui/locales/id.js +++ b/src/firefox/src/ui/locales/id.js @@ -593,6 +593,14 @@ export default { "st.transcription.connected": "Terhubung! Model: {model}", "st.transcription.failed": "Gagal: {error}", "st.transcription.fill_required": "Isi URL dasar dan Model terlebih dahulu.", + "st.imagegen.heading": "Media generatif (fal.ai)", + "st.imagegen.desc": "Digunakan oleh alat agen generate_image untuk membuat gambar dan media lain dari prompt teks melalui API antrean fal.ai. Dapatkan kunci di fal.ai/dashboard/keys. Tidak digunakan untuk obrolan.", + "st.imagegen.saved": "Tersimpan!", + "st.imagegen.cleared": "Dihapus.", + "st.imagegen.testing": "Menguji...", + "st.imagegen.connected": "Terhubung! Model: {model}", + "st.imagegen.failed": "Gagal: {error}", + "st.imagegen.fill_required": "Isi Kunci API dan Model terlebih dahulu.", "st.captcha.desc_html": "Biarkan agen menyelesaikan CAPTCHA secara otomatis melalui API CapSolver. Mendukung reCAPTCHA v2/v3, hCaptcha, dan Cloudflare Turnstile. Menyimpan kunci API yang valid akan mengaktifkan CapSolver secara otomatis; tanpa kunci, agen berhenti dan meminta Anda menyelesaikan CAPTCHA sendiri. CapSolver mengenakan biaya per penyelesaian (~$0.001–$0.003); Anda menggunakan akun dan kunci API sendiri.", "st.captcha.enabled.label": "Aktifkan CapSolver", "st.captcha.enabled.desc": "Saat agen menemui CAPTCHA, ia akan memanggil CapSolver sekali sebelum kembali bertanya kepada Anda. Memerlukan kunci API di bawah.", diff --git a/src/firefox/src/ui/locales/ja.js b/src/firefox/src/ui/locales/ja.js index 1e1c55ae5..a05887113 100644 --- a/src/firefox/src/ui/locales/ja.js +++ b/src/firefox/src/ui/locales/ja.js @@ -593,6 +593,14 @@ export default { "st.transcription.connected": "接続しました!モデル: {model}", "st.transcription.failed": "失敗: {error}", "st.transcription.fill_required": "先にベース URL とモデルを入力してください。", + "st.imagegen.heading": "生成メディア (fal.ai)", + "st.imagegen.desc": "generate_image エージェントツールが fal.ai のキュー API を通じてテキストプロンプトから画像などのメディアを生成するために使用します。キーは fal.ai/dashboard/keys で取得できます。チャットには使用されません。", + "st.imagegen.saved": "保存しました!", + "st.imagegen.cleared": "クリアしました。", + "st.imagegen.testing": "テスト中...", + "st.imagegen.connected": "接続しました!モデル: {model}", + "st.imagegen.failed": "失敗: {error}", + "st.imagegen.fill_required": "先に API キーとモデルを入力してください。", "st.captcha.desc_html": "CapSolver API を使って、エージェントに CAPTCHA を自動で解かせます。reCAPTCHA v2/v3、hCaptcha、Cloudflare Turnstile に対応しています。有効な API キーを保存すると CapSolver が自動的に有効になります。キーがない場合、エージェントは停止し、CAPTCHA を手動で解くよう求めます。CapSolver は解決ごとに課金します(約 ~$0.001–$0.003)。ご自身のアカウントと API キーを使用します。", "st.captcha.enabled.label": "CapSolver を有効化", "st.captcha.enabled.desc": "エージェントが CAPTCHA に遭遇すると、あなたに尋ねる前にまず CapSolver を 1 回呼び出します。下記の API キーが必要です。", diff --git a/src/firefox/src/ui/locales/ko.js b/src/firefox/src/ui/locales/ko.js index ec940ad2f..50da23829 100644 --- a/src/firefox/src/ui/locales/ko.js +++ b/src/firefox/src/ui/locales/ko.js @@ -593,6 +593,14 @@ export default { "st.transcription.connected": "연결됨! 모델: {model}", "st.transcription.failed": "실패: {error}", "st.transcription.fill_required": "먼저 기본 URL과 모델을 입력하세요.", + "st.imagegen.heading": "생성 미디어 (fal.ai)", + "st.imagegen.desc": "generate_image 에이전트 도구가 fal.ai 큐 API를 통해 텍스트 프롬프트에서 이미지 등 미디어를 생성하는 데 사용됩니다. fal.ai/dashboard/keys에서 키를 발급받으세요. 채팅에는 사용되지 않습니다.", + "st.imagegen.saved": "저장되었습니다!", + "st.imagegen.cleared": "지워졌습니다.", + "st.imagegen.testing": "테스트 중...", + "st.imagegen.connected": "연결됨! 모델: {model}", + "st.imagegen.failed": "실패: {error}", + "st.imagegen.fill_required": "먼저 API 키와 모델을 입력하세요.", "st.captcha.desc_html": "에이전트가 CapSolver API를 통해 CAPTCHA를 자동으로 풀게 합니다. reCAPTCHA v2/v3, hCaptcha, Cloudflare Turnstile을 지원합니다. 유효한 API 키를 저장하면 CapSolver가 자동으로 활성화됩니다. 키가 없으면 에이전트가 멈추고 CAPTCHA를 직접 풀어 달라고 요청합니다. CapSolver는 풀이당 요금을 부과하며(~$0.001–$0.003), 본인의 계정과 API 키를 사용합니다.", "st.captcha.enabled.label": "CapSolver 사용", "st.captcha.enabled.desc": "에이전트가 CAPTCHA를 만나면 사용자에게 묻기로 폴백하기 전에 CapSolver를 한 번 호출합니다. 아래에 API 키가 필요합니다.", diff --git a/src/firefox/src/ui/locales/ms.js b/src/firefox/src/ui/locales/ms.js index 3c555ec2d..6a66f7dac 100644 --- a/src/firefox/src/ui/locales/ms.js +++ b/src/firefox/src/ui/locales/ms.js @@ -593,6 +593,14 @@ export default { "st.transcription.connected": "Tersambung! Model: {model}", "st.transcription.failed": "Gagal: {error}", "st.transcription.fill_required": "Isi URL Asas dan Model terlebih dahulu.", + "st.imagegen.heading": "Media generatif (fal.ai)", + "st.imagegen.desc": "Digunakan oleh alat ejen generate_image untuk mencipta imej dan media lain daripada geseran teks melalui API baris giliran fal.ai. Dapatkan kunci di fal.ai/dashboard/keys. Tidak digunakan untuk sembang.", + "st.imagegen.saved": "Disimpan!", + "st.imagegen.cleared": "Dibersihkan.", + "st.imagegen.testing": "Menguji...", + "st.imagegen.connected": "Berjaya disambung! Model: {model}", + "st.imagegen.failed": "Gagal: {error}", + "st.imagegen.fill_required": "Isi Kunci API dan Model dahulu.", "st.captcha.desc_html": "Biarkan ejen menyelesaikan CAPTCHA secara automatik melalui API CapSolver. Menyokong reCAPTCHA v2/v3, hCaptcha dan Cloudflare Turnstile. Menyimpan kunci API yang sah akan mengaktifkan CapSolver secara automatik; tanpa kunci, ejen berhenti dan meminta anda menyelesaikan CAPTCHA sendiri. CapSolver mengenakan bayaran bagi setiap penyelesaian (~$0.001–$0.003); anda menggunakan akaun dan kunci API sendiri.", "st.captcha.enabled.label": "Dayakan CapSolver", "st.captcha.enabled.desc": "Apabila ejen menemui CAPTCHA, ia akan memanggil CapSolver sekali sebelum berundur untuk bertanya kepada anda. Memerlukan kunci API di bawah.", diff --git a/src/firefox/src/ui/locales/nl.js b/src/firefox/src/ui/locales/nl.js index b3f065ced..1090924e7 100644 --- a/src/firefox/src/ui/locales/nl.js +++ b/src/firefox/src/ui/locales/nl.js @@ -785,6 +785,14 @@ export default { 'st.transcription.connected': 'Verbonden! Model: {model}', 'st.transcription.failed': 'Mislukt: {error}', 'st.transcription.fill_required': 'Vul eerst basis-URL en model in.', + "st.imagegen.heading": "Generatieve media (fal.ai)", + "st.imagegen.desc": "Wordt gebruikt door de agenttool generate_image om afbeeldingen en andere media te maken vanuit een tekstprompt via de queue-API van fal.ai. Vraag een sleutel aan op fal.ai/dashboard/keys. Niet voor chat.", + "st.imagegen.saved": "Opgeslagen!", + "st.imagegen.cleared": "Wissen gelukt.", + "st.imagegen.testing": "Testen...", + "st.imagegen.connected": "Verbonden! Model: {model}", + "st.imagegen.failed": "Mislukt: {error}", + "st.imagegen.fill_required": "Vul eerst de API-sleutel en het model in.", 'st.imageBudget.heading': 'Afbeeldingsbudget', 'st.imageBudget.desc': 'Beheer schermafbeeldingsgrootte en hoeveel de agent vastlegt voor visie per beurt...', 'st.imageBudget.detail.label': 'Afbeeldingsdetail', diff --git a/src/firefox/src/ui/locales/pl.js b/src/firefox/src/ui/locales/pl.js index 942aae174..b04fee860 100644 --- a/src/firefox/src/ui/locales/pl.js +++ b/src/firefox/src/ui/locales/pl.js @@ -620,6 +620,14 @@ export default { 'st.transcription.connected': 'Połączono! Model: {model}', 'st.transcription.failed': 'Niepowodzenie: {error}', 'st.transcription.fill_required': 'Najpierw wypełnij bazowy adres URL i model.', + "st.imagegen.heading": "Media generatywne (fal.ai)", + "st.imagegen.desc": "Używane przez narzędzie agenta generate_image do tworzenia obrazów i innych mediów z promptu tekstowego przez API kolejki fal.ai. Klucz uzyskasz na fal.ai/dashboard/keys. Nieużywane do czatu.", + "st.imagegen.saved": "Zapisano!", + "st.imagegen.cleared": "Wyczyszczono.", + "st.imagegen.testing": "Testowanie...", + "st.imagegen.connected": "Połączono! Model: {model}", + "st.imagegen.failed": "Niepowodzenie: {error}", + "st.imagegen.fill_required": "Najpierw wypełnij klucz API i model.", 'st.profile.desc_html': 'Przechowuj krótki opis, którego agent może użyć do wypełniania formularzy rejestracji bez pytania za każdym razem — Twoje imię, służbowy e-mail, firmę i jednorazowe hasło do mało istotnych rejestracji. Po włączeniu poniższy tekst jest dołączany do promptu systemowego agenta w każdej rozmowie.', 'st.profile.enabled.label': 'Włącz automatyczne wypełnianie profilu', 'st.profile.enabled.desc': 'Wstrzykuj tekst profilu do promptu systemowego agenta. Wyłączone = agent nigdy nie widzi tego tekstu.', diff --git a/src/firefox/src/ui/locales/pt.js b/src/firefox/src/ui/locales/pt.js index 9a6d50804..db62986bc 100644 --- a/src/firefox/src/ui/locales/pt.js +++ b/src/firefox/src/ui/locales/pt.js @@ -877,6 +877,14 @@ export default { 'st.transcription.connected': "Conectado! Modelo: {model}", 'st.transcription.failed': "Falha: {error}", 'st.transcription.fill_required': "Preencha primeiro o URL base e o modelo.", + "st.imagegen.heading": "Mídia generativa (fal.ai)", + "st.imagegen.desc": "Usada pela ferramenta de agente generate_image para criar imagens e outras mídias a partir de um prompt de texto pela API de fila da fal.ai. Obtenha uma chave em fal.ai/dashboard/keys. Não usada para chat.", + "st.imagegen.saved": "Salvo!", + "st.imagegen.cleared": "Limpo.", + "st.imagegen.testing": "Testando...", + "st.imagegen.connected": "Conectado! Modelo: {model}", + "st.imagegen.failed": "Falhou: {error}", + "st.imagegen.fill_required": "Preencha primeiro a chave de API e o modelo.", 'st.imageBudget.heading': "Orçamento de imagens", 'st.imageBudget.desc': "Controle o tamanho das capturas de tela e quantas o agente faz para visão por turno. Menos detalhes e dimensões menores reduzem custo e latência em endpoints menores; valores maiores preservam a fidelidade. Os padrões correspondem ao comportamento anterior.", diff --git a/src/firefox/src/ui/locales/ru.js b/src/firefox/src/ui/locales/ru.js index f77112b74..3256fa9ce 100644 --- a/src/firefox/src/ui/locales/ru.js +++ b/src/firefox/src/ui/locales/ru.js @@ -593,6 +593,14 @@ export default { "st.transcription.connected": "Подключено! Модель: {model}", "st.transcription.failed": "Сбой: {error}", "st.transcription.fill_required": "Сначала заполните базовый URL и модель.", + "st.imagegen.heading": "Генеративные медиа (fal.ai)", + "st.imagegen.desc": "Используется инструментом агента generate_image для создания изображений и других медиа из текстового запроса через очередь API fal.ai. Ключ можно получить на fal.ai/dashboard/keys. Не используется для чата.", + "st.imagegen.saved": "Сохранено!", + "st.imagegen.cleared": "Очищено.", + "st.imagegen.testing": "Проверка...", + "st.imagegen.connected": "Подключено! Модель: {model}", + "st.imagegen.failed": "Ошибка: {error}", + "st.imagegen.fill_required": "Сначала заполните ключ API и модель.", "st.captcha.desc_html": "Позвольте агенту автоматически решать CAPTCHA через API CapSolver. Поддерживает reCAPTCHA v2/v3, hCaptcha и Cloudflare Turnstile. Сохранение действительного API-ключа автоматически включает CapSolver; без ключа агент останавливается и просит вас решить CAPTCHA самостоятельно. CapSolver берёт плату за каждое решение (~$0.001–$0.003); используется ваш аккаунт и API-ключ.", "st.captcha.enabled.label": "Включить CapSolver", "st.captcha.enabled.desc": "Когда агент сталкивается с CAPTCHA, он один раз обращается к CapSolver, прежде чем перейти к запросу к вам. Требуется API-ключ ниже.", diff --git a/src/firefox/src/ui/locales/th.js b/src/firefox/src/ui/locales/th.js index 8ae956728..9f3aa3384 100644 --- a/src/firefox/src/ui/locales/th.js +++ b/src/firefox/src/ui/locales/th.js @@ -593,6 +593,14 @@ export default { "st.transcription.connected": "เชื่อมต่อแล้ว! โมเดล: {model}", "st.transcription.failed": "ล้มเหลว: {error}", "st.transcription.fill_required": "กรอก URL ฐานและโมเดลก่อน", + "st.imagegen.heading": "สื่อเชิงสร้างสรรค์ (fal.ai)", + "st.imagegen.desc": "เครื่องมือเอเจนต์ generate_image ใช้สร้างรูปภาพและสื่ออื่น ๆ จากพรอมต์ข้อความผ่านคิว API ของ fal.ai รับคีย์ได้ที่ fal.ai/dashboard/keys ไม่ได้ใช้สำหรับแชท", + "st.imagegen.saved": "บันทึกแล้ว!", + "st.imagegen.cleared": "ล้างแล้ว.", + "st.imagegen.testing": "กำลังทดสอบ...", + "st.imagegen.connected": "เชื่อมต่อแล้ว! โมเดล: {model}", + "st.imagegen.failed": "ล้มเหลว: {error}", + "st.imagegen.fill_required": "กรอกคีย์ API และโมเดลก่อน", "st.captcha.desc_html": "ให้เอเจนต์แก้ CAPTCHA โดยอัตโนมัติผ่าน API ของ CapSolver รองรับ reCAPTCHA v2/v3, hCaptcha และ Cloudflare Turnstile การบันทึกคีย์ API ที่ถูกต้องจะเปิดใช้ CapSolver โดยอัตโนมัติ หากไม่มีคีย์ เอเจนต์จะหยุดและขอให้คุณแก้ CAPTCHA เอง CapSolver คิดค่าบริการต่อการแก้หนึ่งครั้ง (~$0.001–$0.003) โดยใช้บัญชีและคีย์ API ของคุณเอง", "st.captcha.enabled.label": "เปิดใช้งาน CapSolver", "st.captcha.enabled.desc": "เมื่อเอเจนต์เจอ CAPTCHA มันจะเรียก CapSolver หนึ่งครั้งก่อนถอยไปถามคุณ ต้องมีคีย์ API ด้านล่าง", diff --git a/src/firefox/src/ui/locales/tl.js b/src/firefox/src/ui/locales/tl.js index 1a75a20f2..ae9ee6406 100644 --- a/src/firefox/src/ui/locales/tl.js +++ b/src/firefox/src/ui/locales/tl.js @@ -593,6 +593,14 @@ export default { "st.transcription.connected": "Konektado! Modelo: {model}", "st.transcription.failed": "Nabigo: {error}", "st.transcription.fill_required": "Punan muna ang Base URL at Modelo.", + "st.imagegen.heading": "Generative media (fal.ai)", + "st.imagegen.desc": "Ginagamit ng generate_image agent tool para gumawa ng mga larawan at iba pang media mula sa text prompt sa pamamagitan ng fal.ai queue API. Kumuha ng key sa fal.ai/dashboard/keys. Hindi ginagamit sa chat.", + "st.imagegen.saved": "Na-save!", + "st.imagegen.cleared": "Na-clear.", + "st.imagegen.testing": "Sinusuri...", + "st.imagegen.connected": "Nakakonekta! Model: {model}", + "st.imagegen.failed": "Nabigo: {error}", + "st.imagegen.fill_required": "Punan muna ang API Key at Model.", "st.captcha.desc_html": "Hayaan ang ahente na awtomatikong lutasin ang mga CAPTCHA sa pamamagitan ng CapSolver API. Sinusuportahan ang reCAPTCHA v2/v3, hCaptcha, at Cloudflare Turnstile. Awtomatikong pinapagana ang CapSolver kapag nag-save ka ng wastong API key; kung walang key, hihinto ang ahente at hihilingin sa iyong lutasin mismo ang CAPTCHA. Naniningil ang CapSolver sa bawat solve (~$0.001–$0.003); ginagamit mo ang sarili mong account at API key.", "st.captcha.enabled.label": "I-enable ang CapSolver", "st.captcha.enabled.desc": "Kapag may naabot na CAPTCHA ang ahente, tatawag ito sa CapSolver nang isang beses bago bumalik sa pagtatanong sa iyo. Kailangan ng API key sa ibaba.", diff --git a/src/firefox/src/ui/locales/tr.js b/src/firefox/src/ui/locales/tr.js index 8c7739266..a31fa7584 100644 --- a/src/firefox/src/ui/locales/tr.js +++ b/src/firefox/src/ui/locales/tr.js @@ -633,6 +633,14 @@ export default { "st.transcription.connected": "Bağlandı! Model: {model}", "st.transcription.failed": "Başarısız: {error}", "st.transcription.fill_required": "Önce Temel URL ve Model alanlarını doldur.", + "st.imagegen.heading": "Üretken medya (fal.ai)", + "st.imagegen.desc": "generate_image ajan aracı tarafından metin isteminden fal.ai'nin kuyruk API'si üzerinden görsel ve diğer medyaları oluşturmak için kullanılır. Anahtar alın: fal.ai/dashboard/keys. Sohbet için kullanılmaz.", + "st.imagegen.saved": "Kaydedildi!", + "st.imagegen.cleared": "Temizlendi.", + "st.imagegen.testing": "Test ediliyor...", + "st.imagegen.connected": "Bağlandı! Model: {model}", + "st.imagegen.failed": "Başarısız: {error}", + "st.imagegen.fill_required": "Önce API anahtarını ve modeli doldurun.", "st.captcha.desc_html": "Aracının CAPTCHA'ları CapSolver API'si üzerinden otomatik çözmesine izin ver. reCAPTCHA v2/v3, hCaptcha ve Cloudflare Turnstile desteklenir. Geçerli bir API anahtarı kaydedildiğinde CapSolver otomatik olarak etkinleşir; anahtar yoksa aracı durur ve CAPTCHA'yı senin çözmeni ister. CapSolver her çözüm için ücret alır (~$0.001–$0.003); kendi hesabını ve API anahtarını kullanırsın.", "st.captcha.enabled.label": "CapSolver'ı etkinleştir", "st.captcha.enabled.desc": "Aracı bir CAPTCHA ile karşılaştığında, sana sormaya geri dönmeden önce bir kez CapSolver'ı çağırır. Aşağıda bir API anahtarı gerektirir.", diff --git a/src/firefox/src/ui/locales/uk.js b/src/firefox/src/ui/locales/uk.js index 1a66ff607..7aa423ebe 100644 --- a/src/firefox/src/ui/locales/uk.js +++ b/src/firefox/src/ui/locales/uk.js @@ -593,6 +593,14 @@ export default { "st.transcription.connected": "Підключено! Модель: {model}", "st.transcription.failed": "Збій: {error}", "st.transcription.fill_required": "Спочатку заповніть базовий URL і модель.", + "st.imagegen.heading": "Генеративні медіа (fal.ai)", + "st.imagegen.desc": "Використовується інструментом агента generate_image для створення зображень та інших медіа з текстового запиту через API черги fal.ai. Ключ можна отримати на fal.ai/dashboard/keys. Не використовується для чату.", + "st.imagegen.saved": "Збережено!", + "st.imagegen.cleared": "Очищено.", + "st.imagegen.testing": "Перевірка...", + "st.imagegen.connected": "Підключено! Модель: {model}", + "st.imagegen.failed": "Помилка: {error}", + "st.imagegen.fill_required": "Спочатку заповніть ключ API і модель.", "st.captcha.desc_html": "Дозвольте агенту автоматично розв'язувати CAPTCHA через API CapSolver. Підтримує reCAPTCHA v2/v3, hCaptcha та Cloudflare Turnstile. Збереження дійсного API-ключа автоматично вмикає CapSolver; без ключа агент зупиняється й просить вас розв'язати CAPTCHA самостійно. CapSolver стягує плату за кожне розв'язання (~$0.001–$0.003); ви використовуєте власний акаунт і API-ключ.", "st.captcha.enabled.label": "Увімкнути CapSolver", "st.captcha.enabled.desc": "Коли агент натрапляє на CAPTCHA, він один раз викличе CapSolver, перш ніж повернутися до запиту до вас. Потрібен API-ключ нижче.", diff --git a/src/firefox/src/ui/locales/vi.js b/src/firefox/src/ui/locales/vi.js index 200a7b6cc..d946d1ad3 100644 --- a/src/firefox/src/ui/locales/vi.js +++ b/src/firefox/src/ui/locales/vi.js @@ -877,6 +877,14 @@ export default { 'st.transcription.connected': "Đã kết nối! Model: {model}", 'st.transcription.failed': "Không thành công: {error}", 'st.transcription.fill_required': "Trước tiên hãy điền URL cơ sở và Mô hình.", + "st.imagegen.heading": "Media tạo sinh (fal.ai)", + "st.imagegen.desc": "Được công cụ tác nhân generate_image sử dụng để tạo hình ảnh và phương tiện khác từ lời nhắc văn bản thông qua API hàng đợi của fal.ai. Lấy khóa tại fal.ai/dashboard/keys. Không dùng cho trò chuyện.", + "st.imagegen.saved": "Đã lưu!", + "st.imagegen.cleared": "Đã xóa.", + "st.imagegen.testing": "Đang kiểm tra...", + "st.imagegen.connected": "Đã kết nối! Mô hình: {model}", + "st.imagegen.failed": "Thất bại: {error}", + "st.imagegen.fill_required": "Điền Khóa API và Mô hình trước.", 'st.imageBudget.heading': "Ngân sách hình ảnh", 'st.imageBudget.desc': "Kiểm soát kích thước ảnh chụp màn hình và số ảnh tác nhân chụp cho thị giác trong mỗi lượt. Mức chi tiết và kích thước thấp hơn giúp giảm chi phí và độ trễ cho các endpoint nhỏ; mức cao hơn giữ độ trung thực. Giá trị mặc định khớp với hành vi trước đây.", diff --git a/src/firefox/src/ui/locales/zh.js b/src/firefox/src/ui/locales/zh.js index 990494942..cdc12bf54 100644 --- a/src/firefox/src/ui/locales/zh.js +++ b/src/firefox/src/ui/locales/zh.js @@ -593,6 +593,14 @@ export default { "st.transcription.connected": "连接成功!模型:{model}", "st.transcription.failed": "失败:{error}", "st.transcription.fill_required": "请先填写基础 URL 和模型。", + "st.imagegen.heading": "生成式媒体 (fal.ai)", + "st.imagegen.desc": "generate_image 代理工具通过 fal.ai 的队列 API 根据文本提示生成图片等媒体。请前往 fal.ai/dashboard/keys 获取密钥。不用于聊天。", + "st.imagegen.saved": "已保存!", + "st.imagegen.cleared": "已清除。", + "st.imagegen.testing": "测试中...", + "st.imagegen.connected": "已连接!模型:{model}", + "st.imagegen.failed": "失败:{error}", + "st.imagegen.fill_required": "请先填写 API 密钥和模型。", "st.captcha.desc_html": "让代理通过 CapSolver API 自动解决 CAPTCHA。支持 reCAPTCHA v2/v3、hCaptcha 和 Cloudflare Turnstile。保存有效的 API 密钥后会自动启用 CapSolver;没有密钥时,代理会停止并请你自行解决 CAPTCHA。CapSolver 按每次解决计费(约 ~$0.001–$0.003);使用你自己的账号和 API 密钥。", "st.captcha.enabled.label": "启用 CapSolver", "st.captcha.enabled.desc": "当代理遇到 CAPTCHA 时,会先调用一次 CapSolver,然后再回退到询问你。需要下方的 API 密钥。", diff --git a/src/firefox/src/ui/settings.html b/src/firefox/src/ui/settings.html index 5301982f4..052e14113 100644 --- a/src/firefox/src/ui/settings.html +++ b/src/firefox/src/ui/settings.html @@ -1771,6 +1771,32 @@

+

+ + +

+
+
+
+ + +
+
+ + +
+
+ + + +
+
+
+