Compare commits

..

10 Commits

Author SHA1 Message Date
Simon 598b144d06 feat: change maxSteps to 40 2026-03-05 19:26:56 +08:00
Simon 1cd8a8de60 Merge pull request #158 from alibaba/fix/zod-v3
feat: import zod properly
2026-03-05 19:23:47 +08:00
Simon 9beffca985 docs: update doc for Zod v4 support 2026-03-05 19:15:03 +08:00
Simon 53db2069ce feat: zod support v3/4 2026-03-05 19:13:18 +08:00
Simon 121104e13d feat: truncate llms.txt results 2026-03-05 16:59:08 +08:00
Simon 0dacbda9da chore: improve logging 2026-03-05 16:54:41 +08:00
Simon 8bc27414a4 chore(version): bump version to 1.4.4 2026-03-05 16:25:16 +08:00
Simon 76509d93f0 feat: replace data-browser-use-ignore with data-page-agent-ignore 2026-03-05 16:24:50 +08:00
Simon ff31b0c03e chore: adjust website structure 2026-03-05 16:21:58 +08:00
Simon 6b2ab73d65 docs: model recommendation 2026-03-05 16:10:03 +08:00
33 changed files with 146 additions and 88 deletions
+2 -2
View File
@@ -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.3/dist/iife/page-agent.demo.js |
| China | https://registry.npmmirror.com/page-agent/1.4.3/files/dist/iife/page-agent.demo.js |
| 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 |
> **⚠️ 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).
+2 -2
View File
@@ -49,8 +49,8 @@
| Mirrors | URL |
| ------- | ---------------------------------------------------------------------------------- |
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.4.3/dist/iife/page-agent.demo.js |
| China | https://registry.npmmirror.com/page-agent/1.4.3/files/dist/iife/page-agent.demo.js |
| 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 |
> **⚠️ 仅用于技术评估。** 该 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)。
+40 -23
View File
@@ -1,12 +1,12 @@
{
"name": "root",
"version": "1.4.3",
"version": "1.4.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "root",
"version": "1.4.3",
"version": "1.4.4",
"license": "MIT",
"workspaces": [
"packages/page-controller",
@@ -11113,13 +11113,18 @@
},
"packages/core": {
"name": "@page-agent/core",
"version": "1.4.3",
"version": "1.4.4",
"license": "MIT",
"dependencies": {
"@page-agent/llms": "1.4.3",
"@page-agent/page-controller": "1.4.3",
"chalk": "^5.6.2",
"@page-agent/llms": "1.4.4",
"@page-agent/page-controller": "1.4.4",
"chalk": "^5.6.2"
},
"devDependencies": {
"zod": "^4.3.5"
},
"peerDependencies": {
"zod": "^3.25.0 || ^4.0.0"
}
},
"packages/extension": {
@@ -11127,13 +11132,12 @@
"version": "0.1.8",
"hasInstallScript": true,
"dependencies": {
"@page-agent/core": "1.4.3",
"@page-agent/llms": "1.4.3",
"@page-agent/page-controller": "1.4.3",
"@page-agent/ui": "1.4.3",
"@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",
"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.3",
"version": "1.4.4",
"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.3",
"version": "1.4.4",
"license": "MIT",
"dependencies": {
"@page-agent/core": "1.4.3",
"@page-agent/llms": "1.4.3",
"@page-agent/page-controller": "1.4.3",
"@page-agent/ui": "1.4.3",
"chalk": "^5.6.2",
"@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"
},
"devDependencies": {
"zod": "^4.3.5"
},
"peerDependencies": {
"zod": "^3.25.0 || ^4.0.0"
}
},
"packages/page-controller": {
"name": "@page-agent/page-controller",
"version": "1.4.3",
"version": "1.4.4",
"license": "MIT",
"dependencies": {
"ai-motion": "^0.4.8"
@@ -11195,12 +11212,12 @@
},
"packages/ui": {
"name": "@page-agent/ui",
"version": "1.4.3",
"version": "1.4.4",
"license": "MIT"
},
"packages/website": {
"name": "@page-agent/website",
"version": "1.4.3",
"version": "1.4.4",
"devDependencies": {
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-separator": "^1.1.8",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "root",
"private": true,
"version": "1.4.3",
"version": "1.4.4",
"type": "module",
"workspaces": [
"packages/page-controller",
+9 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@page-agent/core",
"private": false,
"version": "1.4.3",
"version": "1.4.4",
"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.3",
"@page-agent/page-controller": "1.4.3"
"@page-agent/llms": "1.4.4",
"@page-agent/page-controller": "1.4.4"
},
"peerDependencies": {
"zod": "^3.25.0 || ^4.0.0"
},
"devDependencies": {
"zod": "^4.3.5"
}
}
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -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'
+4 -3
View File
@@ -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)
+9 -9
View File
@@ -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 }
}
}
+7 -3
View File
@@ -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
+1
View File
@@ -30,6 +30,7 @@ export default defineConfig({
external: [
'chalk',
'zod',
'zod/v4',
// all the internal packages
/^@page-agent\//,
],
+8 -6
View File
@@ -38,12 +38,14 @@
"wxt": "^0.20.18"
},
"dependencies": {
"@page-agent/core": "1.4.3",
"@page-agent/llms": "1.4.3",
"@page-agent/page-controller": "1.4.3",
"@page-agent/ui": "1.4.3",
"@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",
"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 -1
View File
@@ -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'
+7 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@page-agent/llms",
"version": "1.4.3",
"version": "1.4.4",
"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 -1
View File
@@ -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 -1
View File
@@ -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 -4
View File
@@ -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
+1 -1
View File
@@ -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,
+11 -6
View File
@@ -1,7 +1,7 @@
{
"name": "page-agent",
"private": false,
"version": "1.4.3",
"version": "1.4.4",
"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.3",
"@page-agent/llms": "1.4.3",
"@page-agent/page-controller": "1.4.3",
"@page-agent/ui": "1.4.3",
"chalk": "^5.6.2",
"@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"
},
"peerDependencies": {
"zod": "^3.25.0 || ^4.0.0"
},
"devDependencies": {
"zod": "^4.3.5"
}
}
+1
View File
@@ -30,6 +30,7 @@ export default defineConfig({
external: [
'chalk',
'zod',
'zod/v4',
// all the internal packages
/^@page-agent\//,
],
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@page-agent/page-controller",
"version": "1.4.3",
"version": "1.4.4",
"type": "module",
"main": "./dist/lib/page-controller.js",
"module": "./dist/lib/page-controller.js",
@@ -1446,7 +1446,7 @@ export default (
/**
* @edit add `data-browser-use-ignore` attribute
*/
if (node.dataset?.browserUseIgnore === 'true') {
if (node.dataset?.browserUseIgnore === 'true' || node.dataset?.pageAgentIgnore === 'true') {
return null // Skip this node and its children
}
@@ -28,6 +28,7 @@ export class SimulatorMask {
this.wrapper.id = 'page-agent-runtime_simulator-mask'
this.wrapper.className = styles.wrapper
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)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@page-agent/ui",
"version": "1.4.3",
"version": "1.4.4",
"type": "module",
"main": "./dist/lib/page-agent-ui.js",
"module": "./dist/lib/page-agent-ui.js",
+1
View File
@@ -373,6 +373,7 @@ export class Panel {
wrapper.id = 'page-agent-runtime_agent-panel'
wrapper.className = styles.wrapper
wrapper.setAttribute('data-browser-use-ignore', 'true')
wrapper.setAttribute('data-page-agent-ignore', 'true')
wrapper.innerHTML = `
<div class="${styles.background}"></div>
+1 -1
View File
@@ -62,7 +62,7 @@ Located in `src/components/ui/`:
src/
├── pages/
│ ├── home/
│ │ ├── HomeContent.tsx # Homepage sections
│ │ ├── index.tsx # Homepage
│ │ └── ...Section.tsx
│ └── docs/
│ ├── index.tsx # Docs route switch
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@page-agent/website",
"private": true,
"version": "1.4.3",
"version": "1.4.4",
"type": "module",
"scripts": {
"dev": "vite --host 0.0.0.0",
+2 -2
View File
@@ -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.3/dist/iife/page-agent.demo.js'
'https://cdn.jsdelivr.net/npm/page-agent@1.4.4/dist/iife/page-agent.demo.js'
export const CDN_DEMO_CN_URL =
'https://registry.npmmirror.com/page-agent/1.4.3/files/dist/iife/page-agent.demo.js'
'https://registry.npmmirror.com/page-agent/1.4.4/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({
@@ -10,11 +10,11 @@ const BASELINE = new Set([
'gemini-3-flash',
'deepseek-3.2',
'qwen3.5-plus',
'qwen3.5-flash',
])
// Models grouped by brand, newest first
const MODEL_GROUPS: Record<string, string[]> = {
OpenAI: ['gpt-5.2', 'gpt-5.1', 'gpt-5', 'gpt-5-mini', 'gpt-4.1', 'gpt-4.1-mini'],
Qwen: [
'qwen3.5-plus',
'qwen3.5-flash',
@@ -23,6 +23,7 @@ const MODEL_GROUPS: Record<string, string[]> = {
'qwen-3-plus',
'qwen3:14b (ollama)',
],
OpenAI: ['gpt-5.2', 'gpt-5.1', 'gpt-5', 'gpt-5-mini', 'gpt-4.1', 'gpt-4.1-mini'],
DeepSeek: ['deepseek-3.2'],
Google: ['gemini-3-pro', 'gemini-3-flash', 'gemini-2.5'],
Anthropic: [
@@ -1,10 +1,10 @@
import { Suspense, lazy } from 'react'
import HeroSection from './home/HeroSection'
import HeroSection from './HeroSection'
const FeaturesSection = lazy(() => import('./home/FeaturesSection'))
const ScenariosSection = lazy(() => import('./home/ScenariosSection'))
const OneMoreThingSection = lazy(() => import('./home/OneMoreThingSection'))
const FeaturesSection = lazy(() => import('./FeaturesSection'))
const ScenariosSection = lazy(() => import('./ScenariosSection'))
const OneMoreThingSection = lazy(() => import('./OneMoreThingSection'))
export default function HomePage() {
return (
+2 -2
View File
@@ -3,9 +3,9 @@ import { Route, Switch, useLocation } from 'wouter'
import Footer from './components/Footer'
import Header from './components/Header'
import HomePage from './pages/Home'
import HomePage from './pages/home'
const docsImport = () => import('./pages/docs/index')
const docsImport = () => import('./pages/docs')
const DocsPages = lazy(docsImport)
function ScrollToTop() {