diff --git a/packages/browser-ext/src/lib/browser-message-actions.tsx b/packages/browser-ext/src/lib/browser-message-actions.tsx index accc9fa..b5a7165 100644 --- a/packages/browser-ext/src/lib/browser-message-actions.tsx +++ b/packages/browser-ext/src/lib/browser-message-actions.tsx @@ -21,10 +21,16 @@ import { shareConversation } from "../services/share-conversation"; import { isByokConfigured } from "./ai-provider"; export function BrowserMessageActions({ + message, onRegenerate, onCopy, }: MessageActionsSlotProps) { const { messages, sendMessage } = useChatContext(); + + const textContent = message.parts + .filter((p) => p.type === "text") + .map((p) => (p.type === "text" ? p.text : "")) + .join("\n"); const { user } = useAuth(); const { settings } = useConfigContext(); const [isSharing, setIsSharing] = useState(false); @@ -57,8 +63,8 @@ export function BrowserMessageActions({ return ( - {onCopy && ( - + {onCopy && textContent && ( + onCopy(textContent)} label="Copy"> )} diff --git a/packages/browser-runtime/src/lib/vm/skill-api.ts b/packages/browser-runtime/src/lib/vm/skill-api.ts index 961929b..67d8bc2 100644 --- a/packages/browser-runtime/src/lib/vm/skill-api.ts +++ b/packages/browser-runtime/src/lib/vm/skill-api.ts @@ -4,14 +4,10 @@ * between QuickJS VM and the host environment */ +import type { FileStats } from "./types"; import { zenfs } from "./zenfs-manager"; -export interface FileStats { - isFile: boolean; - isDirectory: boolean; - size: number; - mtime: Date; -} +export type { FileStats }; export interface ToolDefinition { name: string; diff --git a/packages/browser-runtime/src/lib/vm/types.ts b/packages/browser-runtime/src/lib/vm/types.ts new file mode 100644 index 0000000..4069d28 --- /dev/null +++ b/packages/browser-runtime/src/lib/vm/types.ts @@ -0,0 +1,6 @@ +export interface FileStats { + isFile: boolean; + isDirectory: boolean; + size: number; + mtime: Date; +} diff --git a/packages/browser-runtime/src/lib/vm/zenfs-manager.ts b/packages/browser-runtime/src/lib/vm/zenfs-manager.ts index 0948349..25db532 100644 --- a/packages/browser-runtime/src/lib/vm/zenfs-manager.ts +++ b/packages/browser-runtime/src/lib/vm/zenfs-manager.ts @@ -5,7 +5,7 @@ import { configure, fs } from "@zenfs/core"; import { IndexedDB } from "@zenfs/dom"; -import type { FileStats } from "./skill-api"; +import type { FileStats } from "./types"; type FsPromises = { mkdir: (...args: any[]) => Promise; diff --git a/packages/browser-runtime/src/runtime/browser-automation-host.ts b/packages/browser-runtime/src/runtime/browser-automation-host.ts index 033de3e..b71f952 100644 --- a/packages/browser-runtime/src/runtime/browser-automation-host.ts +++ b/packages/browser-runtime/src/runtime/browser-automation-host.ts @@ -1,30 +1,17 @@ import type { ContextProvider } from "@aipexstudio/aipex-core"; -import type { RuntimeAddon } from "./runtime-addon.js"; -import type { RuntimeBroadcastMessage } from "./types.js"; +import type { + AutomationTarget, + RuntimeAddon, + RuntimeBroadcastMessage, + SnapshotCaptureOptions, + SnapshotResult, +} from "./types.js"; -export interface AutomationTarget { - tabId: number; - frameId?: number; - windowId?: number; -} - -export interface SnapshotCaptureOptions { - includeDom?: boolean; - includeScreenshot?: boolean; - includeContext?: boolean; - reason?: string; - tabId?: number; -} - -export interface SnapshotResult { - id: string; - capturedAt: number; - screenshot?: string; - dom?: string; - title?: string; - url?: string; - metadata?: Record; -} +export type { + AutomationTarget, + SnapshotCaptureOptions, + SnapshotResult, +} from "./types.js"; export interface CaptureSessionOptions { target: AutomationTarget; diff --git a/packages/browser-runtime/src/runtime/runtime-addon.ts b/packages/browser-runtime/src/runtime/runtime-addon.ts index 08989b1..a7fad55 100644 --- a/packages/browser-runtime/src/runtime/runtime-addon.ts +++ b/packages/browser-runtime/src/runtime/runtime-addon.ts @@ -1,22 +1,4 @@ -import type { - AutomationTarget, - SnapshotCaptureOptions, - SnapshotResult, -} from "./browser-automation-host.js"; -import type { RuntimeBroadcastMessage } from "./types.js"; - -export interface SnapshotHookContext { - target: AutomationTarget; - options?: SnapshotCaptureOptions; -} - -export interface RuntimeAddon { - id: string; - initialize?(): Promise | void; - onMessage?(message: RuntimeBroadcastMessage): Promise | void; - onBeforeSnapshot?(ctx: SnapshotHookContext): Promise | void; - onAfterSnapshot?( - result: SnapshotResult, - ctx: SnapshotHookContext, - ): Promise | void; -} +export type { + RuntimeAddon, + SnapshotHookContext, +} from "./types.js"; diff --git a/packages/browser-runtime/src/runtime/types.ts b/packages/browser-runtime/src/runtime/types.ts index dfd8fbb..c037e4c 100644 --- a/packages/browser-runtime/src/runtime/types.ts +++ b/packages/browser-runtime/src/runtime/types.ts @@ -8,3 +8,43 @@ export interface RuntimeBroadcastMessage { export interface RuntimeAddonCleanup { dispose(): Promise | void; } + +export interface AutomationTarget { + tabId: number; + frameId?: number; + windowId?: number; +} + +export interface SnapshotCaptureOptions { + includeDom?: boolean; + includeScreenshot?: boolean; + includeContext?: boolean; + reason?: string; + tabId?: number; +} + +export interface SnapshotResult { + id: string; + capturedAt: number; + screenshot?: string; + dom?: string; + title?: string; + url?: string; + metadata?: Record; +} + +export interface SnapshotHookContext { + target: AutomationTarget; + options?: SnapshotCaptureOptions; +} + +export interface RuntimeAddon { + id: string; + initialize?(): Promise | void; + onMessage?(message: RuntimeBroadcastMessage): Promise | void; + onBeforeSnapshot?(ctx: SnapshotHookContext): Promise | void; + onAfterSnapshot?( + result: SnapshotResult, + ctx: SnapshotHookContext, + ): Promise | void; +} diff --git a/packages/browser-runtime/src/tools/index.ts b/packages/browser-runtime/src/tools/index.ts index a541de0..3901998 100644 --- a/packages/browser-runtime/src/tools/index.ts +++ b/packages/browser-runtime/src/tools/index.ts @@ -128,47 +128,8 @@ export function registerDefaultBrowserTools( return registry; } -/** - * Get the currently active tab - * @throws Error if no active tab is found - */ -export async function getActiveTab(): Promise { - const [tab] = await chrome.tabs.query({ - active: true, - currentWindow: true, - }); - - if (!tab?.id) { - throw new Error("No active tab found"); - } - - return tab; -} - -/** - * Execute a script in a specific tab - */ -export async function executeScriptInTab( - tabId: number, - func: (...args: Args) => T, - args: Args, -): Promise { - const results = await chrome.scripting.executeScript({ - target: { tabId }, - func, - args, - }); - - return results[0]?.result as T; -} - -/** - * Execute a script in the active tab - */ -export async function executeScriptInActiveTab( - func: (...args: Args) => T, - args: Args, -): Promise { - const tab = await getActiveTab(); - return await executeScriptInTab(tab.id!, func, args); -} +export { + executeScriptInActiveTab, + executeScriptInTab, + getActiveTab, +} from "./tab-utils"; diff --git a/packages/browser-runtime/src/tools/page.ts b/packages/browser-runtime/src/tools/page.ts index f8389ce..7a9f49a 100644 --- a/packages/browser-runtime/src/tools/page.ts +++ b/packages/browser-runtime/src/tools/page.ts @@ -1,6 +1,6 @@ import { tool } from "@aipexstudio/aipex-core"; import { z } from "zod"; -import { getActiveTab } from "./index"; +import { getActiveTab } from "./tab-utils"; /** * Get page metadata including title, description, keywords, etc. diff --git a/packages/browser-runtime/src/tools/screenshot.ts b/packages/browser-runtime/src/tools/screenshot.ts index a6d54e1..b009b93 100644 --- a/packages/browser-runtime/src/tools/screenshot.ts +++ b/packages/browser-runtime/src/tools/screenshot.ts @@ -3,11 +3,11 @@ import { z } from "zod"; import { cacheScreenshotMetadata } from "../automation/computer"; import { RuntimeScreenshotStorage } from "../lib/screenshot-storage"; import { getAutomationMode } from "../runtime/automation-mode"; -import { getActiveTab } from "./index"; import { captureVisibleTabWithElementCrop, MAX_PADDING, } from "./screenshot-helpers.js"; +import { getActiveTab } from "./tab-utils"; // Re-export the shared helper types/function so existing consumers aren't broken export type { diff --git a/packages/browser-runtime/src/tools/snapshot.ts b/packages/browser-runtime/src/tools/snapshot.ts index fc67b0b..fdc3494 100644 --- a/packages/browser-runtime/src/tools/snapshot.ts +++ b/packages/browser-runtime/src/tools/snapshot.ts @@ -1,7 +1,7 @@ import { tool } from "@aipexstudio/aipex-core"; import { z } from "zod"; import * as snapshotProvider from "../automation/snapshot-provider"; -import { getActiveTab } from "./index"; +import { getActiveTab } from "./tab-utils"; export const takeSnapshotTool = tool({ name: "take_snapshot", diff --git a/packages/browser-runtime/src/tools/tab-utils.ts b/packages/browser-runtime/src/tools/tab-utils.ts new file mode 100644 index 0000000..5679dcd --- /dev/null +++ b/packages/browser-runtime/src/tools/tab-utils.ts @@ -0,0 +1,44 @@ +/** + * Get the currently active tab + * @throws Error if no active tab is found + */ +export async function getActiveTab(): Promise { + const [tab] = await chrome.tabs.query({ + active: true, + currentWindow: true, + }); + + if (!tab?.id) { + throw new Error("No active tab found"); + } + + return tab; +} + +/** + * Execute a script in a specific tab + */ +export async function executeScriptInTab( + tabId: number, + func: (...args: Args) => T, + args: Args, +): Promise { + const results = await chrome.scripting.executeScript({ + target: { tabId }, + func, + args, + }); + + return results[0]?.result as T; +} + +/** + * Execute a script in the active tab + */ +export async function executeScriptInActiveTab( + func: (...args: Args) => T, + args: Args, +): Promise { + const tab = await getActiveTab(); + return await executeScriptInTab(tab.id!, func, args); +} diff --git a/packages/browser-runtime/src/tools/tab.ts b/packages/browser-runtime/src/tools/tab.ts index 42db061..8431b98 100644 --- a/packages/browser-runtime/src/tools/tab.ts +++ b/packages/browser-runtime/src/tools/tab.ts @@ -1,7 +1,7 @@ import { tool } from "@aipexstudio/aipex-core"; import { z } from "zod"; import { getAutomationMode } from "../runtime/automation-mode"; -import { getActiveTab } from "./index"; +import { getActiveTab } from "./tab-utils"; /** * Get all open tabs across all windows diff --git a/packages/core/src/config/ai-providers.ts b/packages/core/src/config/ai-providers.ts index 3a4172a..b064948 100644 --- a/packages/core/src/config/ai-providers.ts +++ b/packages/core/src/config/ai-providers.ts @@ -1,4 +1,4 @@ -import type { ProviderType } from "./settings.js"; +import type { ProviderType } from "./types.js"; export interface AIProviderConfig { name: string; diff --git a/packages/core/src/config/settings.ts b/packages/core/src/config/settings.ts index 83cfa52..7790ad7 100644 --- a/packages/core/src/config/settings.ts +++ b/packages/core/src/config/settings.ts @@ -1,6 +1,7 @@ import type { AIProviderKey } from "./ai-providers.js"; +import type { ProviderType } from "./types.js"; -export type ProviderType = "google" | "openai" | "claude"; +export type { ProviderType }; export interface CustomModelConfig { id: string; diff --git a/packages/core/src/config/types.ts b/packages/core/src/config/types.ts new file mode 100644 index 0000000..f2158de --- /dev/null +++ b/packages/core/src/config/types.ts @@ -0,0 +1 @@ +export type ProviderType = "google" | "openai" | "claude"; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 8bde618..525a9f9 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -4,10 +4,7 @@ import type { Agent as OpenAIAgent, } from "@openai/agents"; import type { AiSdkModel } from "@openai/agents-extensions"; -import type { AIPex } from "./agent/aipex.js"; import type { Context, ContextManager } from "./context/index.js"; -import type { ConversationManager } from "./conversation/manager.js"; -import type { Session } from "./conversation/session.js"; import type { AgentError } from "./utils/errors.js"; // Re-export types from @openai/agents for convenient access @@ -73,7 +70,7 @@ export interface AIPexOptions< * Fully custom ConversationManager instance. * When provided, storage and compression options are ignored. */ - conversationManager?: ConversationManager; + conversationManager?: import("./conversation/manager.js").ConversationManager; /** * Context manager for providing additional context to the agent. @@ -161,7 +158,7 @@ export interface MetricsPayload { } export interface AgentPluginContext { - agent: AIPex; + agent: import("./agent/aipex.js").AIPex; } export interface AgentPluginHooks { @@ -240,8 +237,8 @@ export interface SessionTree { } export interface SessionStorageAdapter { - save(session: Session): Promise; - load(id: string): Promise; + save(session: import("./conversation/session.js").Session): Promise; + load(id: string): Promise; delete(id: string): Promise; listAll(): Promise; getSessionTree(rootId?: string): Promise;