Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 27e9e78636 | |||
| a0c979602e | |||
| 3dc9edb8b5 | |||
| 32b68ab7c8 | |||
| 75a61c05c1 | |||
| bdfa98358b | |||
| dc8b38ccb9 | |||
| e1c288aaeb | |||
| 7a97de2a37 | |||
| 19b03b83ea | |||
| 598b144d06 | |||
| 1cd8a8de60 | |||
| 9beffca985 | |||
| 53db2069ce | |||
| 121104e13d | |||
| 0dacbda9da |
@@ -13,7 +13,7 @@ The GUI Agent Living in Your Webpage. Control web interfaces with natural langua
|
||||
|
||||
👉 <a href="https://alibaba.github.io/page-agent/" target="_blank"><b>🚀 Demo</b></a> | <a href="https://alibaba.github.io/page-agent/docs/introduction/overview" target="_blank"><b>📖 Documentation</b></a>
|
||||
|
||||
<video id="demo-video" src="https://github.com/user-attachments/assets/11aed429-b69c-49d5-8982-fa99b4a0e9a8" controls crossorigin muted></video>
|
||||
<video id="demo-video" src="https://github.com/user-attachments/assets/a1f2eae2-13fb-4aae-98cf-a3fc1620a6c2" controls crossorigin muted></video>
|
||||
|
||||
---
|
||||
|
||||
@@ -49,8 +49,8 @@ Fastest way to try PageAgent with our free Demo LLM:
|
||||
|
||||
| Mirrors | URL |
|
||||
| ------- | ---------------------------------------------------------------------------------- |
|
||||
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.4.4/dist/iife/page-agent.demo.js |
|
||||
| China | https://registry.npmmirror.com/page-agent/1.4.4/files/dist/iife/page-agent.demo.js |
|
||||
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.5.2/dist/iife/page-agent.demo.js |
|
||||
| China | https://registry.npmmirror.com/page-agent/1.5.2/files/dist/iife/page-agent.demo.js |
|
||||
|
||||
> **⚠️ For technical evaluation only.** This demo CDN uses our free [testing LLM API](https://alibaba.github.io/page-agent/docs/features/models#free-testing-api). By using it, you agree to its [terms](https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md).
|
||||
|
||||
|
||||
@@ -5,6 +5,37 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.5.1] - 2026-03-05
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- **`data-browser-use-ignore` → `data-page-agent-ignore`** - DOM ignore attribute renamed to match the project identity
|
||||
- **Config types restructured** - `PageAgentConfig` split into `AgentConfig` + `PageAgentCoreConfig`; config definitions moved from `config/index.ts` to `types.ts`
|
||||
- **Zod v3/v4 dual support** - Libraries now accept both `zod@^3.25` and `zod@^4.0` as peer dependencies
|
||||
|
||||
### Features
|
||||
|
||||
- **Experimental `llms.txt` support** - Agent can fetch and include a site's `llms.txt` in context. Enable via `experimentalLlmsTxt: true`
|
||||
|
||||
### Improvements
|
||||
|
||||
- Default `maxSteps` changed from 20 to 40 for better for complex tasks out of the box
|
||||
- Added 400ms wait between agent steps for page reactions
|
||||
- Increased click wait time (100ms → 200ms) for more reliable interactions
|
||||
- Removed debug `console.log` statements from scroll actions
|
||||
- Reset observations on new task start
|
||||
- Improved logging across packages
|
||||
|
||||
### Extension v0.1.9
|
||||
|
||||
> PageAgent 1.5.1
|
||||
|
||||
- **Advanced config panel** - New collapsible section exposing Max Steps, System Instruction, and experimental `llms.txt` toggle
|
||||
- Streamlined User Auth Token description
|
||||
- Moved testing API notice below auth token section
|
||||
|
||||
---
|
||||
|
||||
## [1.4.0] - 2026-02-27
|
||||
|
||||
### Features
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@
|
||||
|
||||
👉 <a href="https://alibaba.github.io/page-agent/" target="_blank"><b>🚀 Demo</b></a> | <a href="https://alibaba.github.io/page-agent/docs/introduction/overview" target="_blank"><b>📖 Documentation</b></a>
|
||||
|
||||
<video id="demo-video" src="https://github.com/user-attachments/assets/11aed429-b69c-49d5-8982-fa99b4a0e9a8" controls crossorigin muted></video>
|
||||
<video id="demo-video" src="https://github.com/user-attachments/assets/a1f2eae2-13fb-4aae-98cf-a3fc1620a6c2" controls crossorigin muted></video>
|
||||
|
||||
---
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
|
||||
| Mirrors | URL |
|
||||
| ------- | ---------------------------------------------------------------------------------- |
|
||||
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.4.4/dist/iife/page-agent.demo.js |
|
||||
| China | https://registry.npmmirror.com/page-agent/1.4.4/files/dist/iife/page-agent.demo.js |
|
||||
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.5.2/dist/iife/page-agent.demo.js |
|
||||
| China | https://registry.npmmirror.com/page-agent/1.5.2/files/dist/iife/page-agent.demo.js |
|
||||
|
||||
> **⚠️ 仅用于技术评估。** 该 Demo CDN 使用了免费的[测试 LLM API](https://alibaba.github.io/page-agent/docs/features/models#free-testing-api),使用即表示您同意其[条款](https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md)。
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ This free testing API is provided **strictly for technical evaluation and R&D pu
|
||||
|
||||
**Recommendation for Real Usage**: For secure and continuous usage, we strongly advise using the default BYOK mode with your own legally compliant commercial LLM API keys, or connecting to local, offline models (e.g., Ollama).
|
||||
|
||||
**Note**: This service processes data via servers located in Mainland China. If you are located in a region with strict data localization laws (such as the EU/EEA), please do not use this demo.
|
||||
**Note**: This testing LLM api processes data via servers located in Mainland China. If you are located in a region with strict data localization laws (such as the EU/EEA), please do not use this demo.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Generated
+41
-24
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "root",
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "root",
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"packages/page-controller",
|
||||
@@ -11113,27 +11113,31 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@page-agent/core",
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@page-agent/llms": "1.4.4",
|
||||
"@page-agent/page-controller": "1.4.4",
|
||||
"chalk": "^5.6.2",
|
||||
"@page-agent/llms": "1.5.2",
|
||||
"@page-agent/page-controller": "1.5.2",
|
||||
"chalk": "^5.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"zod": "^4.3.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"packages/extension": {
|
||||
"name": "@page-agent/ext",
|
||||
"version": "0.1.8",
|
||||
"version": "0.1.11",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@page-agent/core": "1.4.4",
|
||||
"@page-agent/llms": "1.4.4",
|
||||
"@page-agent/page-controller": "1.4.4",
|
||||
"@page-agent/ui": "1.4.4",
|
||||
"@page-agent/core": "1.5.2",
|
||||
"@page-agent/llms": "1.5.2",
|
||||
"@page-agent/page-controller": "1.5.2",
|
||||
"@page-agent/ui": "1.5.2",
|
||||
"ai-motion": "^0.4.8",
|
||||
"chalk": "^5.6.2",
|
||||
"zod": "^4.3.5"
|
||||
"chalk": "^5.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@radix-ui/react-hover-card": "^1.1.15",
|
||||
@@ -11162,32 +11166,45 @@
|
||||
"tailwindcss": "^4.1.14",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"wxt": "^0.20.18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"packages/llms": {
|
||||
"name": "@page-agent/llms",
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^5.6.2",
|
||||
"chalk": "^5.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"zod": "^4.3.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"packages/page-agent": {
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@page-agent/core": "1.4.4",
|
||||
"@page-agent/llms": "1.4.4",
|
||||
"@page-agent/page-controller": "1.4.4",
|
||||
"@page-agent/ui": "1.4.4",
|
||||
"chalk": "^5.6.2",
|
||||
"@page-agent/core": "1.5.2",
|
||||
"@page-agent/llms": "1.5.2",
|
||||
"@page-agent/page-controller": "1.5.2",
|
||||
"@page-agent/ui": "1.5.2",
|
||||
"chalk": "^5.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"zod": "^4.3.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"packages/page-controller": {
|
||||
"name": "@page-agent/page-controller",
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ai-motion": "^0.4.8"
|
||||
@@ -11195,12 +11212,12 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@page-agent/ui",
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"license": "MIT"
|
||||
},
|
||||
"packages/website": {
|
||||
"name": "@page-agent/website",
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"devDependencies": {
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@radix-ui/react-separator": "^1.1.8",
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "root",
|
||||
"private": true,
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
"packages/page-controller",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@page-agent/core",
|
||||
"private": false,
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"type": "module",
|
||||
"main": "./dist/esm/page-agent-core.js",
|
||||
"module": "./dist/esm/page-agent-core.js",
|
||||
@@ -44,8 +44,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^5.6.2",
|
||||
"zod": "^4.3.5",
|
||||
"@page-agent/llms": "1.4.4",
|
||||
"@page-agent/page-controller": "1.4.4"
|
||||
"@page-agent/llms": "1.5.2",
|
||||
"@page-agent/page-controller": "1.5.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"zod": "^4.3.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { InvokeError, LLM, type Tool } from '@page-agent/llms'
|
||||
import type { BrowserState, PageController } from '@page-agent/page-controller'
|
||||
import chalk from 'chalk'
|
||||
import * as z from 'zod'
|
||||
import * as z from 'zod/v4'
|
||||
|
||||
import SYSTEM_PROMPT from './prompts/system_prompt.md?raw'
|
||||
import { tools } from './tools'
|
||||
@@ -96,7 +96,7 @@ export class PageAgentCore extends EventTarget {
|
||||
constructor(config: PageAgentCoreConfig) {
|
||||
super()
|
||||
|
||||
this.config = { ...config, maxSteps: config.maxSteps || 20 }
|
||||
this.config = { ...config, maxSteps: config.maxSteps ?? 40 }
|
||||
|
||||
this.#llm = new LLM(this.config)
|
||||
this.tools = new Map(tools)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Internal tools for PageAgent.
|
||||
* @note Adapted from browser-use
|
||||
*/
|
||||
import * as z from 'zod'
|
||||
import * as z from 'zod/v4'
|
||||
|
||||
import type { PageAgentCore } from '../PageAgentCore'
|
||||
import { waitFor } from '../utils'
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface AgentConfig extends LLMConfig {
|
||||
|
||||
/**
|
||||
* Maximum number of steps the agent can take per task.
|
||||
* @default 20
|
||||
* @default 40
|
||||
*/
|
||||
maxSteps?: number
|
||||
|
||||
@@ -24,13 +24,14 @@ export interface AgentConfig extends LLMConfig {
|
||||
*
|
||||
* @example
|
||||
* // override internal tool
|
||||
* import { z } from 'zod/v4'
|
||||
* import { tool } from 'page-agent'
|
||||
* const customTools = {
|
||||
* ask_user: tool({
|
||||
* description:
|
||||
* 'Ask the user or parent model a question and wait for their answer. Use this if you need more information or clarification.',
|
||||
* inputSchema: zod.object({
|
||||
* question: zod.string(),
|
||||
* inputSchema: z.object({
|
||||
* question: z.string(),
|
||||
* }),
|
||||
* execute: async function (this: PageAgent, input) {
|
||||
* const answer = await do_some_thing(input.question)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { InvokeError, InvokeErrorType } from '@page-agent/llms'
|
||||
import chalk from 'chalk'
|
||||
import * as z from 'zod'
|
||||
import * as z from 'zod/v4'
|
||||
|
||||
import type { PageAgentTool } from '../tools'
|
||||
|
||||
const log = console.log.bind(console, chalk.yellow('[autoFixer]'))
|
||||
|
||||
/**
|
||||
* Normalize LLM response and fix common format issues.
|
||||
*
|
||||
@@ -34,7 +36,7 @@ export function normalizeResponse(response: any, tools?: Map<string, PageAgentTo
|
||||
|
||||
// case: sometimes the model only returns the action level
|
||||
if (toolCall.function.name && toolCall.function.name !== 'AgentOutput') {
|
||||
console.log(chalk.yellow(`[normalizeResponse] #1: fixing tool_call`))
|
||||
log(`#1: fixing tool_call`)
|
||||
resolvedArguments = { action: safeJsonParse(resolvedArguments) }
|
||||
}
|
||||
} else {
|
||||
@@ -47,13 +49,13 @@ export function normalizeResponse(response: any, tools?: Map<string, PageAgentTo
|
||||
|
||||
// case: sometimes the content json includes upper level wrapper
|
||||
if (resolvedArguments?.name === 'AgentOutput') {
|
||||
console.log(chalk.yellow(`[normalizeResponse] #2: fixing tool_call`))
|
||||
log(`#2: fixing tool_call`)
|
||||
resolvedArguments = safeJsonParse(resolvedArguments.arguments)
|
||||
}
|
||||
|
||||
// case: sometimes even 2-levels of wrapping
|
||||
if (resolvedArguments?.type === 'function') {
|
||||
console.log(chalk.yellow(`[normalizeResponse] #3: fixing tool_call`))
|
||||
log(`#3: fixing tool_call`)
|
||||
resolvedArguments = safeJsonParse(resolvedArguments.function.arguments)
|
||||
}
|
||||
|
||||
@@ -66,7 +68,7 @@ export function normalizeResponse(response: any, tools?: Map<string, PageAgentTo
|
||||
!resolvedArguments?.next_goal &&
|
||||
!resolvedArguments?.thinking
|
||||
) {
|
||||
console.log(chalk.yellow(`[normalizeResponse] #4: fixing tool_call`))
|
||||
log(`#4: fixing tool_call`)
|
||||
resolvedArguments = { action: safeJsonParse(resolvedArguments) }
|
||||
}
|
||||
} else {
|
||||
@@ -90,7 +92,7 @@ export function normalizeResponse(response: any, tools?: Map<string, PageAgentTo
|
||||
|
||||
// fix incomplete formats
|
||||
if (!resolvedArguments.action) {
|
||||
console.log(chalk.yellow(`[normalizeResponse] #5: fixing tool_call`))
|
||||
log(`#5: fixing tool_call`)
|
||||
resolvedArguments.action = { name: 'wait', input: { seconds: 1 } }
|
||||
}
|
||||
|
||||
@@ -149,9 +151,7 @@ function validateAction(action: any, tools: Map<string, PageAgentTool>): any {
|
||||
(k) => !(schema.shape as Record<string, z.ZodType>)[k].safeParse(undefined).success
|
||||
)
|
||||
if (requiredKey) {
|
||||
console.log(
|
||||
chalk.yellow(`[normalizeResponse] coercing primitive action input for "${toolName}"`)
|
||||
)
|
||||
log(`coercing primitive action input for "${toolName}"`)
|
||||
value = { [requiredKey]: value }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import chalk from 'chalk'
|
||||
|
||||
export { normalizeResponse } from './autoFixer'
|
||||
export * from './autoFixer'
|
||||
|
||||
export async function waitFor(seconds: number): Promise<void> {
|
||||
await new Promise((resolve) => setTimeout(resolve, seconds * 1000))
|
||||
@@ -72,11 +72,15 @@ export async function fetchLlmsTxt(url: string): Promise<string | null> {
|
||||
if (res.ok) {
|
||||
result = await res.text()
|
||||
console.log(chalk.green(`[llms.txt] Found (${result.length} chars)`))
|
||||
if (result.length > 1000) {
|
||||
console.log(chalk.yellow(`[llms.txt] Truncating to 1000 chars`))
|
||||
result = truncate(result, 1000)
|
||||
}
|
||||
} else {
|
||||
console.log(chalk.gray(`[llms.txt] ${res.status} for ${endpoint}`))
|
||||
console.debug(chalk.gray(`[llms.txt] ${res.status} for ${endpoint}`))
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(chalk.gray(`[llms.txt] Failed for ${endpoint}`), e)
|
||||
console.debug(chalk.gray(`[llms.txt] not found for ${endpoint}`), e)
|
||||
}
|
||||
llmsTxtCache.set(origin, result)
|
||||
return result
|
||||
|
||||
@@ -30,6 +30,7 @@ export default defineConfig({
|
||||
external: [
|
||||
'chalk',
|
||||
'zod',
|
||||
'zod/v4',
|
||||
// all the internal packages
|
||||
/^@page-agent\//,
|
||||
],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@page-agent/ext",
|
||||
"private": true,
|
||||
"version": "0.1.8",
|
||||
"version": "0.1.11",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "wxt",
|
||||
@@ -38,12 +38,14 @@
|
||||
"wxt": "^0.20.18"
|
||||
},
|
||||
"dependencies": {
|
||||
"@page-agent/core": "1.4.4",
|
||||
"@page-agent/llms": "1.4.4",
|
||||
"@page-agent/page-controller": "1.4.4",
|
||||
"@page-agent/ui": "1.4.4",
|
||||
"@page-agent/core": "1.5.2",
|
||||
"@page-agent/llms": "1.5.2",
|
||||
"@page-agent/page-controller": "1.5.2",
|
||||
"@page-agent/ui": "1.5.2",
|
||||
"ai-motion": "^0.4.8",
|
||||
"chalk": "^5.6.2",
|
||||
"zod": "^4.3.5"
|
||||
"chalk": "^5.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type PageAgentConfig, PageAgentCore } from '@page-agent/core'
|
||||
import { type AgentConfig, PageAgentCore } from '@page-agent/core'
|
||||
|
||||
import { RemotePageController } from './RemotePageController'
|
||||
import { TabsController } from './TabsController'
|
||||
@@ -17,7 +17,7 @@ function detectLanguage(): 'en-US' | 'zh-CN' {
|
||||
* - can be used from a side panel or a content script
|
||||
*/
|
||||
export class MultiPageAgent extends PageAgentCore {
|
||||
constructor(config: Omit<PageAgentConfig, 'pageController'> & { includeInitialTab?: boolean }) {
|
||||
constructor(config: AgentConfig & { includeInitialTab?: boolean }) {
|
||||
// multi page controller
|
||||
const tabsController = new TabsController()
|
||||
const pageController = new RemotePageController(tabsController)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* - switch_to_tab: Switch to an existing tab
|
||||
* - close_tab: Close a tab (optionally switch to another)
|
||||
*/
|
||||
import * as z from 'zod'
|
||||
import * as z from 'zod/v4'
|
||||
|
||||
import type { TabsController } from './TabsController'
|
||||
|
||||
|
||||
@@ -16,7 +16,13 @@ import { DEMO_CONFIG, migrateLegacyEndpoint } from './constants'
|
||||
/** Language preference: undefined means follow system */
|
||||
export type LanguagePreference = SupportedLanguage | undefined
|
||||
|
||||
export interface ExtConfig extends LLMConfig {
|
||||
export interface AdvancedConfig {
|
||||
maxSteps?: number
|
||||
systemInstruction?: string
|
||||
experimentalLlmsTxt?: boolean
|
||||
}
|
||||
|
||||
export interface ExtConfig extends LLMConfig, AdvancedConfig {
|
||||
language?: LanguagePreference
|
||||
}
|
||||
|
||||
@@ -40,9 +46,10 @@ export function useAgent(): UseAgentResult {
|
||||
const [config, setConfig] = useState<ExtConfig | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
chrome.storage.local.get(['llmConfig', 'language']).then((result) => {
|
||||
chrome.storage.local.get(['llmConfig', 'language', 'advancedConfig']).then((result) => {
|
||||
let llmConfig = (result.llmConfig as LLMConfig) ?? DEMO_CONFIG
|
||||
const language = (result.language as SupportedLanguage) || undefined
|
||||
const advancedConfig = (result.advancedConfig as AdvancedConfig) ?? {}
|
||||
|
||||
// Auto-migrate legacy testing endpoints
|
||||
const migrated = migrateLegacyEndpoint(llmConfig)
|
||||
@@ -53,14 +60,18 @@ export function useAgent(): UseAgentResult {
|
||||
chrome.storage.local.set({ llmConfig: DEMO_CONFIG })
|
||||
}
|
||||
|
||||
setConfig({ ...llmConfig, language })
|
||||
setConfig({ ...llmConfig, ...advancedConfig, language })
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!config) return
|
||||
|
||||
const agent = new MultiPageAgent(config)
|
||||
const { systemInstruction, ...agentConfig } = config
|
||||
const agent = new MultiPageAgent({
|
||||
...agentConfig,
|
||||
instructions: systemInstruction ? { system: systemInstruction } : undefined,
|
||||
})
|
||||
agentRef.current = agent
|
||||
|
||||
const handleStatusChange = (e: Event) => {
|
||||
@@ -106,15 +117,26 @@ export function useAgent(): UseAgentResult {
|
||||
agentRef.current?.stop()
|
||||
}, [])
|
||||
|
||||
const configure = useCallback(async ({ language, ...llmConfig }: ExtConfig) => {
|
||||
await chrome.storage.local.set({ llmConfig })
|
||||
if (language) {
|
||||
await chrome.storage.local.set({ language })
|
||||
} else {
|
||||
await chrome.storage.local.remove('language')
|
||||
}
|
||||
setConfig({ ...llmConfig, language })
|
||||
}, [])
|
||||
const configure = useCallback(
|
||||
async ({
|
||||
language,
|
||||
maxSteps,
|
||||
systemInstruction,
|
||||
experimentalLlmsTxt,
|
||||
...llmConfig
|
||||
}: ExtConfig) => {
|
||||
await chrome.storage.local.set({ llmConfig })
|
||||
if (language) {
|
||||
await chrome.storage.local.set({ language })
|
||||
} else {
|
||||
await chrome.storage.local.remove('language')
|
||||
}
|
||||
const advancedConfig: AdvancedConfig = { maxSteps, systemInstruction, experimentalLlmsTxt }
|
||||
await chrome.storage.local.set({ advancedConfig })
|
||||
setConfig({ ...llmConfig, ...advancedConfig, language })
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
return {
|
||||
status,
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
import { Copy, CornerUpLeft, Eye, EyeOff, HatGlasses, Home, Loader2, Scale } from 'lucide-react'
|
||||
import {
|
||||
ChevronDown,
|
||||
Copy,
|
||||
CornerUpLeft,
|
||||
Eye,
|
||||
EyeOff,
|
||||
HatGlasses,
|
||||
Home,
|
||||
Loader2,
|
||||
Scale,
|
||||
} from 'lucide-react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { siGithub } from 'simple-icons'
|
||||
|
||||
@@ -6,6 +16,7 @@ import { DEMO_API_KEY, DEMO_BASE_URL, DEMO_MODEL, isTestingEndpoint } from '@/ag
|
||||
import type { ExtConfig, LanguagePreference } from '@/agent/useAgent'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
|
||||
interface ConfigPanelProps {
|
||||
config: ExtConfig | null
|
||||
@@ -18,18 +29,26 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
||||
const [baseURL, setBaseURL] = useState(config?.baseURL || DEMO_BASE_URL)
|
||||
const [model, setModel] = useState(config?.model || DEMO_MODEL)
|
||||
const [language, setLanguage] = useState<LanguagePreference>(config?.language)
|
||||
const [maxSteps, setMaxSteps] = useState<number | undefined>(config?.maxSteps)
|
||||
const [systemInstruction, setSystemInstruction] = useState(config?.systemInstruction ?? '')
|
||||
const [experimentalLlmsTxt, setExperimentalLlmsTxt] = useState(
|
||||
config?.experimentalLlmsTxt ?? false
|
||||
)
|
||||
const [advancedOpen, setAdvancedOpen] = useState(false)
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [userAuthToken, setUserAuthToken] = useState<string>('')
|
||||
const [copied, setCopied] = useState(false)
|
||||
const [showToken, setShowToken] = useState(false)
|
||||
const [showApiKey, setShowApiKey] = useState(false)
|
||||
|
||||
// Update local state when config prop changes
|
||||
useEffect(() => {
|
||||
setApiKey(config?.apiKey || DEMO_API_KEY)
|
||||
setBaseURL(config?.baseURL || DEMO_BASE_URL)
|
||||
setModel(config?.model || DEMO_MODEL)
|
||||
setLanguage(config?.language)
|
||||
setMaxSteps(config?.maxSteps)
|
||||
setSystemInstruction(config?.systemInstruction ?? '')
|
||||
setExperimentalLlmsTxt(config?.experimentalLlmsTxt ?? false)
|
||||
}, [config])
|
||||
|
||||
// Poll for user auth token every second until found
|
||||
@@ -67,7 +86,15 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
||||
const handleSave = async () => {
|
||||
setSaving(true)
|
||||
try {
|
||||
await onSave({ apiKey, baseURL, model, language })
|
||||
await onSave({
|
||||
apiKey,
|
||||
baseURL,
|
||||
model,
|
||||
language,
|
||||
maxSteps: maxSteps || undefined,
|
||||
systemInstruction: systemInstruction || undefined,
|
||||
experimentalLlmsTxt,
|
||||
})
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
@@ -87,28 +114,11 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Testing API notice */}
|
||||
{isTestingEndpoint(baseURL) && (
|
||||
<div className="p-2.5 rounded-md border border-amber-500/30 bg-amber-500/5 text-[10px] text-muted-foreground leading-relaxed">
|
||||
<Scale className="size-3 inline-block mr-1 -mt-0.5 text-amber-600" />
|
||||
You are using the free testing API. By using this service you agree to the{' '}
|
||||
<a
|
||||
href="https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline hover:text-foreground"
|
||||
>
|
||||
Terms of Use & Privacy Policy
|
||||
</a>
|
||||
. No sensitive data. No guaranteed availability.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* User Auth Token Section */}
|
||||
<div className="flex flex-col gap-1.5 p-3 bg-muted/50 rounded-md border">
|
||||
<label className="text-xs font-medium text-muted-foreground">User Auth Token</label>
|
||||
<p className="text-[10px] text-muted-foreground mb-1">
|
||||
Add this token to a website's localStorage to give it authorization to call this extension
|
||||
Give a website the ability to call this extension.
|
||||
</p>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Input
|
||||
@@ -153,6 +163,23 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Testing API notice */}
|
||||
{isTestingEndpoint(baseURL) && (
|
||||
<div className="p-2.5 rounded-md border border-amber-500/30 bg-amber-500/5 text-[11px] text-muted-foreground leading-relaxed">
|
||||
<Scale className="size-3 inline-block mr-1 -mt-0.5 text-amber-600" />
|
||||
You are using the free testing API. By using this service you agree to the{' '}
|
||||
<a
|
||||
href="https://github.com/alibaba/page-agent/blob/main/docs/terms-and-privacy.md"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline hover:text-foreground"
|
||||
>
|
||||
Terms of Use & Privacy Policy
|
||||
</a>
|
||||
. No sensitive data. No guaranteed availability.
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<label className="text-xs text-muted-foreground">Model</label>
|
||||
<Input
|
||||
@@ -197,6 +224,52 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Advanced Config */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setAdvancedOpen(!advancedOpen)}
|
||||
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground cursor-pointer mt-1 font-bold"
|
||||
>
|
||||
Advanced
|
||||
<ChevronDown
|
||||
className="size-3 transition-transform"
|
||||
style={{ transform: advancedOpen ? 'rotate(0deg)' : 'rotate(90deg)' }}
|
||||
/>
|
||||
</button>
|
||||
|
||||
{advancedOpen && (
|
||||
<>
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<label className="text-xs text-muted-foreground">Max Steps</label>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="40"
|
||||
min={1}
|
||||
max={200}
|
||||
value={maxSteps ?? ''}
|
||||
onChange={(e) => setMaxSteps(e.target.value ? Number(e.target.value) : undefined)}
|
||||
className="text-xs h-8 [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none [-moz-appearance:textfield]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<label className="text-xs text-muted-foreground">System Instruction</label>
|
||||
<textarea
|
||||
placeholder="Additional instructions for the agent..."
|
||||
value={systemInstruction}
|
||||
onChange={(e) => setSystemInstruction(e.target.value)}
|
||||
rows={3}
|
||||
className="text-xs rounded-md border border-input bg-background px-3 py-2 resize-y min-h-[60px]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label className="flex items-center justify-between cursor-pointer">
|
||||
<span className="text-xs text-muted-foreground">Experimental llms.txt support</span>
|
||||
<Switch checked={experimentalLlmsTxt} onCheckedChange={setExperimentalLlmsTxt} />
|
||||
</label>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="flex gap-2 mt-2">
|
||||
<Button variant="outline" onClick={onClose} className="flex-1 h-8 text-xs cursor-pointer">
|
||||
Cancel
|
||||
|
||||
@@ -43,20 +43,24 @@ export function MotionOverlay({ active }: { active: boolean }) {
|
||||
const motionRef = useRef<Motion | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const mode = document.documentElement.classList.contains('dark') ? 'dark' : 'light'
|
||||
const motion = new Motion({
|
||||
mode,
|
||||
borderWidth: 4,
|
||||
borderRadius: 14,
|
||||
glowWidth: mode === 'dark' ? 120 : 60,
|
||||
styles: { position: 'absolute', inset: '0' },
|
||||
})
|
||||
motionRef.current = motion
|
||||
containerRef.current!.appendChild(motion.element)
|
||||
motion.autoResize(containerRef.current!)
|
||||
try {
|
||||
const mode = document.documentElement.classList.contains('dark') ? 'dark' : 'light'
|
||||
const motion = new Motion({
|
||||
mode,
|
||||
borderWidth: 4,
|
||||
borderRadius: 14,
|
||||
glowWidth: mode === 'dark' ? 120 : 60,
|
||||
styles: { position: 'absolute', inset: '0' },
|
||||
})
|
||||
motionRef.current = motion
|
||||
containerRef.current!.appendChild(motion.element)
|
||||
motion.autoResize(containerRef.current!)
|
||||
} catch (e) {
|
||||
console.warn('[MotionOverlay] Motion unavailable:', e)
|
||||
}
|
||||
|
||||
return () => {
|
||||
motion.dispose()
|
||||
motionRef.current?.dispose()
|
||||
motionRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@page-agent/llms",
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"type": "module",
|
||||
"main": "./dist/lib/page-agent-llms.js",
|
||||
"module": "./dist/lib/page-agent-llms.js",
|
||||
@@ -37,7 +37,12 @@
|
||||
"postpublish": "node -e \"['LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^5.6.2",
|
||||
"chalk": "^5.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"zod": "^4.3.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* OpenAI Client implementation
|
||||
*/
|
||||
import * as z from 'zod'
|
||||
import * as z from 'zod/v4'
|
||||
|
||||
import { InvokeError, InvokeErrorType } from './errors'
|
||||
import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Core types for LLM integration
|
||||
*/
|
||||
import type * as z from 'zod'
|
||||
import type * as z from 'zod/v4'
|
||||
|
||||
/**
|
||||
* Message format - OpenAI standard (industry standard)
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
* Utility functions for LLM integration
|
||||
*/
|
||||
import chalk from 'chalk'
|
||||
import * as z from 'zod'
|
||||
import * as z from 'zod/v4'
|
||||
|
||||
import type { Tool } from './types'
|
||||
|
||||
function debug(message: string) {
|
||||
console.debug(chalk.gray('[LLM]'), message)
|
||||
}
|
||||
const debug = console.debug.bind(console, chalk.gray('[LLM]'))
|
||||
|
||||
/**
|
||||
* Convert Zod schema to OpenAI tool format
|
||||
|
||||
@@ -25,7 +25,7 @@ export default defineConfig({
|
||||
},
|
||||
outDir: resolve(__dirname, 'dist', 'lib'),
|
||||
rollupOptions: {
|
||||
external: ['chalk', 'zod'],
|
||||
external: ['chalk', 'zod', 'zod/v4'],
|
||||
},
|
||||
minify: false,
|
||||
sourcemap: true,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "page-agent",
|
||||
"private": false,
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"type": "module",
|
||||
"main": "./dist/esm/page-agent.js",
|
||||
"module": "./dist/esm/page-agent.js",
|
||||
@@ -44,11 +44,16 @@
|
||||
"postpublish": "node -e \"['README.md','LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@page-agent/core": "1.4.4",
|
||||
"@page-agent/llms": "1.4.4",
|
||||
"@page-agent/page-controller": "1.4.4",
|
||||
"@page-agent/ui": "1.4.4",
|
||||
"chalk": "^5.6.2",
|
||||
"@page-agent/core": "1.5.2",
|
||||
"@page-agent/llms": "1.5.2",
|
||||
"@page-agent/page-controller": "1.5.2",
|
||||
"@page-agent/ui": "1.5.2",
|
||||
"chalk": "^5.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"zod": "^4.3.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ export default defineConfig({
|
||||
external: [
|
||||
'chalk',
|
||||
'zod',
|
||||
'zod/v4',
|
||||
// all the internal packages
|
||||
/^@page-agent\//,
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@page-agent/page-controller",
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"type": "module",
|
||||
"main": "./dist/lib/page-controller.js",
|
||||
"module": "./dist/lib/page-controller.js",
|
||||
|
||||
@@ -8,13 +8,7 @@ import cursorStyles from './cursor.module.css'
|
||||
export class SimulatorMask {
|
||||
shown: boolean = false
|
||||
wrapper = document.createElement('div')
|
||||
motion = new Motion({
|
||||
mode: isPageDark() ? 'dark' : 'light',
|
||||
styles: {
|
||||
position: 'absolute',
|
||||
inset: '0',
|
||||
},
|
||||
})
|
||||
motion: Motion | null = null
|
||||
|
||||
#cursor = document.createElement('div')
|
||||
|
||||
@@ -30,8 +24,17 @@ export class SimulatorMask {
|
||||
this.wrapper.setAttribute('data-browser-use-ignore', 'true')
|
||||
this.wrapper.setAttribute('data-page-agent-ignore', 'true')
|
||||
|
||||
this.wrapper.appendChild(this.motion.element)
|
||||
this.motion.autoResize(this.wrapper)
|
||||
try {
|
||||
const motion = new Motion({
|
||||
mode: isPageDark() ? 'dark' : 'light',
|
||||
styles: { position: 'absolute', inset: '0' },
|
||||
})
|
||||
this.motion = motion
|
||||
this.wrapper.appendChild(motion.element)
|
||||
motion.autoResize(this.wrapper)
|
||||
} catch (e) {
|
||||
console.warn('[SimulatorMask] Motion overlay unavailable:', e)
|
||||
}
|
||||
|
||||
// Capture all mouse, keyboard, and wheel events
|
||||
this.wrapper.addEventListener('click', (e) => {
|
||||
@@ -145,8 +148,8 @@ export class SimulatorMask {
|
||||
if (this.shown) return
|
||||
|
||||
this.shown = true
|
||||
this.motion.start()
|
||||
this.motion.fadeIn()
|
||||
this.motion?.start()
|
||||
this.motion?.fadeIn()
|
||||
|
||||
this.wrapper.style.display = 'block'
|
||||
|
||||
@@ -163,8 +166,8 @@ export class SimulatorMask {
|
||||
if (!this.shown) return
|
||||
|
||||
this.shown = false
|
||||
this.motion.fadeOut()
|
||||
this.motion.pause()
|
||||
this.motion?.fadeOut()
|
||||
this.motion?.pause()
|
||||
|
||||
this.#cursor.classList.remove(cursorStyles.clicking)
|
||||
|
||||
@@ -174,7 +177,7 @@ export class SimulatorMask {
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.motion.dispose()
|
||||
this.motion?.dispose()
|
||||
this.wrapper.remove()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@page-agent/ui",
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"type": "module",
|
||||
"main": "./dist/lib/page-agent-ui.js",
|
||||
"module": "./dist/lib/page-agent-ui.js",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@page-agent/website",
|
||||
"private": true,
|
||||
"version": "1.4.4",
|
||||
"version": "1.5.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host 0.0.0.0",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Demo build (auto-init with demo LLM, for quick testing)
|
||||
export const CDN_DEMO_URL =
|
||||
'https://cdn.jsdelivr.net/npm/page-agent@1.4.4/dist/iife/page-agent.demo.js'
|
||||
'https://cdn.jsdelivr.net/npm/page-agent@1.5.2/dist/iife/page-agent.demo.js'
|
||||
export const CDN_DEMO_CN_URL =
|
||||
'https://registry.npmmirror.com/page-agent/1.4.4/files/dist/iife/page-agent.demo.js'
|
||||
'https://registry.npmmirror.com/page-agent/1.5.2/files/dist/iife/page-agent.demo.js'
|
||||
|
||||
// Demo LLM for website testing (homepage quick trial uses flash)
|
||||
export const DEMO_MODEL = 'qwen3.5-flash'
|
||||
|
||||
@@ -181,7 +181,7 @@ const result = await agent.execute('Fill in the form with test data')`}
|
||||
{
|
||||
name: 'maxSteps',
|
||||
type: 'number',
|
||||
defaultValue: '20',
|
||||
defaultValue: '40',
|
||||
description: isZh ? '每个任务的最大步骤数' : 'Maximum number of steps per task',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -16,6 +16,22 @@ export default function CustomTools() {
|
||||
</p>
|
||||
|
||||
<div className="space-y-8">
|
||||
<section>
|
||||
<Heading id="zod-version" className="text-2xl font-bold mb-4">
|
||||
{isZh ? 'Zod 版本' : 'Zod Version'}
|
||||
</Heading>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||
{isZh
|
||||
? 'Page Agent 使用 Zod 定义工具的输入 schema。支持 Zod 3 (>=3.25.0) 和 Zod 4,请从 zod/v4 子路径导入。不支持 Zod Mini。'
|
||||
: 'Page Agent uses Zod for tool input schemas. Both Zod 3 (>=3.25.0) and Zod 4 are supported. Always import from the zod/v4 subpath. Zod Mini is not supported.'}
|
||||
</p>
|
||||
<CodeEditor
|
||||
code={`// Zod 3 (>=3.25.0) or Zod 4
|
||||
import { z } from 'zod/v4'`}
|
||||
language="javascript"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<Heading id="define-tools" className="text-2xl font-bold mb-4">
|
||||
{isZh ? '定义工具' : 'Define Tools'}
|
||||
@@ -27,7 +43,7 @@ export default function CustomTools() {
|
||||
</p>
|
||||
|
||||
<CodeEditor
|
||||
code={`import { z } from 'zod'
|
||||
code={`import { z } from 'zod/v4'
|
||||
import { PageAgent, tool } from 'page-agent'
|
||||
|
||||
const pageAgent = new PageAgent({
|
||||
|
||||
@@ -18,8 +18,10 @@ function ScrollToTop() {
|
||||
|
||||
export default function Router() {
|
||||
useEffect(() => {
|
||||
const id = requestIdleCallback(() => docsImport())
|
||||
return () => cancelIdleCallback(id)
|
||||
const schedule = globalThis.requestIdleCallback ?? ((cb: () => void) => setTimeout(cb, 1))
|
||||
const cancel = globalThis.cancelIdleCallback ?? clearTimeout
|
||||
const id = schedule(() => docsImport())
|
||||
return () => cancel(id)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user