From e49512158585b7e6fda492fdeb2925c64728e247 Mon Sep 17 00:00:00 2001 From: Omkar Bansod Date: Tue, 14 Oct 2025 01:01:45 +0530 Subject: [PATCH 1/7] Add BrowserOS prompt editor to the options UI --- BrowserOS.md | 20 +++ src/options/OptionsNew.tsx | 129 +++++++++----- .../components/BrowserOSPromptEditor.tsx | 157 ++++++++++++++++++ src/options/components/SettingsLayout.tsx | 86 ++++++++-- 4 files changed, 339 insertions(+), 53 deletions(-) create mode 100644 BrowserOS.md create mode 100644 src/options/components/BrowserOSPromptEditor.tsx diff --git a/BrowserOS.md b/BrowserOS.md new file mode 100644 index 00000000..d05af9bb --- /dev/null +++ b/BrowserOS.md @@ -0,0 +1,20 @@ +# BrowserOS System Prompt + +This document captures the core guardrails that are always included when the built-in BrowserOS provider is selected. Any custom text that you enter in the BrowserOS provider settings page is prepended to this prompt at runtime. + +## Mission +- Act as a focused browser co-pilot that can plan, execute, and explain actions taken on behalf of the user. +- Respect the user's instructions, existing browser state, and privacy settings at all times. +- Prefer accurate, verifiable answers over speculation. Say "I don't know" if the information is unavailable. + +## Tools and Capabilities +- You can orchestrate BrowserOS automation tools (navigation, clicking, typing, scrolling, extraction, screenshot, tab control, etc.). +- You can analyze the provided `` snapshots to understand the current page before acting. +- Use visual-labelled node IDs from the screenshot when available; fall back to text search tools when necessary. + +## Operating Guidelines +1. Before taking action, restate the user's task and confirm the relevant context from the latest browser state. +2. Think step-by-step. Plan actions, execute them via the provided tools, then reassess the browser state. +3. Never expose raw ``, ``, or internal tool responses directly to the user. Summarize instead. +4. If a tool call fails, retry with an alternative approach or request human input after reasonable attempts. +5. When tasks complete, deliver a concise final answer explaining what was done and cite any important URLs or data that influenced the outcome. diff --git a/src/options/OptionsNew.tsx b/src/options/OptionsNew.tsx index 795cb307..9429560b 100644 --- a/src/options/OptionsNew.tsx +++ b/src/options/OptionsNew.tsx @@ -1,14 +1,15 @@ -import React, { useState, useEffect } from 'react' +import React, { useCallback, useMemo, useState, useEffect } from 'react' import { SettingsLayout } from './components/SettingsLayout' import { LLMProvidersSection } from './components/LLMProvidersSection' +import { BrowserOSPromptEditor } from './components/BrowserOSPromptEditor' import { ProviderTemplates } from './components/ProviderTemplates' import { ConfiguredModelsList } from './components/ConfiguredModelsList' import { AddProviderModal } from './components/AddProviderModal' import { useBrowserOSPrefs } from './hooks/useBrowserOSPrefs' -import { useOptionsStore } from './stores/optionsStore' import { useSettingsStore } from '@/sidepanel/stores/settingsStore' import { testLLMProvider } from './services/llm-test-service' import { LLMProvider, TestResult } from './types/llm-settings' +import { Bot, FileText } from 'lucide-react' import './styles.css' export function OptionsNew() { @@ -52,12 +53,28 @@ export function OptionsNew() { return () => window.removeEventListener('storage', handleStorageChange) }, [theme]) - const handleUseTemplate = (template: LLMProvider) => { + const handleUseTemplate = useCallback((template: LLMProvider) => { setEditingProvider(template) setIsAddingProvider(true) - } + }, [setEditingProvider, setIsAddingProvider]) - const handleSaveProvider = async (provider: Partial) => { + const browserOSProvider = useMemo( + () => providers.find(provider => provider.id === 'browseros'), + [providers] + ) + + const handleSaveBrowserOSPrompt = useCallback(async (prompt: string) => { + const currentBrowserOSProvider = providers.find(provider => provider.id === 'browseros') + if (!currentBrowserOSProvider) { + throw new Error('BrowserOS provider not found') + } + await updateProvider({ + ...currentBrowserOSProvider, + systemPrompt: prompt + }) + }, [providers, updateProvider]) + + const handleSaveProvider = useCallback(async (provider: Partial) => { try { if (editingProvider?.id) { await updateProvider(provider as LLMProvider) @@ -70,9 +87,9 @@ export function OptionsNew() { // Show error to user - the error will be displayed in the modal throw error } - } + }, [editingProvider, updateProvider, addProvider]) - const handleTestProvider = async (providerId: string) => { + const handleTestProvider = useCallback(async (providerId: string) => { const provider = providers.find(p => p.id === providerId) if (!provider) return @@ -98,40 +115,72 @@ export function OptionsNew() { } })) } - } + }, [providers, testLLMProvider]) - return ( - -
- setIsAddingProvider(true)} - /> + const sections = useMemo(() => [ + { + id: 'browseros-ai', + label: 'BrowserOS AI', + icon: Bot, + content: ( +
+ setIsAddingProvider(true)} + /> + + - - - { - setEditingProvider(provider) - setIsAddingProvider(true) - }} - onDelete={deleteProvider} - onClearTestResult={(providerId) => { - setTestResults(prev => { - const newResults = { ...prev } - delete newResults[providerId] - return newResults - }) - }} + { + setEditingProvider(provider) + setIsAddingProvider(true) + }} + onDelete={deleteProvider} + onClearTestResult={(providerId) => { + setTestResults(prev => { + const newResults = { ...prev } + delete newResults[providerId] + return newResults + }) + }} + /> +
+ ) + }, + { + id: 'browseros-system-prompt', + label: 'BrowserOS system prompt', + icon: FileText, + content: ( + -
+ ) + } + ], [ + browserOSProvider, + defaultProvider, + providers, + setDefaultProvider, + testResults, + handleUseTemplate, + handleSaveBrowserOSPrompt, + handleTestProvider, + deleteProvider + ]) + + return ( + <> + - + ) -} \ No newline at end of file +} diff --git a/src/options/components/BrowserOSPromptEditor.tsx b/src/options/components/BrowserOSPromptEditor.tsx new file mode 100644 index 00000000..f7b27308 --- /dev/null +++ b/src/options/components/BrowserOSPromptEditor.tsx @@ -0,0 +1,157 @@ +import React, { useEffect, useMemo, useRef, useState } from 'react' +import { LLMProvider } from '../types/llm-settings' + +const browserOSLogo = + typeof chrome !== 'undefined' && chrome?.runtime?.getURL + ? chrome.runtime.getURL('assets/browseros.svg') + : 'assets/browseros.svg' + +interface BrowserOSPromptEditorProps { + provider: LLMProvider | undefined + onSave: (prompt: string) => Promise +} + +export function BrowserOSPromptEditor({ provider, onSave }: BrowserOSPromptEditorProps) { + const [promptValue, setPromptValue] = useState('') + const [isSaving, setIsSaving] = useState(false) + const [errorMessage, setErrorMessage] = useState(null) + const [isEditing, setIsEditing] = useState(true) + + const providerId = provider?.id ?? 'browseros' + const previousProviderIdRef = useRef(null) + const previousSystemPromptRef = useRef(undefined) + + // Reset local state whenever provider changes + useEffect(() => { + const nextValue = provider?.systemPrompt ?? '' + setPromptValue(nextValue) + setErrorMessage(null) + + const providerChanged = previousProviderIdRef.current !== providerId + const systemPromptChanged = previousSystemPromptRef.current !== provider?.systemPrompt + + if (providerChanged) { + // Allow editing immediately for brand new provider setups with no saved prompt + setIsEditing(nextValue.length === 0) + } else if (systemPromptChanged) { + // After saving, lock editing until user opts back in + setIsEditing(false) + } + + previousProviderIdRef.current = providerId + previousSystemPromptRef.current = provider?.systemPrompt + }, [providerId, provider?.systemPrompt]) + + const isDirty = useMemo(() => { + const current = provider?.systemPrompt ?? '' + return promptValue !== current + }, [provider?.systemPrompt, promptValue]) + + if (!provider) { + return null + } + + const handleSave = async () => { + if (!isDirty || isSaving || !isEditing) return + setIsSaving(true) + setErrorMessage(null) + try { + await onSave(promptValue) + setIsEditing(false) + } catch (error) { + const message = error instanceof Error ? error.message : 'Failed to save prompt' + setErrorMessage(message) + } finally { + setIsSaving(false) + } + } + + const handleReset = () => { + if (isSaving || !isEditing) return + setPromptValue('') + setErrorMessage(null) + } + + const handleEnterEditMode = () => { + setIsEditing(true) + setErrorMessage(null) + } + + return ( +
+
+
+
+

+ BrowserOS system prompt +

+

+ Add optional instructions that run before the built-in BrowserOS guidance. + These notes are prepended whenever BrowserOS agent mode is active. +

+
+
+ BrowserOS logo +
+
+ +
+