Files
BrowserOS/reference-code/old-lib/tools/browser-navigation/SearchTool.ts
Felarof 8245dfe0ff Rewrite Agent Loop (#7)
* clean-up bunch of files for re-write

* more clean-up and adding basic agent

* Minor fix moved types into respective files.

* Deleted bunch of old files

backup

Update gitignore

Deleted a bunch of files

Remove message manager

Deleted old docs

Update rules

rename Profiler to profiler

* Temporarily adding old code

* Adding two small things back

* backup

* Implemented LangChainProvider and updated cursor rules

backup

LangChainProvider

curosr rules

* Implement tests for LangChainProvider -- unit test and integration test

integration test passes

integration test backup

* Tool Design

Tools Desing

tools design

* NavigationTool ready

NavigationTool ready

NavigationTool ready

NaivgationTool ready

backup

* MessageManager

MessageManager

backup

* Fixed integration test

* Agent design new

Updated agent design and added bunch of /NTN commands

agent new design

* Delete old agent design

* MessageManagerReadOnly class

* PlannerTool ready

PlannerTool almost ready

* ToolManager and DoneTool

* Integration of BrowserAgent

* BrowserAgent implementation v0.1

* BrowserAgent small fix v0.2

* Tool calling design

too call design

tool design claude

* Update agent tool design with // NTN

* add zod-to-json npm install

* BrowserAGent v0.3

* BrowserAgent v0.4

* BrowserAgent v0.5

* fixes

* Build error fixes in my NEWLY added code

build errors fix

* Build error fixes in old code (integration work)

backup

* Comment StreamEventProcessor for now, it is not used

* Small build error fix

* Small rename

* Added integration test to check structuredLLM and changed to 4o-mini

change default to nxtscape

integration test

* Small docstring

* Simplified BrowserAgent code and added integration test

Simplified BrowserAgent code

BrowserAGent integrationt est

* Update CLAUDE.md with project memory and instructions on how to write code

Update CLAUDE.md with project memory and instructions on how to write code

Project Memory

* Just a mova.. Moved ToolManager outside. Build works.

* TabOperations tool

TabOperations Tool and fixing some test

tab operations

* Update CLAUDE.md

* Added ClassificationTool

classifiction tool

classification prommpt

* Refactored and simplified PlannerTool unit test and integration test

* Updated Plnnaer tool

* Update CLAUDE.md

* BrowserAgent modified to do classification

BrowserAgent with classification

* minor fix to ToolManager

* Instead of ToolCall and ToolResult -- just updating message manager once

* minor fix to BrowserAgent integration test

* Changed done to "done_tool"

* Updated CLAUDE.md to reflect understanding of claude

* Uncommented stream event processor

* Renamed EventBus to StreamEventBus

* Commented StreamEventProcessor

* Event Processor

* Integrated EventProcessor with BrowserAgent

Added EventProcessor to BrowserAgetn

* Renamed StreamEventBus to EventBus

* Made EventBus required parameter in ExecutionContext

* PlanGenerator rewrite

PlanGenerator rewrite

backup

* For simple task, explicitly tell it to call done tool

* Max attempts for simple task

* backup

* Revert "backup"

This reverts commit 7d79a3d4d5774bfef79ec9827878b74edad3593f.

* Consolidating where EventBus and EventProcessor are created and initialized

backup

* Update CLAUDE.md

Update CLAUDE.md

* Improving agent loop code

Cleaned up processTooCall

classification task

* Create test-writer subAgent

test-agent-prompt

test agent prompt

test-agent-prompt

Update test-writer.md

* BrowserAgent test

Browseragent test

BrowserAgent test

* BrowserAgent refactor

backup

backup

* Minor fixes

* Minor fix

* minor change -- NEW AGENT LOOP IS WORKING WELL

* Update cursor rules

* Small change

* Improved BrowserAgent integration test

Improved BrowserAgent integration test

* Small change

* Update CLAUDE.md

* Different tools

* FindElementTool is ready

Find element update

backup

find element backup

* Updated to test strings to say "tests..."

* ScrollTool is ready

* RefreshStateTool is updated as well

* MessageManager updated

* SearchTool is ready

backup

* Interaction Element is also ready

* Add debugMessage emitter

* ValidatorTool ready and tests are passing

Validation Tool

validator tool

backup

backup

* GroupTabs tool ready

* Registered all the tools

* Planning changed to 5 steps

* BrowserAgent integration test fix

* Minor string changes

* backup

* Removed too many confusing events in EventProcessor -- there is only event.info right now

* Abort control implemented

backup

Abort

* Formatter for toolResult

Formatter for toolResult

backup

* Always render using Markdown

* Minor fix

---------

Co-authored-by: Nikhil Sonti <nikhilsv92@gmail.com>
2025-07-29 08:14:45 -07:00

250 lines
8.1 KiB
TypeScript

import { z } from 'zod';
import { NxtscapeTool } from '../base/NxtscapeTool';
import { ToolConfig } from '../base/ToolConfig';
import { ExecutionContext } from '@/lib/runtime/ExecutionContext';
/**
* Enum for search providers
*/
export const SearchProviderEnum = z.enum([
'google', // Google search
'amazon', // Amazon product search
'google_maps', // Google Maps location search
'google_finance' // Google Finance stock/financial search
]);
export type SearchProvider = z.infer<typeof SearchProviderEnum>;
/**
* Schema for search tool input
*/
export const SearchInputSchema = z.object({
searchProvider: SearchProviderEnum, // The search provider to use
query: z.string(), // The search query
intent: z.string().optional() // Optional description of why this search is being performed
});
export type SearchInput = z.infer<typeof SearchInputSchema>;
/**
* Schema for search tool output
*/
export const SearchOutputSchema = z.object({
success: z.boolean(), // Whether the operation succeeded
searchProvider: SearchProviderEnum, // Search provider that was used
message: z.string(), // Human-readable result message
query: z.string(), // The search query that was used
url: z.string() // The URL that was navigated to
});
export type SearchOutput = z.infer<typeof SearchOutputSchema>;
/**
* Tool for performing searches across different providers
*/
export class SearchTool extends NxtscapeTool<SearchInput, SearchOutput> {
constructor(executionContext: ExecutionContext) {
const config: ToolConfig<SearchInput, SearchOutput> = {
name: 'search',
description: 'Perform searches on different platforms. Providers: "google" (general web search), "amazon" (product search), "google_maps" (location search), "google_finance" (stock/financial search). Always pass searchProvider and query.',
category: 'navigation',
version: '1.0.0',
inputSchema: SearchInputSchema,
outputSchema: SearchOutputSchema,
examples: [
{
description: 'Search on Google',
input: {
searchProvider: 'google',
query: 'best programming laptops 2024',
intent: 'Finding information about programming laptops'
},
output: {
success: true,
searchProvider: 'google',
message: 'Searched for "best programming laptops 2024" on Google',
query: 'best programming laptops 2024',
url: 'https://www.google.com/search?q=best+programming+laptops+2024'
}
},
{
description: 'Search on Amazon',
input: {
searchProvider: 'amazon',
query: 'mechanical keyboard',
intent: 'Looking for mechanical keyboards on Amazon'
},
output: {
success: true,
searchProvider: 'amazon',
message: 'Searched for "mechanical keyboard" on Amazon',
query: 'mechanical keyboard',
url: 'https://www.amazon.com/s?k=mechanical+keyboard'
}
},
{
description: 'Search on Google Maps',
input: {
searchProvider: 'google_maps',
query: 'coffee shops near Times Square NYC',
intent: 'Finding coffee shops in Times Square area'
},
output: {
success: true,
searchProvider: 'google_maps',
message: 'Searched for "coffee shops near Times Square NYC" on Google Maps',
query: 'coffee shops near Times Square NYC',
url: 'https://www.google.com/maps/search/coffee+shops+near+Times+Square+NYC'
}
},
{
description: 'Search on Google Finance',
input: {
searchProvider: 'google_finance',
query: 'AAPL',
intent: 'Looking up Apple stock information'
},
output: {
success: true,
searchProvider: 'google_finance',
message: 'Searched for "AAPL" on Google Finance',
query: 'AAPL',
url: 'https://www.google.com/finance/quote/AAPL:NASDAQ'
}
}
],
streamingConfig: {
displayName: 'Search',
icon: '🔍',
progressMessage: 'Performing search...'
}
};
super(config, executionContext);
}
/**
* Override: Generate contextual display message based on search provider
*/
getProgressMessage(args: SearchInput): string {
try {
// Note: args should already be parsed by StreamEventProcessor
const searchProvider = args?.searchProvider;
const query = args?.query;
const intent = args?.intent;
// Use intent if provided, otherwise generate based on provider
if (intent) {
return intent;
}
const providerNames = {
'google': 'Google',
'amazon': 'Amazon',
'google_maps': 'Google Maps',
'google_finance': 'Google Finance'
};
const providerName = providerNames[searchProvider as keyof typeof providerNames] || 'Search';
return query ? `Searching ${providerName} for "${query}"` : `Searching on ${providerName}`;
} catch {
return 'Performing search...';
}
}
/**
* Override: Format result based on search provider
*/
FormatResultForUI(output: SearchOutput): string {
if (!output.success) {
return `${output.message}`;
}
const providerIcons = {
'google': '🔍',
'amazon': '🛒',
'google_maps': '📍',
'google_finance': '📈'
};
const icon = providerIcons[output.searchProvider] || '🔍';
return `${icon} Searched: "${output.query}"`;
}
protected async execute(input: SearchInput): Promise<SearchOutput> {
try {
// Build the search URL based on the provider
const searchUrl = this.buildSearchUrl(input.searchProvider, input.query);
// Get the current page and navigate
const page = await this.executionContext.browserContext.getCurrentPage();
await page.navigateTo(searchUrl);
// Wait a bit for the page to load
await new Promise(resolve => setTimeout(resolve, 1500));
const providerNames = {
'google': 'Google',
'amazon': 'Amazon',
'google_maps': 'Google Maps',
'google_finance': 'Google Finance'
};
const providerName = providerNames[input.searchProvider] || input.searchProvider;
// Get the final URL after any redirects
const finalUrl = page.url();
return {
success: true,
searchProvider: input.searchProvider,
message: `Searched for "${input.query}" on ${providerName}`,
query: input.query,
url: finalUrl
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
success: false,
searchProvider: input.searchProvider,
message: `Search failed: ${errorMessage}`,
query: input.query,
url: ''
};
}
}
/**
* Build the search URL for the given provider and query
*/
private buildSearchUrl(provider: SearchProvider, query: string): string {
const encodedQuery = encodeURIComponent(query);
switch (provider) {
case 'google':
return `https://www.google.com/search?q=${encodedQuery}`;
case 'amazon':
return `https://www.amazon.com/s?k=${encodedQuery}`;
case 'google_maps':
return `https://www.google.com/maps/search/${encodedQuery}`;
case 'google_finance':
// Google Finance has a special format for stock symbols
// Try to detect if it's a stock symbol (all caps, 1-5 letters)
if (/^[A-Z]{1,5}$/.test(query.trim())) {
// Assume NASDAQ for US stocks, but this could be improved
return `https://www.google.com/finance/quote/${query.trim()}:NASDAQ`;
}
// For non-symbol queries, use the search
return `https://www.google.com/search?q=${encodedQuery}+stock+finance`;
default:
// Fallback to Google search
return `https://www.google.com/search?q=${encodedQuery}`;
}
}
}