mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-19 11:31:03 +00:00
* 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>
267 lines
7.5 KiB
TypeScript
267 lines
7.5 KiB
TypeScript
import { MessageType } from '@/lib/types/messaging';
|
|
import { z } from 'zod';
|
|
|
|
/**
|
|
* Port connection names
|
|
*/
|
|
export enum PortName {
|
|
OPTIONS_TO_BACKGROUND = 'options-to-background',
|
|
SIDEPANEL_TO_BACKGROUND = 'sidepanel-to-background'
|
|
}
|
|
|
|
// Create a zod enum for PortName
|
|
export const PortNameSchema = z.nativeEnum(PortName);
|
|
|
|
/**
|
|
* Port message structure
|
|
*/
|
|
export const PortMessageSchema = z.object({
|
|
type: z.nativeEnum(MessageType),
|
|
payload: z.unknown(),
|
|
id: z.string().optional() // Optional message ID for correlation
|
|
});
|
|
|
|
export type PortMessage<T = unknown> = z.infer<typeof PortMessageSchema> & { payload: T };
|
|
|
|
/**
|
|
* Port messaging service for communication between extension components
|
|
*/
|
|
export class PortMessaging {
|
|
private port: chrome.runtime.Port | null = null;
|
|
private listeners: Map<MessageType, Array<(payload: unknown, messageId?: string) => void>> = new Map();
|
|
private connectionListeners: Array<(connected: boolean) => void> = [];
|
|
private connected = false;
|
|
private currentPortName: PortName | null = null;
|
|
private heartbeatInterval: number | null = null;
|
|
private heartbeatIntervalMs = 5000; // Send heartbeat every 5 seconds
|
|
private autoReconnect = false;
|
|
private reconnectTimeoutMs = 1000; // Wait 1 second before reconnecting
|
|
|
|
/**
|
|
* Connects to a port with the specified name
|
|
* @param portName - Name of the port to connect to
|
|
* @param enableAutoReconnect - Whether to automatically reconnect on disconnect
|
|
* @returns true if connection successful
|
|
*/
|
|
public connect(portName: PortName, enableAutoReconnect: boolean = false): boolean {
|
|
try {
|
|
this.currentPortName = portName;
|
|
this.autoReconnect = enableAutoReconnect;
|
|
this.port = chrome.runtime.connect({ name: portName });
|
|
|
|
this.port.onMessage.addListener(this.handleIncomingMessage);
|
|
this.port.onDisconnect.addListener(this.handleDisconnect);
|
|
|
|
this.connected = true;
|
|
this.notifyConnectionListeners(true);
|
|
|
|
// Start heartbeat to keep connection alive
|
|
this.startHeartbeat();
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error(`[PortMessaging] Connection error: ${error instanceof Error ? error.message : String(error)}`);
|
|
this.connected = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Disconnects from the current port
|
|
*/
|
|
public disconnect(): void {
|
|
this.autoReconnect = false; // Disable auto-reconnect for manual disconnect
|
|
this.stopHeartbeat();
|
|
|
|
if (this.port) {
|
|
this.port.disconnect();
|
|
this.port = null;
|
|
this.connected = false;
|
|
this.notifyConnectionListeners(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Starts sending heartbeat messages to keep the port alive
|
|
*/
|
|
private startHeartbeat(): void {
|
|
this.stopHeartbeat(); // Clear any existing heartbeat
|
|
|
|
this.heartbeatInterval = window.setInterval(() => {
|
|
if (this.connected && this.port) {
|
|
try {
|
|
this.sendMessage(MessageType.HEARTBEAT, { timestamp: Date.now() });
|
|
} catch (error) {
|
|
console.warn('[PortMessaging] Heartbeat failed:', error);
|
|
// Don't attempt to reconnect here, let the disconnect handler do it
|
|
}
|
|
}
|
|
}, this.heartbeatIntervalMs);
|
|
}
|
|
|
|
/**
|
|
* Stops the heartbeat timer
|
|
*/
|
|
private stopHeartbeat(): void {
|
|
if (this.heartbeatInterval !== null) {
|
|
clearInterval(this.heartbeatInterval);
|
|
this.heartbeatInterval = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attempts to reconnect to the port
|
|
*/
|
|
private attemptReconnect(): void {
|
|
if (!this.autoReconnect || !this.currentPortName) {
|
|
return;
|
|
}
|
|
|
|
setTimeout(() => {
|
|
if (!this.connected && this.currentPortName) {
|
|
const success = this.connect(this.currentPortName, this.autoReconnect);
|
|
if (!success) {
|
|
this.attemptReconnect(); // Keep trying
|
|
}
|
|
}
|
|
}, this.reconnectTimeoutMs);
|
|
}
|
|
|
|
/**
|
|
* Adds a message listener for a specific message type
|
|
* @param type - Message type to listen for
|
|
* @param callback - Function to call when message is received
|
|
*/
|
|
public addMessageListener<T>(
|
|
type: MessageType,
|
|
callback: (payload: T, messageId?: string) => void
|
|
): void {
|
|
if (!this.listeners.has(type)) {
|
|
this.listeners.set(type, []);
|
|
}
|
|
|
|
const listeners = this.listeners.get(type);
|
|
if (listeners) {
|
|
listeners.push(callback as (payload: unknown, messageId?: string) => void);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes a message listener
|
|
* @param type - Message type
|
|
* @param callback - Callback to remove
|
|
*/
|
|
public removeMessageListener<T>(
|
|
type: MessageType,
|
|
callback: (payload: T, messageId?: string) => void
|
|
): void {
|
|
const typeListeners = this.listeners.get(type);
|
|
|
|
if (typeListeners) {
|
|
const index = typeListeners.indexOf(callback as (payload: unknown, messageId?: string) => void);
|
|
if (index !== -1) {
|
|
typeListeners.splice(index, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a connection state listener
|
|
* @param callback - Function to call on connection state changes
|
|
*/
|
|
public addConnectionListener(callback: (connected: boolean) => void): void {
|
|
this.connectionListeners.push(callback);
|
|
|
|
// Immediately notify with current state
|
|
callback(this.connected);
|
|
}
|
|
|
|
/**
|
|
* Removes a connection state listener
|
|
* @param callback - Callback to remove
|
|
*/
|
|
public removeConnectionListener(callback: (connected: boolean) => void): void {
|
|
const index = this.connectionListeners.indexOf(callback);
|
|
if (index !== -1) {
|
|
this.connectionListeners.splice(index, 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sends a message through the port
|
|
* @param type - Message type
|
|
* @param payload - Message payload
|
|
* @param messageId - Optional message ID for correlation
|
|
* @returns true if message sent successfully
|
|
*/
|
|
public sendMessage<T>(type: MessageType, payload: T, messageId?: string): boolean {
|
|
if (!this.port || !this.connected) {
|
|
console.error('[PortMessaging] Cannot send message: Not connected');
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
const message: PortMessage<T> = {
|
|
type,
|
|
payload,
|
|
id: messageId
|
|
};
|
|
|
|
this.port.postMessage(message);
|
|
return true;
|
|
} catch (error) {
|
|
console.error(`[PortMessaging] Send error: ${error instanceof Error ? error.message : String(error)}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if connected to a port
|
|
* @returns true if connected
|
|
*/
|
|
public isConnected(): boolean {
|
|
return this.connected && this.port !== null;
|
|
}
|
|
|
|
/**
|
|
* Handles incoming messages from the port
|
|
*/
|
|
private handleIncomingMessage = (message: PortMessage): void => {
|
|
const { type, payload, id } = message;
|
|
|
|
// Handle heartbeat acknowledgment
|
|
if (type === MessageType.HEARTBEAT_ACK) {
|
|
// Heartbeat acknowledged, connection is alive
|
|
return;
|
|
}
|
|
|
|
const listeners = this.listeners.get(type);
|
|
|
|
if (listeners && listeners.length > 0) {
|
|
listeners.forEach(listener => listener(payload, id));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handles port disconnection
|
|
*/
|
|
private handleDisconnect = (): void => {
|
|
this.stopHeartbeat();
|
|
this.port = null;
|
|
this.connected = false;
|
|
this.notifyConnectionListeners(false);
|
|
|
|
// Attempt to reconnect if auto-reconnect is enabled
|
|
if (this.autoReconnect) {
|
|
this.attemptReconnect();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Notifies connection listeners of state changes
|
|
*/
|
|
private notifyConnectionListeners(connected: boolean): void {
|
|
this.connectionListeners.forEach(listener => listener(connected));
|
|
}
|
|
}
|