Mode: Implementation Specification — Instructions for an AI coding agent to rebuild this from scratch. Not a summary or description.
1. Product Goal
Build a multimodal AI Agent monorepo that ships two primary products: (1) Agent TARS — a CLI and Web UI that brings GUI Agent and Vision capabilities into terminal, browser, and product workflows via cutting-edge multimodal LLMs and MCP tool integration; and (2) UI-TARS Desktop — a native Electron desktop application providing GUI automation driven by the UI-TARS Vision-Language Model to control local and remote computers and browsers. Target users are developers and automation engineers who need human-like task completion through natural language instructions. The system must support multiple model providers (OpenAI-compatible, Anthropic, Volcengine, HuggingFace), hybrid browser control (GUI Agent, DOM, or hybrid strategy), and seamless MCP server mounting.
Implement the following core features:
- Support one-click CLI execution with both headful Web UI and headless server modes
- Implement a hybrid browser agent controlling browsers via GUI grounding, DOM extraction, or hybrid strategy
- Build an Event Stream protocol driving context engineering and an Agent UI
- Support mounting arbitrary MCP Servers (stdio, SSE, streamable-HTTP) to extend agent capabilities
- Implement local and remote computer operators (ADB, nut-js, browser-operator, browserbase)
- Create an SDK abstraction layer for GUI agents with pluggable model and operator implementations
- Build a visualizer component for replaying and inspecting agent execution dumps
- Support OpenAI Chat Completions API and Response API for model invocation with sliding image context management
2. Tech Stack & Versions
- Node.js ≥22.x — primary runtime (CLI requires ≥22, monorepo engines ≥20)
- TypeScript ^5.7.2 — language across all packages
- pnpm 9.10.0 — workspace package manager
- Turborepo ^2.4.4 — monorepo build orchestration
- Rslib (
@rslib/core) — library bundling for all internal packages (ESM + CJS + DTS) - Vitest ^3.0.x — unit, integration, and benchmark testing
- Electron — desktop application runtime (UI-TARS Desktop)
- React ^18.x — UI renderer (Visualizer, Agent UI)
- Ant Design ^5.x — component library for Visualizer
- Zod — runtime schema validation across MCP tools, agent actions, and IPC
- OpenAI SDK (
openai) — model provider client (Chat Completions + Responses API) - Puppeteer-core — browser automation engine
- @modelcontextprotocol/sdk — MCP client/server protocol implementation
- ESLint ^8.57.0 + Prettier ^3.3.2 — code quality
- Husky ^9.1.7 + lint-staged ^14.0.1 — git hooks
- Changesets ^2.27.11 — versioning and publishing
- Playwright ^1.49.1 — E2E testing for Electron app
3. Project Setup & Commands
Initialize the monorepo with the following structure and commands:
# Create root workspace
mkdir monorepo && cd monorepo
git init
pnpm init
# Install core dev dependencies
pnpm add -Dw turbo@^2.4.4 typescript@^5.7.2 @types/node@^20.14.8
pnpm add -Dw eslint@^8.57.0 @typescript-eslint/eslint-plugin@^5.0.0 @typescript-eslint/parser@^5.0.0 eslint-plugin-import@^2.25.0 eslint-plugin-react@^7.34.3
pnpm add -Dw prettier@^3.3.2 @trivago/prettier-plugin-sort-imports@^5.2.1
pnpm add -Dw vitest@^3.0.8 @vitest/coverage-v8@^3.0.8
pnpm add -Dw husky@^9.1.7 lint-staged@^14.0.1 @commitlint/cli@^19.6.1 @commitlint/config-conventional@^19.6.0
pnpm add -Dw @changesets/cli@^2.27.11 secretlint@^10.2.1
pnpm add -Dw tsx@^4.19.2 ts-node@^10.9.2 rimraf@^6.0.1 cross-env@^7.0.3 sass-embedded@^1.83.1
pnpm add -Dw @playwright/test@^1.49.1 electron-playwright-helpers@^1.7.1
pnpm add -Dw opencommit@^3.2.5 @electron-toolkit/tsconfig@^1.0.1Define the following package.json scripts at the root:
{
"scripts": {
"bootstrap": "pnpm i",
"dev:ui-tars": "turbo run ui-tars-desktop#dev",
"format": "prettier --write .",
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
"test": "vitest",
"test:bench": "vitest bench",
"coverage": "vitest run --coverage",
"prepare": "husky"
}
}Create pnpm-workspace.yaml:
packages:
- 'apps/*'
- 'apps/agent-tars/src/*'
- 'apps/ui-tars/src/*'
- 'packages/ui-tars/**'
- 'packages/agent-infra/**'
- '!packages/agent-infra/create-new-mcp/template-*'
- 'packages/common/*'Create the following directory structure:
apps/
agent-tars/ # Agent TARS application (CLI + Web UI + Server)
ui-tars/ # UI-TARS Desktop Electron app
packages/
agent-infra/ # Agent infrastructure libraries
browser/ # Browser automation (LocalBrowser, RemoteBrowser)
browser-use/ # Browser agent with DOM extraction
mcp-servers/ # Built-in MCP servers (filesystem, etc.)
mcp-client/ # MCP client implementation
mcp-http-server/ # MCP HTTP server launcher
mcp-shared/ # Shared MCP types
logger/ # Logging infrastructure
search/ # Search providers (Bing, DuckDuckGo, browser-search)
operators/ # Computer operators (adb, nut-js, browser-operator, browserbase)
shared/ # Shared agent-infra utilities
create-new-mcp/ # CLI scaffolding tool for new MCP servers
ui-tars/
sdk/ # GUIAgent SDK (Model, Operator abstractions)
action-parser/ # Parse VLM predictions into actions
visualizer/ # React-based execution dump visualizer
cli/ # UI-TARS CLI
electron-ipc/ # Type-safe Electron IPC
utio/ # Telemetry/analytics
common/
electron-build/ # Electron build utilities
configs/ # Shared build/lint configs4. Environment Variables
VLM_PROVIDER=huggingface # Model provider identifier — required
VLM_BASE_URL=http://endpoint/v1 # OpenAI-compatible base URL — required
VLM_API_KEY=hf_xxx # API key for model provider — required
VLM_MODEL_NAME=your_model_name # Model name to invoke — required
OPENAI_API_KEY=sk-xxx # OpenAI API key (when provider=openai) — optional
ANTHROPIC_API_KEY=sk-ant-xxx # Anthropic API key (when provider=anthropic) — optional
VOLCENGINE_API_KEY=xxx # Volcengine API key (when provider=volcengine) — optional
DEBUG=mcp # Enable debug logging for MCP client — optional
UTIO_ENDPOINT=https://utio.example.com/api # Telemetry endpoint — optional5. Key Dependencies
Model & AI:
openai— OpenAI-compatible client for Chat Completions and Responses APIzod— Schema validation for all tool inputs, agent outputs, IPC contracts
Browser Automation:
puppeteer-core— Headless/headful browser control via CDP@agent-infra/browser(workspace) — LocalBrowser, RemoteBrowser, BrowserFinder abstractions
MCP Protocol:
@modelcontextprotocol/sdk— Client (stdio, SSE, streamable-HTTP transports), Server, InMemory transportuuid— Generate unique tool IDsminimatch— Glob pattern matching for MCP tool filtering
CLI Interaction:
@clack/prompts— Interactive CLI prompts for create-new-mcp scaffoldingmri— CLI argument parsingpicocolors— Terminal color output
Electron Desktop:
electron— Desktop runtime@electron-toolkit/tsconfig— Shared TS configs for Electron
UI/Visualizer:
antd— Component library (Modal, Input, Button, Upload, ConfigProvider)react-resizable-panels— Resizable panel layouts in Visualizer@midscene/core— Execution dump types for Visualizer compatibility
Build Tooling:
@rslib/core— Library build system producing ESM + CJS + DTS bundles
6. Data Models & Schema
MCP Filesystem Server Schemas (packages/agent-infra/mcp-servers/filesystem/src/schema.ts)
import { z } from 'zod';
export const ReadFileArgsSchema = z.object({ path: z.string() });
export const ReadMultipleFilesArgsSchema = z.object({ paths: z.array(z.string()) });
export const WriteFileArgsSchema = z.object({ path: z.string(), content: z.string() });
export const EditOperation = z.object({
oldText: z.string().describe('Text to search for - must match exactly'),
newText: z.string().describe('Text to replace with'),
});
export const EditFileArgsSchema = z.object({
path: z.string(),
edits: z.array(EditOperation),
dryRun: z.boolean().default(false),
});
export const CreateDirectoryArgsSchema = z.object({ path: z.string() });
export const ListDirectoryArgsSchema = z.object({ path: z.string() });
export const DirectoryTreeArgsSchema = z.object({ path: z.string() });
export const MoveFileArgsSchema = z.object({ source: z.string(), destination: z.string() });
export const SearchFilesArgsSchema = z.object({
path: z.string(),
pattern: z.string(),
excludePatterns: z.array(z.string()).optional().default([]),
});
export const GetFileInfoArgsSchema = z.object({ path: z.string() });Browser Agent Action Schemas (packages/agent-infra/browser-use/src/agent/actions/schemas.ts)
import { z } from 'zod';
export interface ActionSchema {
name: string;
description: string;
schema: z.ZodType;
}
export const doneActionSchema: ActionSchema = {
name: 'done', description: 'Complete task',
schema: z.object({ text: z.string() }),
};
export const searchGoogleActionSchema: ActionSchema = {
name: 'search_google', description: 'Search Google in the current tab',
schema: z.object({ query: z.string() }),
};
export const goToUrlActionSchema: ActionSchema = {
name: 'go_to_url', description: 'Navigate to URL in the current tab',
schema: z.object({ url: z.string() }),
};
export const goBackActionSchema: ActionSchema = {
name: 'go_back', description: 'Go back to the previous page',
schema: z.object({}),
};
export const clickElementActionSchema: ActionSchema = {
name: 'click_element', description: 'Click element',
schema: z.object({
desc: z.string().optional(),
index: z.number(),
xpath: z.string().optional(),
}),
};
export const inputTextActionSchema: ActionSchema = {
name: 'input_text', description: 'Input text into an interactive input element',
schema: z.object({
desc: z.string().optional(),
index: z.number(),
text: z.string(),
xpath: z.string().optional(),
}),
};
export const switchTabActionSchema: ActionSchema = {
name: 'switch_tab', description: 'Switch to tab by id',
schema: z.object({ tab_id: z.number() }),
};
export const openTabActionSchema: ActionSchema = {
name: 'open_tab', description: 'Open URL in new tab',
schema: z.object({ url: z.string() }),
};
export const scrollDownActionSchema: ActionSchema = {
name: 'scroll_down',
description: 'Scroll down the page by pixel amount',
schema: z.object({ desc: z.string().optional(), amount: z.number().optional() }),
};
export const scrollUpActionSchema: ActionSchema = {
name: 'scroll_up',
description: 'Scroll up the page by pixel amount',
schema: z.object({ desc: z.string().optional(), amount: z.number().optional() }),
};
export const sendKeysActionSchema: ActionSchema = {
name: 'send_keys',
description: 'Send strings of special keys like Backspace, Enter, Control+o',
schema: z.object({ desc: z.string().optional(), keys: z.string() }),
};
export const scrollToTextActionSchema: ActionSchema = {
name: 'scroll_to_text',
description: 'Scroll to specific text on the page',
schema: z.object({ desc: z.string().optional(), text: z.string() }),
};
export const getDropdownOptionsActionSchema: ActionSchema = {
name: 'get_dropdown_options', description: 'Get all options from a native dropdown',
schema: z.object({ index: z.number() }),
};
export const selectDropdownOptionActionSchema: ActionSchema = {
name: 'select_dropdown_option',
description: 'Select dropdown option for interactive element index',
schema: z.object({ index: z.number(), text: z.string() }),
};Agent Types (packages/agent-infra/browser-use/src/agent/types.ts)
export interface AgentOptions {
maxSteps: number;
maxActionsPerStep: number;
maxFailures: number;
retryDelay: number;
maxInputTokens: number;
maxErrorLength: number;
useVision: boolean;
useVisionForPlanner: boolean;
validateOutput: boolean;
includeAttributes: string[];
planningInterval: number;
}
export const DEFAULT_AGENT_OPTIONS: AgentOptions = {
maxSteps: 100, maxActionsPerStep: 10, maxFailures: 3, retryDelay: 10,
maxInputTokens: 128000, maxErrorLength: 400, useVision: false,
useVisionForPlanner: false, validateOutput: true,
includeAttributes: ['title','type','name','role','tabindex','aria-label','placeholder','value','alt','aria-expanded'],
planningInterval: 3,
};Browser Context Types (packages/agent-infra/browser-use/src/browser/types.ts)
export interface BrowserContextConfig {
minimumWaitPageLoadTime: number; // default: 1
waitForNetworkIdlePageLoadTime: number; // default: 1.0
maximumWaitPageLoadTime: number; // default: 5.0
waitBetweenActions: number; // default: 1.0
browserWindowSize: { width: number; height: number }; // default: 1280x1100
highlightElements: boolean; // default: true
viewportExpansion: number; // default: 500
includeDynamicAttributes: boolean; // default: true
homePageUrl: string; // default: 'https://www.google.com'
}
export interface PageState extends DOMState {
tabId: number;
url: string;
title: string;
screenshot: string | null;
pixelsAbove: number;
pixelsBelow: number;
}
export interface BrowserState extends PageState {
pages?: PageInfo[];
}UI-TARS SDK Core Types (packages/ui-tars/sdk/src/types.ts)
export interface InvokeParams {
conversations: Message[];
images: string[];
screenContext: { width: number; height: number };
scaleFactor?: number;
uiTarsVersion?: UITarsModelVersion;
headers?: Record<string, string>;
previousResponseId?: string;
}
export interface InvokeOutput {
prediction: string;
parsedPredictions: PredictionParsed[];
costTime?: number;
costTokens?: number;
responseId?: string;
}
export interface GUIAgentConfig<TOperator> {
operator: TOperator;
model: InstanceType<typeof UITarsModel> | ConstructorParameters<typeof UITarsModel>[0];
systemPrompt?: string;
signal?: AbortSignal;
onData?: (params: { data: GUIAgentData }) => void;
onError?: (params: { data: GUIAgentData; error: GUIAgentError }) => void;
logger?: Logger;
retry?: { model?: RetryConfig; screenshot?: RetryConfig; execute?: RetryConfig; };
maxLoopCount?: number; // default: 25
loopIntervalInMs?: number; // default: 0
uiTarsVersion?: UITarsModelVersion;
}MCP Client Types
export interface MCPClientOptions {
isDebug?: boolean;
defaultTimeout?: number; // seconds, default: 60
}
export interface MCPTool extends Tool {
id: string;
serverName: string;
}UTIO Event Types (packages/ui-tars/utio/src/types.ts)
export type EventType = 'appLaunched' | 'sendInstruction' | 'shareReport';
export interface AppLaunchedEvent extends BaseEvent<'appLaunched'> {
platform: string;
osVersion: string;
screenWidth: number;
screenHeight: number;
}
export interface SendInstructionEvent extends BaseEvent<'sendInstruction'> {
instruction: string;
}
export interface ShareReportEvent extends BaseEvent<'shareReport'> {
lastScreenshot?: string;
report?: string;
instruction: string;
}Electron IPC Types
export type ZodSchema<TInput> = { parse: (input: any) => TInput };
export type HandleFunction<TInput = any, TResult = any> = (args: {
context: HandleContext;
input: TInput;
}) => Promise<TResult>;
export type HandleContext = { sender: WebContents | null };
export type RouterType = Record<string, { handle: HandleFunction }>;UITarsModelVersion Enum
export enum UITarsModelVersion {
V1_0 = 'V1_0',
V1_5 = 'V1_5',
DOUBAO_1_5_15B = 'DOUBAO_1_5_15B',
DOUBAO_1_5_20B = 'DOUBAO_1_5_20B',
}7. Database & Storage Setup
N/A — This project does not use a traditional database. State is managed in-memory during agent execution, persisted to local files (execution dumps as JSON), and stored in browser localStorage for the Visualizer environment configuration. File-based storage is used for MCP server configurations and execution reports written to ./midscene_run/report/ directories.
8. API Endpoints & Contracts
MCP HTTP Server Endpoints
POST /mcp
Auth: None (configure at deployment)
Request: JSON-RPC 2.0 envelope { jsonrpc: "2.0", method: string, params: object, id: number }
Response: JSON-RPC 2.0 result { jsonrpc: "2.0", result: object, id: number }
Methods: tools/list, tools/call, resources/list, prompts/list, initialize
Transport: Streamable HTTP or SSEAgent TARS Server Endpoints [INFERRED]
POST /api/sessions
Auth: None
Request: { instruction: string, provider: string, model: string, apiKey: string }
Response: { sessionId: string }
Errors: 400 when instruction is empty
GET /api/sessions/:id/events
Auth: None
Response: SSE stream of AgentEvent objects
Events: { actor: string, state: string, details: string, browserState?: BrowserState }
POST /api/sessions/:id/stop
Auth: None
Response: { status: "stopped" }UTIO Telemetry
POST {UTIO_ENDPOINT}
Auth: None
Request: EventPayload<T> — { type: EventType, ...eventSpecificFields }
Response: 200 OK (silent fail on error)9. Authentication & Permissions
N/A for traditional user auth. This system uses API key-based authentication for model providers:
- Accept API keys via CLI flags (
--apiKey), environment variables (VLM_API_KEY,OPENAI_API_KEY, etc.), or runtime configuration UI - Pass keys to the OpenAI client constructor as
apiKeyinClientOptions - Never log or expose API keys — mask values as
***in all UI displays (exceptMIDSCENE_MODEL_NAME) - Store environment configuration in browser
localStoragefor the Visualizer component - For MCP servers using stdio transport, inject environment variables into the child process with enhanced PATH resolution
- For HTTP-based MCP servers, pass custom headers via
requestInit.headers
10. Pages & Routes
Agent TARS Web UI [INFERRED]
/— Main chat/agent interface; user inputs instruction, views streaming agent execution, tool calls, and browser screenshots/settings— Model provider configuration, MCP server management, operator selection/events— Event Stream Viewer for debugging data flow between agent steps
UI-TARS Desktop (Electron Windows)
- Main Window — Agent UI with instruction input, live screenshot preview, action timeline, and status indicators
- Report Window — Visualizer rendering execution dumps with Sidebar, Timeline, DetailPanel, DetailSide, and Player components
Visualizer Routes (Standalone HTML)
/playground.html— Interactive playground for testing agent flows/report.html— Static report viewer loading.web-dump.jsonfiles via drag-and-drop Upload component
11. Component Architecture
Visualizer Component Tree
Visualizer (root)
├── ConfigProvider (antd theme: globalThemeConfig)
├── EnvConfig
│ ├── Button (Edit) → Modal with Input.TextArea for KEY=VALUE pairs
│ └── Tooltip for status indicators
├── PanelGroup (main-page-layout, horizontal)
│ ├── Panel (sidebar, 20%)
│ │ └── Sidebar — list of execution tasks
│ ├── PanelResizeHandle
│ └── Panel (main-right, 80%)
│ ├── Timeline — temporal action sequence
│ └── PanelGroup (page-detail-layout-v2)
│ ├── Panel (detail-panel, 75%)
│ │ └── DetailPanel — screenshots, actions, results
│ ├── PanelResizeHandle
│ └── Panel (detail-side)
│ └── DetailSide — metadata and side info
├── GlobalHoverPreview — floating element preview on hover
└── Player — replay mode for execution animationsEnvConfig Props: None (uses useEnvConfig Zustand store)
EnvConfig State: isModalOpen: boolean, tempConfigString: string, showEditButton: boolean
EnvConfig Behaviors: Load config from textarea, persist to store, mask API key values
Visualizer Props: { logoAction?: () => void; dumps?: EnhancedGroupedActionDump[]; onActiveTaskChange?: (taskIndex, task) => void }
Agent TARS CLI Architecture
@agent-tars/cli
├── CLI Parser (commander/yargs)
│ ├── --provider (openai|anthropic|volcengine|huggingface|...)
│ ├── --model
│ ├── --apiKey
│ └── --headless (flag for server mode)
├── AgentKernel
│ ├── MCPClient (mounts tools from MCP servers)
│ ├── BrowserAgent (browser-use or external operator)
│ ├── ModelProvider (OpenAI-compatible)
│ └── EventStream (protocol-driven event emission)
└── Output Renderer
├── Web UI (Express/Hono server + SSE)
└── Terminal (streaming console output)UI-TARS SDK Architecture
GUIAgent (orchestrator)
├── Model (abstract)
│ └── UITarsModel extends Model
│ ├── invokeModelProvider() — OpenAI Chat Completions or Responses API
│ ├── preprocessResizeImage() — compress to maxPixels by version
│ ├── convertToOpenAIMessages() — map conversations + images
│ └── actionParser() — parse VLM prediction into actions
├── Operator (abstract)
│ ├── screenshot() → ScreenshotOutput
│ └── execute(params: ExecuteParams) → ExecuteOutput
├── Loop Controller
│ ├── maxLoopCount (default: 25)
│ └── loopIntervalInMs (default: 0)
└── Event Emitter (onData, onError callbacks)12. State Management
- Visualizer: Use Zustand for the
useExecutionDumpstore managing:dump,_executionDumpLoadId,replayAllMode,allExecutionAnimation,insightWidth,insightHeight,onActiveTaskChange. Actions:setGroupedDump,setReplayAllMode,reset,setOnActiveTaskChange. - EnvConfig: Use Zustand
useEnvConfigstore managing:config(Record<string, string>),configString(string). Actions:loadConfig(configString). - MCP Client: Internal
Map<string, any>store for server registry;EventEmitterfor status changes;activeServers: Map<ServerNames, {client, server}>for connected clients. - Agent Context: Class-based mutable state in
AgentContextholdingtaskId,browserContext,messageManager,eventManager,paused,stopped,consecutiveFailures,nSteps,actionResults. - UITarsModel: Instance state
headImageContext: { messageIndex: number; responseIds: string[] } | nullfor sliding window image management in Responses API mode. - CLI/Server: Use EventEmitter-based Event Stream for real-time streaming to Web UI via SSE.
13. UI/UX & Design System
- Component Library: Ant Design v5 with
ConfigProviderwrapping all Visualizer components usingglobalThemeConfig - Layout:
react-resizable-panelsfor resizable PanelGroup layouts withautoSaveIdfor persistence - Colors: Status indicators using
iconForStatus('success')(green check) andiconForStatus('failed')(red X); standard antd color palette - Typography: Default antd typography; monospace for config text areas and code display
- Upload UX:
Draggercomponent accepting.web-dump.jsonfiles with FileReader parsing and error messaging viamessage.error() - Responsive: Panel-based responsive layout with resize handles; window resize listener with 300ms throttle
- Animations: Replay mode with
Playercomponent animating execution scripts; panel drag transitions - Styling: LESS files (
index.less) for custom styles;whiteSpace: nowrapwithwordWrap: break-wordfor config text areas - Dark/Light: Configure via antd
ConfigProvidertheme tokens [INFERRED]
14. Integrations & External Services
Model Providers (OpenAI-compatible)
- Install
openaiSDK - Initialize on server/main process:
new OpenAI({ baseURL, apiKey, maxRetries: 0 }) - Implement two invocation paths:
- Chat Completions API:
openai.chat.completions.create({ model, messages, max_tokens, temperature, top_p, stream: false }) - Responses API:
openai.responses.create({ input, model, temperature, top_p, previous_response_id, max_output_tokens })with sliding window image context deletion viaopenai.responses.delete(responseId)
- Chat Completions API:
- Support custom headers passthrough for provider-specific extensions (e.g.,
thinking: { type: 'disabled' })
MCP Protocol
- Install
@modelcontextprotocol/sdk - Client transports: StdioClientTransport, SSEClientTransport, StreamableHTTPClientTransport, InMemoryTransport
- Server: Expose tools via stdio or HTTP using
server.jsonandsmithery.yamlconfiguration - Enhanced PATH: For stdio servers, augment PATH with platform-specific directories (
/opt/homebrew/bin,~/.nvm/current/bin,~/.cargo/bin, etc.)
Browser Automation
- Install
puppeteer-core - Implement
LocalBrowser(launches browser via detected executable path) andRemoteBrowser(connects via CDP WebSocket) - Inject anti-detection scripts: override
navigator.webdriver,window.chrome, shadow DOMattachShadowtoopenmode - Inject
buildDomTree.jsscript viaevaluateOnNewDocumentfor DOM element extraction
Telemetry (UTIO)
- Initialize
new UTIO(endpoint)with POST to endpoint - Send events:
appLaunched(platform, osVersion, screenWidth, screenHeight),sendInstruction(instruction text),shareReport(lastScreenshot, report URL)
15. Core Workflows & Business Logic
GUI Agent Execution Loop
- User provides natural language instruction via CLI flag, Web UI input, or Electron window
GUIAgentinitializes with operator (screenshot + execute) and model (invoke) configurations- Agent enters loop (max 25 iterations by default):
a. Call
operator.screenshot()to capture current screen/browser state b. Preprocess image: resize tomaxPixelsbased onUITarsModelVersion(V1_0:MAX_PIXELS_V1_0, V1_5:MAX_PIXELS_V1_5, Doubao:MAX_PIXELS_DOUBAO) c. Build OpenAI messages from conversation history + compressed images viaconvertToOpenAIMessages()d. Callmodel.invoke()which internally callsinvokeModelProvider()→ OpenAI API e. Parse prediction viaactionParser({ prediction, factor, screenContext, scaleFactor, modelVer })f. EmitGUIAgentDataviaonDatacallback (status, prediction, parsed actions, cost metrics) g. For each parsed prediction, calloperator.execute({ prediction, parsedPrediction, screenWidth, screenHeight, scaleFactor, factors })h. WaitloopIntervalInMsthen repeat - On error, emit via
onErrorcallback withGUIAgentError - On completion (agent returns
doneaction or max loops), emit final status
Browser Agent Step Execution
- Get browser state:
page.getState()→ screenshot + DOM element tree + selector map - Build message with current state, previous actions memory, and user instruction
- Invoke LLM to get
AgentOutputcontainingcurrent_state(page_summary, evaluation, memory, next_goal) andaction[]array - Execute each action in order: click_element, input_text, scroll_down, go_to_url, switch_tab, etc.
- Wait
waitBetweenActions(default 1s) between actions - Collect
ActionResultfor each action (isDone, extractedContent, error) - Emit
AgentEventwith actor, state, details, browserState
MCP Server Activation Flow
- Construct
MCPClientwith array ofMCPServerconfigs (name, url/command, status) - Call
client.init()→ loads all servers with statusactivate - For each server: create
Clientinstance, determine transport type:- URL-based: create StreamableHTTPClientTransport or SSEClientTransport
- Command-based: create StdioClientTransport with enhanced PATH
- Built-in: use InMemoryTransport with paired server/client
- Call
client.connect(transport)and store inactiveServersmap - List tools via
client.listTools(), apply allow/block filters usingminimatch - Expose filtered tools as
MCPTool[]with generatedidandserverName
create-new-mcp Scaffolding Flow
- Parse CLI args with
mri(target dir, template, overwrite) - Prompt for project name via
@clack/promptsif not provided - Handle existing directory (overwrite, cancel, or ignore)
- Validate package name with regex
/^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/ - Copy
template-default/files to target directory - Replace
{{variable_name}}and%%variable_name%%patterns in all files - Output next steps:
cd <dir>,<pkg-manager> install,<pkg-manager> run dev
16. Validation & Error Handling
Zod Schemas
Use the schemas defined in Section 6 for all MCP tool inputs, agent action outputs, and IPC message validation. Every tool call argument must be validated against its corresponding Zod schema before execution.
Error Response Format
{ error: string; code: string; field?: string }Agent Error Handling
- Track
consecutiveFailuresinAgentContext; abort aftermaxFailures(default: 3) - Truncate error messages to
maxErrorLength(default: 400 chars) before feeding back to LLM - Implement retry with configurable
RetryConfig(maxRetries, onRetry callback) for model, screenshot, and execute operations - On VLM response error (empty prediction), construct error with
name: 'vlm response error'andstack: JSON.stringify(result)
Loading States
- Visualizer: Show
<Empty>component when no dump loaded; Dragger upload prompt for file input - EnvConfig: Show “No config” with setup button when
Object.keys(config).length === 0