/** * Message Protocol for PageAgentExt * * MV3 Compliant Architecture: * - SidePanel hosts the agent, all state lives there * - Background (SW) is a stateless message relay * - Content Script runs PageController * * Message flows: * 1. RPC: SidePanel → SW → ContentScript → sendResponse (PageController calls) * 2. Query: ContentScript → SW → SidePanel → SW → ContentScript (mask state check) * 3. Events: SW → SidePanel (tab events from chrome.tabs API) */ // ============================================================================ // Shared Types // ============================================================================ /** Action result from PageController operations */ export interface ActionResult { success: boolean message: string } /** Browser state for LLM consumption */ export interface BrowserState { url: string title: string header: string content: string footer: string } /** Scroll options */ export interface ScrollOptions { down: boolean numPages: number pixels?: number index?: number } /** Horizontal scroll options */ export interface ScrollHorizontallyOptions { right: boolean pixels: number index?: number } // ============================================================================ // Message Types // ============================================================================ /** Message type identifier */ type MessageType = | 'rpc:call' // SidePanel → SW: RPC call to content script (response via sendResponse) | 'cs:rpc' // SW → ContentScript: Forwarded RPC call | 'cs:query' // ContentScript → SW: Query to sidepanel | 'query:response' // SW → ContentScript: Query response | 'tab:event' // SW → SidePanel: Tab event notification /** Base message structure */ interface BaseMessage { type: MessageType id: string // Unique message ID for request-response matching } // ============================================================================ // RPC Messages (SidePanel ↔ SW ↔ ContentScript) // ============================================================================ /** SidePanel → SW: Request to call PageController method */ export interface RPCCallMessage extends BaseMessage { type: 'rpc:call' tabId: number method: string args: unknown[] } /** SW → ContentScript: Forwarded RPC call */ export interface CSRPCMessage extends BaseMessage { type: 'cs:rpc' method: string args: unknown[] } // ============================================================================ // Query Messages (ContentScript → SW → SidePanel) // ============================================================================ /** Query types that content script can ask */ export type QueryType = 'shouldShowMask' /** ContentScript → SW: Query to sidepanel */ export interface CSQueryMessage extends BaseMessage { type: 'cs:query' queryType: QueryType tabId: number } /** SW → ContentScript: Query response */ export interface QueryResponseMessage extends BaseMessage { type: 'query:response' result: unknown } // ============================================================================ // Tab Event Messages (SW → SidePanel) // ============================================================================ /** Tab event types */ export type TabEventType = 'removed' | 'updated' | 'activated' | 'windowFocusChanged' /** SW → SidePanel: Tab event notification */ export interface TabEventMessage extends BaseMessage { type: 'tab:event' eventType: TabEventType tabId: number data?: { // For 'updated' events status?: string url?: string // For 'activated' events windowId?: number // For 'windowFocusChanged' events focused?: boolean } } // ============================================================================ // Union Types // ============================================================================ /** All message types */ export type ExtensionMessage = | RPCCallMessage | CSRPCMessage | CSQueryMessage | QueryResponseMessage | TabEventMessage // ============================================================================ // Utility Functions // ============================================================================ /** Generate unique message ID */ export function generateMessageId(): string { return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}` } /** Known message types for type guard */ const MESSAGE_TYPES = new Set([ 'rpc:call', 'cs:rpc', 'cs:query', 'query:response', 'tab:event', ]) /** Type guard - checks if message has a known type */ export function isExtensionMessage(msg: unknown): msg is ExtensionMessage { return typeof msg === 'object' && msg !== null && MESSAGE_TYPES.has((msg as any).type) }