refactor: zod tree-shaking; better error handling in agent steps

This commit is contained in:
Simon
2026-02-09 17:49:10 +08:00
parent 6fdb8d03f2
commit 6f0ff1fd33
12 changed files with 82 additions and 67 deletions

11
package-lock.json generated
View File

@@ -41,6 +41,7 @@
"typescript-eslint": "^8.54.0",
"unplugin-dts": "^1.0.0-beta.6",
"vite": "^7.3.1",
"vite-bundle-analyzer": "^1.3.6",
"vite-plugin-css-injected-by-js": "^3.5.2"
},
"engines": {
@@ -10411,6 +10412,16 @@
}
}
},
"node_modules/vite-bundle-analyzer": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/vite-bundle-analyzer/-/vite-bundle-analyzer-1.3.6.tgz",
"integrity": "sha512-elFXkF9oGy4CJEeOdP1DSEHRDKWfmaEDfT9/GuF4jmyfmUF6nC//iN+ythqMEVvrtchOEHGKLumZwnu1NjoI0w==",
"dev": true,
"license": "MIT",
"bin": {
"analyze": "dist/bin.js"
}
},
"node_modules/vite-node": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-5.3.0.tgz",

View File

@@ -59,7 +59,8 @@
"typescript-eslint": "^8.54.0",
"unplugin-dts": "^1.0.0-beta.6",
"vite": "^7.3.1",
"vite-plugin-css-injected-by-js": "^3.5.2"
"vite-plugin-css-injected-by-js": "^3.5.2",
"vite-bundle-analyzer": "^1.3.6"
},
"overrides": {
"typescript": "^5.9.3"

View File

@@ -5,13 +5,13 @@
import { InvokeError, LLM, type Tool } from '@page-agent/llms'
import type { PageController } from '@page-agent/page-controller'
import chalk from 'chalk'
import zod from 'zod'
import * as zod from 'zod'
import { type PageAgentConfig } from './config'
import { DEFAULT_MAX_STEPS } from './config/constants'
import SYSTEM_PROMPT from './prompts/system_prompt.md?raw'
import { tools } from './tools'
import {
import type {
AgentActivity,
AgentReflection,
AgentStatus,
@@ -21,8 +21,7 @@ import {
MacroToolInput,
MacroToolResult,
} from './types'
import { normalizeResponse, trimLines, uid, waitFor } from './utils'
import { assert } from './utils/assert'
import { assert, normalizeResponse, trimLines, uid, waitFor } from './utils'
export { type PageAgentConfig }
export { tool, type PageAgentTool } from './tools'
@@ -209,20 +208,20 @@ export class PageAgentCore extends EventTarget {
lastURL: '',
}
try {
let step = 0
let step = 0
while (true) {
try {
console.group(`step: ${step}`)
while (true) {
await this.#generateObservations(step)
await onBeforeStep?.(this, step)
console.group(`step: ${step}`)
// abort
if (this.#abortController.signal.aborted) throw new Error('AbortError')
// Thinking
// status
console.log(chalk.blue('Thinking...'))
this.#emitActivity({ type: 'thinking' })
@@ -270,22 +269,13 @@ export class PageAgentCore extends EventTarget {
//
await onAfterStep?.(this, this.history)
console.log(chalk.green('Step finished:'), actionName)
console.groupEnd()
await onAfterStep?.(this, this.history)
// finish task if done
step++
if (step > this.config.maxSteps) {
this.#onDone(false)
const result: ExecutionResult = {
success: false,
data: 'Step count exceeded maximum limit',
history: this.history,
}
await onAfterTask?.(this, result)
return result
}
if (actionName === 'done') {
const success = action.input?.success ?? false
const text = action.input?.text || 'no text provided'
@@ -299,19 +289,32 @@ export class PageAgentCore extends EventTarget {
await onAfterTask?.(this, result)
return result
}
} catch (error: unknown) {
console.groupEnd() // to prevent nested groups
console.error('Task failed', error)
const errorMessage = String(error)
this.#emitActivity({ type: 'error', message: errorMessage })
this.#onDone(false)
const result: ExecutionResult = {
success: false,
data: errorMessage,
history: this.history,
}
await onAfterTask?.(this, result)
return result
}
} catch (error: unknown) {
console.error('Task failed', error)
const errorMessage = String(error)
this.#emitActivity({ type: 'error', message: errorMessage })
this.#onDone(false)
const result: ExecutionResult = {
success: false,
data: errorMessage,
history: this.history,
step++
if (step > this.config.maxSteps) {
this.#onDone(false)
const result: ExecutionResult = {
success: false,
data: 'Step count exceeded maximum limit',
history: this.history,
}
await onAfterTask?.(this, result)
return result
}
await onAfterTask?.(this, result)
return result
}
}

View File

@@ -2,7 +2,7 @@
* Internal tools for PageAgent.
* @note Adapted from browser-use
*/
import zod, { type z } from 'zod'
import * as zod from 'zod'
import type { PageAgentCore } from '../PageAgentCore'
import { waitFor } from '../utils'
@@ -13,7 +13,7 @@ import { waitFor } from '../utils'
export interface PageAgentTool<TParams = any> {
// name: string
description: string
inputSchema: z.ZodType<TParams>
inputSchema: zod.ZodType<TParams>
execute: (this: PageAgentCore, args: TParams) => Promise<string>
}

View File

@@ -1,17 +0,0 @@
import chalk from 'chalk'
/**
* Simple assertion function that throws an error if the condition is falsy
* @param condition - The condition to assert
* @param message - Optional error message
* @throws Error if condition is falsy
*/
export function assert(condition: unknown, message?: string, silent?: boolean): asserts condition {
if (!condition) {
const errorMessage = message ?? 'Assertion failed'
if (!silent) console.error(chalk.red(`❌ assert: ${errorMessage}`))
throw new Error(errorMessage)
}
}

View File

@@ -1,3 +1,5 @@
import chalk from 'chalk'
export { normalizeResponse } from './autoFixer'
/**
@@ -85,3 +87,19 @@ export function uid() {
ids.push(id)
return id
}
/**
* Simple assertion function that throws an error if the condition is falsy
* @param condition - The condition to assert
* @param message - Optional error message
* @throws Error if condition is falsy
*/
export function assert(condition: unknown, message?: string, silent?: boolean): asserts condition {
if (!condition) {
const errorMessage = message ?? 'Assertion failed'
if (!silent) console.error(chalk.red(`❌ assert: ${errorMessage}`))
throw new Error(errorMessage)
}
}

View File

@@ -6,7 +6,7 @@
* - switch_to_tab: Switch to an existing tab
* - close_tab: Close a tab (optionally switch to another)
*/
import zod from 'zod'
import * as zod from 'zod'
import type { TabsController } from './TabsController'

View File

@@ -2,7 +2,7 @@
* Utility functions for LLM integration
*/
import chalk from 'chalk'
import { z } from 'zod'
import * as z from 'zod'
import type { Tool } from './types'

View File

@@ -44,11 +44,11 @@
"postpublish": "node -e \"['README.md','LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
},
"dependencies": {
"chalk": "^5.6.2",
"zod": "^4.3.5",
"@page-agent/core": "1.1.1",
"@page-agent/llms": "1.1.1",
"@page-agent/page-controller": "1.1.1",
"@page-agent/core": "1.1.1",
"@page-agent/ui": "1.1.1"
"@page-agent/ui": "1.1.1",
"chalk": "^5.6.2",
"zod": "^4.3.5"
}
}

View File

@@ -3,6 +3,7 @@ import { config as dotenvConfig } from 'dotenv'
import { dirname, resolve } from 'path'
import { fileURLToPath } from 'url'
import { defineConfig } from 'vite'
// import { analyzer } from 'vite-bundle-analyzer'
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
const __dirname = dirname(fileURLToPath(import.meta.url))
@@ -14,8 +15,11 @@ dotenvConfig({ path: resolve(__dirname, '../../.env') })
// - alias all local packages so that they can be build in
// - no external
// - no d.ts. dts does not work with monorepo aliasing
export default defineConfig(({ mode }) => ({
plugins: [cssInjectedByJsPlugin({ relativeCSSInjection: true })],
export default defineConfig(() => ({
plugins: [
cssInjectedByJsPlugin({ relativeCSSInjection: true }),
// analyzer()
],
publicDir: false,
esbuild: {
keepNames: true,

View File

@@ -25,7 +25,7 @@ export default function CustomTools() {
</p>
<CodeEditor
code={`import zod from 'zod'
code={`import * as zod from 'zod'
import { PageAgent, tool } from 'page-agent'
// override internal tool

View File

@@ -362,11 +362,6 @@ export default function TroubleshootingPage() {
{/* Header */}
<div className="mb-10">
<h1 className="text-4xl font-bold mb-4 text-gray-900 dark:text-white">Troubleshooting</h1>
{/* <p className="text-lg text-gray-600 dark:text-gray-300">
{isZh
? ''
: 'Running into issues? Find solutions by symptom.'}
</p> */}
</div>
{/* Two-column: content + TOC */}