Compare commits

...

20 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
Simon 2754023a80 chore(version): bump version to 1.4.3 2026-03-04 21:18:51 +08:00
Simon fa5c65db53 docs: update custom-tool docs 2026-03-04 21:17:32 +08:00
Simon 53c2c19d69 docs: update advanced docs; rm best-practice 2026-03-04 21:13:10 +08:00
Simon e5437b445a fix(core): Core and PageAgent use different config types; improve code structure 2026-03-04 20:05:48 +08:00
Simon 09bdf9ddaf feat(core): experimental support for llms.txt (#157)
* feat(core): experimental support for llms.txt

* docs: experimentalLlmsTxt
2026-03-04 18:53:24 +08:00
Simon b58d2a09ef Merge pull request #152 from alibaba/dependabot/npm_and_yarn/development-dependencies-7e2b43fd30
chore(deps-dev): bump the development-dependencies group with 7 updates
2026-03-03 22:04:35 +08:00
Simon ce442742ba Merge pull request #153 from alibaba/dependabot/npm_and_yarn/vite-plugin-css-injected-by-js-4.0.1
chore(deps-dev): bump vite-plugin-css-injected-by-js from 3.5.2 to 4.0.1
2026-03-03 22:04:17 +08:00
Simon 632f22962e docs: add recommended startup parameters for Ollama 2026-03-03 21:59:02 +08:00
dependabot[bot] 72c3b12fb4 chore(deps-dev): bump the development-dependencies group with 7 updates
Bumps the development-dependencies group with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [@microsoft/api-extractor](https://github.com/microsoft/rushstack/tree/HEAD/apps/api-extractor) | `7.57.3` | `7.57.6` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `25.3.0` | `25.3.3` |
| [globals](https://github.com/sindresorhus/globals) | `17.3.0` | `17.4.0` |
| [lint-staged](https://github.com/lint-staged/lint-staged) | `16.2.7` | `16.3.1` |
| [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `0.575.0` | `0.576.0` |
| [motion](https://github.com/motiondivision/motion) | `12.34.3` | `12.34.4` |
| [simple-icons](https://github.com/simple-icons/simple-icons) | `16.9.0` | `16.10.0` |


Updates `@microsoft/api-extractor` from 7.57.3 to 7.57.6
- [Changelog](https://github.com/microsoft/rushstack/blob/main/apps/api-extractor/CHANGELOG.md)
- [Commits](https://github.com/microsoft/rushstack/commits/@microsoft/api-extractor_v7.57.6/apps/api-extractor)

Updates `@types/node` from 25.3.0 to 25.3.3
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `globals` from 17.3.0 to 17.4.0
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](https://github.com/sindresorhus/globals/compare/v17.3.0...v17.4.0)

Updates `lint-staged` from 16.2.7 to 16.3.1
- [Release notes](https://github.com/lint-staged/lint-staged/releases)
- [Changelog](https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lint-staged/lint-staged/compare/v16.2.7...v16.3.1)

Updates `lucide-react` from 0.575.0 to 0.576.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/0.576.0/packages/lucide-react)

Updates `motion` from 12.34.3 to 12.34.4
- [Changelog](https://github.com/motiondivision/motion/blob/main/CHANGELOG.md)
- [Commits](https://github.com/motiondivision/motion/compare/v12.34.3...v12.34.4)

Updates `simple-icons` from 16.9.0 to 16.10.0
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/16.9.0...16.10.0)

---
updated-dependencies:
- dependency-name: "@microsoft/api-extractor"
  dependency-version: 7.57.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@types/node"
  dependency-version: 25.3.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: globals
  dependency-version: 17.4.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: lint-staged
  dependency-version: 16.3.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: lucide-react
  dependency-version: 0.576.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: motion
  dependency-version: 12.34.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: simple-icons
  dependency-version: 16.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-03 13:58:04 +00:00
dependabot[bot] 5fec6846b0 chore(deps-dev): bump vite-plugin-css-injected-by-js from 3.5.2 to 4.0.1
Bumps [vite-plugin-css-injected-by-js](https://github.com/marco-prontera/vite-plugin-css-injected-by-js) from 3.5.2 to 4.0.1.
- [Release notes](https://github.com/marco-prontera/vite-plugin-css-injected-by-js/releases)
- [Commits](https://github.com/marco-prontera/vite-plugin-css-injected-by-js/compare/v3.5.2...v4.0.1)

---
updated-dependencies:
- dependency-name: vite-plugin-css-injected-by-js
  dependency-version: 4.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-02 21:00:45 +00:00
49 changed files with 994 additions and 803 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.2/dist/iife/page-agent.demo.js |
| China | https://registry.npmmirror.com/page-agent/1.4.2/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 -1
View File
@@ -122,9 +122,10 @@ PageAgent is now ready for production use. The API is stable and breaking change
- **Ask User Tool** - Agent can ask users for clarification
- **i18n Support** - English and Chinese localization
### Configuration
### Configuration
```typescript
// Version 1.0.0
interface PageAgentConfig {
// LLM Configuration (required)
baseURL: string
+2 -2
View File
@@ -49,8 +49,8 @@
| Mirrors | URL |
| ------- | ---------------------------------------------------------------------------------- |
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.4.2/dist/iife/page-agent.demo.js |
| China | https://registry.npmmirror.com/page-agent/1.4.2/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)。
+119 -150
View File
@@ -1,12 +1,12 @@
{
"name": "root",
"version": "1.4.2",
"version": "1.4.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "root",
"version": "1.4.2",
"version": "1.4.4",
"license": "MIT",
"workspaces": [
"packages/page-controller",
@@ -21,10 +21,10 @@
"@commitlint/cli": "^20.4.2",
"@commitlint/config-conventional": "^20.4.2",
"@eslint/js": "^9.39.2",
"@microsoft/api-extractor": "^7.57.3",
"@microsoft/api-extractor": "^7.57.6",
"@tailwindcss/vite": "^4.2.1",
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/node": "^25.3.0",
"@types/node": "^25.3.3",
"@vitejs/plugin-react-swc": "^4.1.0",
"chalk": "^5.6.2",
"concurrently": "^9.2.1",
@@ -35,16 +35,16 @@
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"eslint-plugin-react-x": "^2.13.0",
"globals": "^17.0.0",
"globals": "^17.4.0",
"husky": "^9.1.7",
"lint-staged": "^16.2.4",
"lint-staged": "^16.3.1",
"prettier": "^3.8.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.56.1",
"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"
"vite-plugin-css-injected-by-js": "^4.0.1"
},
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
@@ -1685,19 +1685,19 @@
}
},
"node_modules/@microsoft/api-extractor": {
"version": "7.57.3",
"resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.57.3.tgz",
"integrity": "sha512-2+k2FNp+6zug/VmpK0wZFPQu7cKMihjHBP1iUuZs6Ep5P9uR1hD4dR5Ss7z/MiBkwVmKnUm6Pojhkz/c431SMw==",
"version": "7.57.6",
"resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.57.6.tgz",
"integrity": "sha512-0rFv/D8Grzw1Mjs2+8NGUR+o4h9LVm5zKRtMeWnpdB5IMJF4TeHCL1zR5LMCIudkOvyvjbhMG5Wjs0B5nqsrRQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@microsoft/api-extractor-model": "7.33.1",
"@microsoft/api-extractor-model": "7.33.4",
"@microsoft/tsdoc": "~0.16.0",
"@microsoft/tsdoc-config": "~0.18.0",
"@rushstack/node-core-library": "5.20.1",
"@rushstack/rig-package": "0.7.1",
"@rushstack/terminal": "0.22.1",
"@rushstack/ts-command-line": "5.3.1",
"@microsoft/tsdoc-config": "~0.18.1",
"@rushstack/node-core-library": "5.20.3",
"@rushstack/rig-package": "0.7.2",
"@rushstack/terminal": "0.22.3",
"@rushstack/ts-command-line": "5.3.3",
"diff": "~8.0.2",
"lodash": "~4.17.23",
"minimatch": "10.2.1",
@@ -1711,15 +1711,15 @@
}
},
"node_modules/@microsoft/api-extractor-model": {
"version": "7.33.1",
"resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.33.1.tgz",
"integrity": "sha512-KX0LI6xzI0gcBOXXmr5mnnbdhsK2W93pqvJo8OgJgWvRRh+wMEp0Ccj38h1XKeJ29E1tuAZKSUOfHUQ1WA8fZg==",
"version": "7.33.4",
"resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.33.4.tgz",
"integrity": "sha512-u1LTaNTikZAQ9uK6KG1Ms7nvNedsnODnspq/gH2dcyETWvH4hVNGNDvRAEutH66kAmxA4/necElqGNs1FggC8w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@microsoft/tsdoc": "~0.16.0",
"@microsoft/tsdoc-config": "~0.18.0",
"@rushstack/node-core-library": "5.20.1"
"@microsoft/tsdoc-config": "~0.18.1",
"@rushstack/node-core-library": "5.20.3"
}
},
"node_modules/@microsoft/api-extractor/node_modules/lru-cache": {
@@ -1766,35 +1766,18 @@
"license": "MIT"
},
"node_modules/@microsoft/tsdoc-config": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.0.tgz",
"integrity": "sha512-8N/vClYyfOH+l4fLkkr9+myAoR6M7akc8ntBJ4DJdWH2b09uVfr71+LTMpNyG19fNqWDg8KEDZhx5wxuqHyGjw==",
"version": "0.18.1",
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.1.tgz",
"integrity": "sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@microsoft/tsdoc": "0.16.0",
"ajv": "~8.12.0",
"ajv": "~8.18.0",
"jju": "~1.4.0",
"resolve": "~1.22.2"
}
},
"node_modules/@microsoft/tsdoc-config/node_modules/ajv": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -2948,13 +2931,13 @@
]
},
"node_modules/@rushstack/node-core-library": {
"version": "5.20.1",
"resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.20.1.tgz",
"integrity": "sha512-QvxZyh+RsTJ77JpQkS9K9lJujh6lj5WyMxieT0bdACtwqxEkGB9zCuSMX5UlXRweaIgSpu1ztdHmhV07fKUpMg==",
"version": "5.20.3",
"resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.20.3.tgz",
"integrity": "sha512-95JgEPq2k7tHxhF9/OJnnyHDXfC9cLhhta0An/6MlkDsX2A6dTzDrTUG18vx4vjc280V0fi0xDH9iQczpSuWsw==",
"dev": true,
"license": "MIT",
"dependencies": {
"ajv": "~8.13.0",
"ajv": "~8.18.0",
"ajv-draft-04": "~1.0.0",
"ajv-formats": "~3.0.1",
"fs-extra": "~11.3.0",
@@ -2972,23 +2955,6 @@
}
}
},
"node_modules/@rushstack/node-core-library/node_modules/ajv": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz",
"integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.4.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@rushstack/node-core-library/node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -3041,9 +3007,9 @@
}
},
"node_modules/@rushstack/rig-package": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.7.1.tgz",
"integrity": "sha512-hLwDnp4yMcAd/gcUol8NPWNctpIXzVOgMyhZ8DagnEJls9TOZd0xF//5hS+YTiX7/+4rLfBra+NoB3rtFxjDdA==",
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.7.2.tgz",
"integrity": "sha512-9XbFWuqMYcHUso4mnETfhGVUSaADBRj6HUAAEYk50nMPn8WRICmBuCphycQGNB3duIR6EEZX3Xj3SYc2XiP+9A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3052,13 +3018,13 @@
}
},
"node_modules/@rushstack/terminal": {
"version": "0.22.1",
"resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.22.1.tgz",
"integrity": "sha512-Mdtu0VN7v31O5Zcno8ZZH5kQHF13Ez7WN9Aio7nFJVcR36i4bkERionYrWgBDQJ0JdVPLKGecZER/xRU5IvGLw==",
"version": "0.22.3",
"resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.22.3.tgz",
"integrity": "sha512-gHC9pIMrUPzAbBiI4VZMU7Q+rsCzb8hJl36lFIulIzoceKotyKL3Rd76AZ2CryCTKEg+0bnTj406HE5YY5OQvw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rushstack/node-core-library": "5.20.1",
"@rushstack/node-core-library": "5.20.3",
"@rushstack/problem-matcher": "0.2.1",
"supports-color": "~8.1.1"
},
@@ -3072,13 +3038,13 @@
}
},
"node_modules/@rushstack/ts-command-line": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.3.1.tgz",
"integrity": "sha512-mid/JIZSJafwy3x9e4v0wVLuAqSSYYErEHV0HXPALYLSBN13YNkR5caOk0hf97lSRKrxhtvQjGaDKSEelR3sMg==",
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.3.3.tgz",
"integrity": "sha512-c+ltdcvC7ym+10lhwR/vWiOhsrm/bP3By2VsFcs5qTKv+6tTmxgbVrtJ5NdNjANiV5TcmOZgUN+5KYQ4llsvEw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rushstack/terminal": "0.22.1",
"@rushstack/terminal": "0.22.3",
"@types/argparse": "1.0.38",
"argparse": "~1.0.9",
"string-argv": "~0.3.1"
@@ -3822,9 +3788,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.3.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz",
"integrity": "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==",
"version": "25.3.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz",
"integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4309,9 +4275,9 @@
"license": "MIT"
},
"node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6436,13 +6402,13 @@
}
},
"node_modules/framer-motion": {
"version": "12.34.3",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.34.3.tgz",
"integrity": "sha512-v81ecyZKYO/DfpTwHivqkxSUBzvceOpoI+wLfgCgoUIKxlFKEXdg0oR9imxwXumT4SFy8vRk9xzJ5l3/Du/55Q==",
"version": "12.34.5",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.34.5.tgz",
"integrity": "sha512-Z2dQ+o7BsfpJI3+u0SQUNCrN+ajCKJen1blC4rCHx1Ta2EOHs+xKJegLT2aaD9iSMbU3OoX+WabQXkloUbZmJQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"motion-dom": "^12.34.3",
"motion-dom": "^12.34.5",
"motion-utils": "^12.29.2",
"tslib": "^2.4.0"
},
@@ -6688,9 +6654,9 @@
}
},
"node_modules/globals": {
"version": "17.3.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz",
"integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==",
"version": "17.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz",
"integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -7775,19 +7741,18 @@
}
},
"node_modules/lint-staged": {
"version": "16.2.7",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz",
"integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==",
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.3.1.tgz",
"integrity": "sha512-bqvvquXzFBAlSbluugR4KXAe4XnO/QZcKVszpkBtqLWa2KEiVy8n6Xp38OeUbv/gOJOX4Vo9u5pFt/ADvbm42Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"commander": "^14.0.2",
"commander": "^14.0.3",
"listr2": "^9.0.5",
"micromatch": "^4.0.8",
"nano-spawn": "^2.0.0",
"pidtree": "^0.6.0",
"string-argv": "^0.3.2",
"yaml": "^2.8.1"
"tinyexec": "^1.0.2",
"yaml": "^2.8.2"
},
"bin": {
"lint-staged": "bin/lint-staged.js"
@@ -7962,9 +7927,9 @@
}
},
"node_modules/lucide-react": {
"version": "0.575.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.575.0.tgz",
"integrity": "sha512-VuXgKZrk0uiDlWjGGXmKV6MSk9Yy4l10qgVvzGn2AWBx1Ylt0iBexKOAoA6I7JO3m+M9oeovJd3yYENfkUbOeg==",
"version": "0.576.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.576.0.tgz",
"integrity": "sha512-koNxU14BXrxUfZQ9cUaP0ES1uyPZKYDjk31FQZB6dQ/x+tXk979sVAn9ppZ/pVeJJyOxVM8j1E+8QEuSc02Vug==",
"dev": true,
"license": "ISC",
"peerDependencies": {
@@ -8156,13 +8121,13 @@
}
},
"node_modules/motion": {
"version": "12.34.3",
"resolved": "https://registry.npmjs.org/motion/-/motion-12.34.3.tgz",
"integrity": "sha512-xZIkBGO7v/Uvm+EyaqYd+9IpXu0sZqLywVlGdCFrrMiaO9JI4Kx51mO9KlHSWwll+gZUVY5OJsWgYI5FywJ/tw==",
"version": "12.34.5",
"resolved": "https://registry.npmjs.org/motion/-/motion-12.34.5.tgz",
"integrity": "sha512-N06NLJ9IeBHeielRqIvYvjPfXuRdyTxa+9++BgpGa+hY2D7TcMkI6QzV3jaRuv0aZRXgMa7cPy9YcBUBisPzAQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"framer-motion": "^12.34.3",
"framer-motion": "^12.34.5",
"tslib": "^2.4.0"
},
"peerDependencies": {
@@ -8183,9 +8148,9 @@
}
},
"node_modules/motion-dom": {
"version": "12.34.3",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.34.3.tgz",
"integrity": "sha512-sYgFe+pR9aIM7o4fhs2aXtOI+oqlUd33N9Yoxcgo1Fv7M20sRkHtCmzE/VRNIcq7uNJ+qio+Xubt1FXH3pQ+eQ==",
"version": "12.34.5",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.34.5.tgz",
"integrity": "sha512-k33CsnxO2K3gBRMUZT+vPmc4Utlb5menKdG0RyVNLtlqRaaJPRWlE9fXl8NTtfZ5z3G8TDvqSu0MENLqSTaHZA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8710,19 +8675,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pidtree": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
"dev": true,
"license": "MIT",
"bin": {
"pidtree": "bin/pidtree.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/pino": {
"version": "9.7.0",
"resolved": "https://registry.npmjs.org/pino/-/pino-9.7.0.tgz",
@@ -9560,9 +9512,9 @@
}
},
"node_modules/simple-icons": {
"version": "16.9.0",
"resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-16.9.0.tgz",
"integrity": "sha512-aKst2C7cLkFyaiQ/Crlwxt9xYOpGPk05XuJZ0ZTJNNCzHCKYrGWz2ebJSi5dG8CmTCxUF/BGs6A8uyJn/EQxqw==",
"version": "16.10.0",
"resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-16.10.0.tgz",
"integrity": "sha512-62kuxaG3pE+cFNerudUtwb9BLmudzayHrlHkvU9gf8Nxcj7VYOm9dh3WNbGaFk60aQtfnRyzViZaouFG2B45kg==",
"dev": true,
"funding": [
{
@@ -10554,9 +10506,9 @@
}
},
"node_modules/vite-plugin-css-injected-by-js": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.5.2.tgz",
"integrity": "sha512-2MpU/Y+SCZyWUB6ua3HbJCrgnF0KACAsmzOQt1UvRVJCGF6S8xdA3ZUhWcWdM9ivG4I5az8PnQmwwrkC2CAQrQ==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-4.0.1.tgz",
"integrity": "sha512-WfyRojojQyAO/KzWf+efcXpTPv6zJPXaRmr9EYq5a4v5I3PWCR7kR01hiri2lW6+rHm3a57kpwsf+iahIJi1Qw==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -11161,13 +11113,18 @@
},
"packages/core": {
"name": "@page-agent/core",
"version": "1.4.2",
"version": "1.4.4",
"license": "MIT",
"dependencies": {
"@page-agent/llms": "1.4.2",
"@page-agent/page-controller": "1.4.2",
"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": {
@@ -11175,13 +11132,12 @@
"version": "0.1.8",
"hasInstallScript": true,
"dependencies": {
"@page-agent/core": "1.4.2",
"@page-agent/llms": "1.4.2",
"@page-agent/page-controller": "1.4.2",
"@page-agent/ui": "1.4.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",
"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",
@@ -11198,44 +11154,57 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"idb": "^8.0.3",
"lucide-react": "^0.575.0",
"motion": "^12.34.3",
"lucide-react": "^0.576.0",
"motion": "^12.34.5",
"next-themes": "^0.4.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"rough-notation": "^0.5.1",
"simple-icons": "^16.9.0",
"simple-icons": "^16.10.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0",
"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.2",
"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.2",
"version": "1.4.4",
"license": "MIT",
"dependencies": {
"@page-agent/core": "1.4.2",
"@page-agent/llms": "1.4.2",
"@page-agent/page-controller": "1.4.2",
"@page-agent/ui": "1.4.2",
"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.2",
"version": "1.4.4",
"license": "MIT",
"dependencies": {
"ai-motion": "^0.4.8"
@@ -11243,12 +11212,12 @@
},
"packages/ui": {
"name": "@page-agent/ui",
"version": "1.4.2",
"version": "1.4.4",
"license": "MIT"
},
"packages/website": {
"name": "@page-agent/website",
"version": "1.4.2",
"version": "1.4.4",
"devDependencies": {
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-separator": "^1.1.8",
@@ -11259,13 +11228,13 @@
"@types/react-dom": "^19.2.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.575.0",
"motion": "^12.34.3",
"lucide-react": "^0.576.0",
"motion": "^12.34.5",
"next-themes": "^0.4.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"rough-notation": "^0.5.1",
"simple-icons": "^16.9.0",
"simple-icons": "^16.10.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.1.14",
+6 -6
View File
@@ -1,7 +1,7 @@
{
"name": "root",
"private": true,
"version": "1.4.2",
"version": "1.4.4",
"type": "module",
"workspaces": [
"packages/page-controller",
@@ -38,10 +38,10 @@
"@commitlint/cli": "^20.4.2",
"@commitlint/config-conventional": "^20.4.2",
"@eslint/js": "^9.39.2",
"@microsoft/api-extractor": "^7.57.3",
"@microsoft/api-extractor": "^7.57.6",
"@tailwindcss/vite": "^4.2.1",
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/node": "^25.3.0",
"@types/node": "^25.3.3",
"@vitejs/plugin-react-swc": "^4.1.0",
"chalk": "^5.6.2",
"concurrently": "^9.2.1",
@@ -52,15 +52,15 @@
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"eslint-plugin-react-x": "^2.13.0",
"globals": "^17.0.0",
"globals": "^17.4.0",
"husky": "^9.1.7",
"lint-staged": "^16.2.4",
"lint-staged": "^16.3.1",
"prettier": "^3.8.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.56.1",
"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": "^4.0.1",
"vite-bundle-analyzer": "^1.3.6"
},
"overrides": {
+9 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@page-agent/core",
"private": false,
"version": "1.4.2",
"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.2",
"@page-agent/page-controller": "1.4.2"
"@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"
}
}
+20 -14
View File
@@ -5,14 +5,13 @@
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 { type PageAgentConfig, type SupportedLanguage } from './config'
import { DEFAULT_MAX_STEPS } from './config/constants'
import SYSTEM_PROMPT from './prompts/system_prompt.md?raw'
import { tools } from './tools'
import type {
AgentActivity,
AgentConfig,
AgentReflection,
AgentStatus,
AgentStepEvent,
@@ -21,13 +20,13 @@ import type {
MacroToolInput,
MacroToolResult,
} from './types'
import { assert, normalizeResponse, uid, waitFor } from './utils'
import { assert, fetchLlmsTxt, normalizeResponse, uid, waitFor } from './utils'
export { type PageAgentConfig }
export type { SupportedLanguage }
export { tool, type PageAgentTool } from './tools'
export type * from './types'
export type PageAgentCoreConfig = AgentConfig & { pageController: PageController }
/**
* AI agent for browser automation.
*
@@ -60,7 +59,7 @@ export type * from './types'
*/
export class PageAgentCore extends EventTarget {
readonly id = uid()
readonly config: PageAgentConfig & { maxSteps: number }
readonly config: PageAgentCoreConfig & { maxSteps: number }
readonly tools: typeof tools
/** PageController for DOM operations */
readonly pageController: PageController
@@ -94,10 +93,10 @@ export class PageAgentCore extends EventTarget {
browserState: null as BrowserState | null,
}
constructor(config: PageAgentConfig & { pageController: PageController }) {
constructor(config: PageAgentCoreConfig) {
super()
this.config = { ...config, maxSteps: config.maxSteps || DEFAULT_MAX_STEPS }
this.config = { ...config, maxSteps: config.maxSteps || 40 }
this.#llm = new LLM(this.config)
this.tools = new Map(tools)
@@ -222,6 +221,7 @@ export class PageAgentCore extends EventTarget {
this.history = []
this.#setStatus('running')
this.#emitHistoryChange()
this.#observations = []
// Reset internal states
this.#states = { totalWaitTime: 0, lastURL: '', browserState: null }
@@ -462,14 +462,13 @@ export class PageAgentCore extends EventTarget {
* Get instructions from config
*/
async #getInstructions(): Promise<string> {
const { instructions } = this.config
if (!instructions) return ''
const { instructions, experimentalLlmsTxt } = this.config
const systemInstructions = instructions.system?.trim()
const systemInstructions = instructions?.system?.trim()
let pageInstructions: string | undefined
const url = this.#states.browserState?.url || ''
if (instructions.getPageInstructions && url) {
if (instructions?.getPageInstructions && url) {
try {
pageInstructions = instructions.getPageInstructions(url)?.trim()
} catch (error) {
@@ -479,7 +478,10 @@ export class PageAgentCore extends EventTarget {
)
}
}
if (!systemInstructions && !pageInstructions) return ''
const llmsTxt = experimentalLlmsTxt && url ? await fetchLlmsTxt(url) : undefined
if (!systemInstructions && !pageInstructions && !llmsTxt) return ''
let result = '<instructions>\n'
@@ -491,6 +493,10 @@ export class PageAgentCore extends EventTarget {
result += `<page_instructions>\n${pageInstructions}\n</page_instructions>\n`
}
if (llmsTxt) {
result += `<llms_txt>\n${llmsTxt}\n</llms_txt>\n`
}
result += '</instructions>\n\n'
return result
-1
View File
@@ -1 +0,0 @@
export const DEFAULT_MAX_STEPS = 20
-152
View File
@@ -1,152 +0,0 @@
import type { LLMConfig } from '@page-agent/llms'
import type { PageControllerConfig } from '@page-agent/page-controller'
import type { PageAgentCore } from '../PageAgentCore'
import type { PageAgentTool } from '../tools'
import type { ExecutionResult, HistoricalEvent } from '../types'
export type { LLMConfig }
/** Supported UI languages */
export type SupportedLanguage = 'en-US' | 'zh-CN'
export interface AgentConfig {
// theme?: 'light' | 'dark'
language?: SupportedLanguage
/**
* Maximum number of steps the agent can take per task.
* @default 20
*/
maxSteps?: number
/**
* Custom tools to extend PageAgent capabilities
* @experimental
* @note You can also override or remove internal tools by using the same name.
* @see PageAgentTool
*
* @example
* // override internal tool
* 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(),
* }),
* execute: async function (this: PageAgent, input) {
* const answer = await do_some_thing(input.question)
* return "✅ Received user answer: " + answer
* },
* })
* }
*
* @example
* // remove internal tool
* const customTools = {
* ask_user: null // never ask user questions
* }
*/
customTools?: Record<string, PageAgentTool | null>
/**
* Instructions to guide the agent's behavior
*/
instructions?: {
/**
* Global system-level instructions, applied to all tasks
*/
system?: string
/**
* Dynamic page-level instructions callback
* Called before each step to get instructions for the current page
* @param url - Current page URL (window.location.href)
* @returns Instructions string, or undefined/null to skip
*/
getPageInstructions?: (url: string) => string | undefined | null
}
/**
* Lifecycle hooks for task execution.
* @experimental API may change in future versions.
*
* All hooks receive the agent instance as first parameter.
*/
/**
* Called before each step execution.
* @experimental
* @param agent - The PageAgentCore instance
* @param stepCount - Current step number (0-indexed)
*/
onBeforeStep?: (agent: PageAgentCore, stepCount: number) => Promise<void> | void
/**
* Called after each step execution.
* @experimental
* @param agent - The PageAgentCore instance
* @param history - Current history of events
*/
onAfterStep?: (agent: PageAgentCore, history: HistoricalEvent[]) => Promise<void> | void
/**
* Called before task execution starts.
* @experimental
* @param agent - The PageAgentCore instance
*/
onBeforeTask?: (agent: PageAgentCore) => Promise<void> | void
/**
* Called after task execution completes (success or failure).
* @experimental
* @param agent - The PageAgentCore instance
* @param result - The execution result
*/
onAfterTask?: (agent: PageAgentCore, result: ExecutionResult) => Promise<void> | void
/**
* Called when the agent is disposed.
* @experimental
* @note This hook can block the disposal process if it's async.
* @param agent - The PageAgentCore instance
* @param reason - Optional reason for disposal
*/
onDispose?: (agent: PageAgentCore, reason?: string) => void
// page behavior hooks
/**
* @experimental
* Enable the experimental script execution tool that allows executing generated JavaScript code on the page.
* @note Can cause unpredictable side effects.
* @note May bypass some safe guards and data-masking mechanisms.
*/
experimentalScriptExecutionTool?: boolean
/**
* Transform page content before sending to LLM.
* Called after DOM extraction and simplification, before LLM invocation.
* Use cases: inspect extraction results, modify page info, mask sensitive data.
*
* @param content - Simplified page content that will be sent to LLM
* @returns Transformed content
*
* @example
* // Mask phone numbers
* transformPageContent: async (content) => {
* return content.replace(/1[3-9]\d{9}/g, '***********')
* }
*/
transformPageContent?: (content: string) => Promise<string> | string
/**
* Completely override the default system prompt.
* @experimental Use with caution - incorrect prompts may break agent behavior.
*/
customSystemPrompt?: string
}
export type PageAgentConfig = LLMConfig & AgentConfig & PageControllerConfig
+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'
+156
View File
@@ -1,3 +1,159 @@
import type { LLMConfig } from '@page-agent/llms'
// @note circular dependency but okay
import type { PageAgentCore } from './PageAgentCore'
import type { PageAgentTool } from './tools'
/** Supported UI languages */
export type SupportedLanguage = 'en-US' | 'zh-CN'
export interface AgentConfig extends LLMConfig {
language?: SupportedLanguage
/**
* Maximum number of steps the agent can take per task.
* @default 40
*/
maxSteps?: number
/**
* Custom tools to extend PageAgent capabilities
* @experimental
* @note You can also override or remove internal tools by using the same name.
* @see PageAgentTool
*
* @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: z.object({
* question: z.string(),
* }),
* execute: async function (this: PageAgent, input) {
* const answer = await do_some_thing(input.question)
* return "✅ Received user answer: " + answer
* },
* })
* }
*
* @example
* // remove internal tool
* const customTools = {
* ask_user: null // never ask user questions
* }
*/
customTools?: Record<string, PageAgentTool | null>
/**
* Instructions to guide the agent's behavior
*/
instructions?: {
/**
* Global system-level instructions, applied to all tasks
*/
system?: string
/**
* Dynamic page-level instructions callback
* Called before each step to get instructions for the current page
* @param url - Current page URL (window.location.href)
* @returns Instructions string, or undefined/null to skip
*/
getPageInstructions?: (url: string) => string | undefined | null
}
/**
* Lifecycle hooks for task execution.
* @experimental API may change in future versions.
*
* All hooks receive the agent instance as first parameter.
*/
/**
* Called before each step execution.
* @experimental
* @param agent - The PageAgentCore instance
* @param stepCount - Current step number (0-indexed)
*/
onBeforeStep?: (agent: PageAgentCore, stepCount: number) => Promise<void> | void
/**
* Called after each step execution.
* @experimental
* @param agent - The PageAgentCore instance
* @param history - Current history of events
*/
onAfterStep?: (agent: PageAgentCore, history: HistoricalEvent[]) => Promise<void> | void
/**
* Called before task execution starts.
* @experimental
* @param agent - The PageAgentCore instance
*/
onBeforeTask?: (agent: PageAgentCore) => Promise<void> | void
/**
* Called after task execution completes (success or failure).
* @experimental
* @param agent - The PageAgentCore instance
* @param result - The execution result
*/
onAfterTask?: (agent: PageAgentCore, result: ExecutionResult) => Promise<void> | void
/**
* Called when the agent is disposed.
* @experimental
* @note This hook can block the disposal process if it's async.
* @param agent - The PageAgentCore instance
* @param reason - Optional reason for disposal
*/
onDispose?: (agent: PageAgentCore, reason?: string) => void
// page behavior hooks
/**
* @experimental
* Enable the experimental script execution tool that allows executing generated JavaScript code on the page.
* @note Can cause unpredictable side effects.
* @note May bypass some safe guards and data-masking mechanisms.
*/
experimentalScriptExecutionTool?: boolean
/**
* @experimental
* Fetch /llms.txt from current site origin and include as context.
* Only fetched once per origin per task.
* @default false
*/
experimentalLlmsTxt?: boolean
/**
* Transform page content before sending to LLM.
* Called after DOM extraction and simplification, before LLM invocation.
* Use cases: inspect extraction results, modify page info, mask sensitive data.
*
* @param content - Simplified page content that will be sent to LLM
* @returns Transformed content
*
* @example
* // Mask phone numbers
* transformPageContent: async (content) => {
* return content.replace(/1[3-9]\d{9}/g, '***********')
* }
*/
transformPageContent?: (content: string) => Promise<string> | string
/**
* Completely override the default system prompt.
* @experimental Use with caution - incorrect prompts may break agent behavior.
*/
customSystemPrompt?: string
}
/**
* Agent reflection state - the reflection-before-action model
*
+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 }
}
}
+30 -1
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))
@@ -57,6 +57,35 @@ export function uid() {
return id
}
const llmsTxtCache = new Map<string, string | null>()
/** Fetch /llms.txt for a URL's origin. Cached per origin, `null` = tried and not found. */
export async function fetchLlmsTxt(url: string): Promise<string | null> {
const origin = new URL(url).origin
if (llmsTxtCache.has(origin)) return llmsTxtCache.get(origin)!
const endpoint = `${origin}/llms.txt`
let result: string | null = null
try {
console.log(chalk.gray(`[llms.txt] Fetching ${endpoint}`))
const res = await fetch(endpoint, { signal: AbortSignal.timeout(3000) })
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.debug(chalk.gray(`[llms.txt] ${res.status} for ${endpoint}`))
}
} catch (e) {
console.debug(chalk.gray(`[llms.txt] not found for ${endpoint}`), e)
}
llmsTxtCache.set(origin, result)
return result
}
/**
* Simple assertion function that throws an error if the condition is falsy
* @param condition - The condition to assert
+1
View File
@@ -30,6 +30,7 @@ export default defineConfig({
external: [
'chalk',
'zod',
'zod/v4',
// all the internal packages
/^@page-agent\//,
],
+11 -9
View File
@@ -24,13 +24,13 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"idb": "^8.0.3",
"lucide-react": "^0.575.0",
"motion": "^12.34.3",
"lucide-react": "^0.576.0",
"motion": "^12.34.5",
"next-themes": "^0.4.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"rough-notation": "^0.5.1",
"simple-icons": "^16.9.0",
"simple-icons": "^16.10.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.1.14",
@@ -38,12 +38,14 @@
"wxt": "^0.20.18"
},
"dependencies": {
"@page-agent/core": "1.4.2",
"@page-agent/llms": "1.4.2",
"@page-agent/page-controller": "1.4.2",
"@page-agent/ui": "1.4.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",
"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.2",
"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.2",
"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.2",
"@page-agent/llms": "1.4.2",
"@page-agent/page-controller": "1.4.2",
"@page-agent/ui": "1.4.2",
"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"
}
}
+5 -4
View File
@@ -2,12 +2,13 @@
* Copyright (C) 2025 Alibaba Group Holding Limited
* All rights reserved.
*/
import { type PageAgentConfig, PageAgentCore, type PageAgentTool, tool } from '@page-agent/core'
import { PageController } from '@page-agent/page-controller'
import { type AgentConfig, PageAgentCore } from '@page-agent/core'
import { PageController, type PageControllerConfig } from '@page-agent/page-controller'
import { Panel } from '@page-agent/ui'
export type { PageAgentConfig, PageAgentTool }
export { tool }
export * from '@page-agent/core'
export type PageAgentConfig = AgentConfig & PageControllerConfig
export class PageAgent extends PageAgentCore {
panel: Panel
+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.2",
"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.2",
"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
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@page-agent/website",
"private": true,
"version": "1.4.2",
"version": "1.4.4",
"type": "module",
"scripts": {
"dev": "vite --host 0.0.0.0",
@@ -19,13 +19,13 @@
"@types/react-dom": "^19.2.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.575.0",
"motion": "^12.34.3",
"lucide-react": "^0.576.0",
"motion": "^12.34.5",
"next-themes": "^0.4.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"rough-notation": "^0.5.1",
"simple-icons": "^16.9.0",
"simple-icons": "^16.10.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.1.14",
@@ -5,10 +5,9 @@
*/
import * as React from 'react'
import { Badge } from '@/components/ui/badge'
import { cn } from '@/lib/utils'
import { Badge } from './badge'
// ============================================================================
// Types
// ============================================================================
@@ -35,6 +34,8 @@ export interface APIReferenceProps {
description?: React.ReactNode
/** Property definitions */
properties: PropDefinition[]
/** Display variant: 'properties' for fields, 'methods' for methods */
variant?: 'properties' | 'methods'
/** Additional CSS classes */
className?: string
}
@@ -43,7 +44,14 @@ export interface APIReferenceProps {
// Component
// ============================================================================
export function APIReference({ title, description, properties, className }: APIReferenceProps) {
export function APIReference({
title,
description,
properties,
variant = 'properties',
className,
}: APIReferenceProps) {
const isMethodsVariant = variant === 'methods'
return (
<div className={cn('my-6', className)}>
{title && (
@@ -58,10 +66,10 @@ export function APIReference({ title, description, properties, className }: APIR
<thead>
<tr className="bg-gray-50 dark:bg-gray-800/50">
<th className="px-4 py-3 text-left font-medium text-gray-600 dark:text-gray-300">
Property
{isMethodsVariant ? 'Method' : 'Property'}
</th>
<th className="px-4 py-3 text-left font-medium text-gray-600 dark:text-gray-300">
Type
{isMethodsVariant ? 'Return Type' : 'Type'}
</th>
<th className="px-4 py-3 text-left font-medium text-gray-600 dark:text-gray-300 hidden md:table-cell">
Default
@@ -120,7 +128,7 @@ function PropRow({ name, type, required, defaultValue, description, status }: Pr
{/* Type */}
<td className="px-4 py-3 align-top">
<code className="font-mono text-xs text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded whitespace-nowrap">
<code className="font-mono text-xs text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded wrap-break-word">
{type}
</code>
</td>
@@ -42,7 +42,7 @@ const CodeEditor: React.FC<CodeEditorProps> = ({
return (
<div
className={`group relative ${containerClasses} rounded-xl border shadow-2xl overflow-hidden ${className}`}
className={`group relative ${containerClasses} rounded-xl border shadow-2xl my-4 overflow-hidden ${className}`}
>
{/* 编辑器顶部栏 */}
{showHeader && (
+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.2/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.2/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'
+3 -6
View File
@@ -34,10 +34,6 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
title: isZh ? '故障排查' : 'Troubleshooting',
path: '/introduction/troubleshooting',
},
{
title: '🚧 ' + (isZh ? '最佳实践' : 'Best Practices'),
path: '/integration/best-practices',
},
],
},
{
@@ -50,7 +46,7 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
{ title: isZh ? 'Chrome 扩展' : 'Chrome Extension', path: '/features/chrome-extension' },
{
title: isZh ? '接入第三方 Agent' : 'Third-party Agent',
path: '/integration/third-party-agent',
path: '/features/third-party-agent',
},
],
},
@@ -59,10 +55,11 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
items: [
{ title: 'PageAgent', path: '/advanced/page-agent' },
{ title: 'PageAgentCore', path: '/advanced/page-agent-core' },
{ title: 'PageController', path: '/advanced/page-controller' },
{ title: isZh ? '自定义 UI' : 'Custom UI', path: '/advanced/custom-ui' },
{
title: '🚧 ' + (isZh ? '安全与权限' : 'Security & Permissions'),
path: '/integration/security-permissions',
path: '/advanced/security-permissions',
},
],
},
@@ -1,6 +1,6 @@
import { APIDivider, APIReference } from '@/components/APIReference'
import CodeEditor from '@/components/CodeEditor'
import { Heading } from '@/components/Heading'
import { APIDivider, APIReference } from '@/components/ui/api-reference'
import { useLanguage } from '@/i18n/context'
export default function CustomUIDocs() {
@@ -1,6 +1,8 @@
import { Link } from 'wouter'
import { APIDivider, APIReference, TypeRef } from '@/components/APIReference'
import CodeEditor from '@/components/CodeEditor'
import { Heading } from '@/components/Heading'
import { APIDivider, APIReference, TypeRef } from '@/components/ui/api-reference'
import { useLanguage } from '@/i18n/context'
export default function PageAgentCoreDocs() {
@@ -50,19 +52,13 @@ const agent = new PageAgentCore({
baseURL: 'https://api.openai.com/v1',
apiKey: 'your-api-key',
model: 'gpt-5.2',
language: 'en-US',
})
// Listen to events for UI display
agent.addEventListener('statuschange', () => {
console.log('Status:', agent.status)
})
agent.addEventListener('historychange', () => {
console.log('History:', agent.history)
})
agent.addEventListener('activity', (e) => {
const activity = (e as CustomEvent).detail
console.log('Activity:', activity.type)
@@ -75,14 +71,54 @@ const result = await agent.execute('Fill in the form with test data')`}
<APIDivider title={isZh ? '配置' : 'Configuration'} />
{/* LLM Configuration */}
{/* Configuration */}
<section className="mb-10">
<Heading id="llmconfig">LLMConfig</Heading>
<Heading id="configuration">PageAgentCoreConfig</Heading>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh
? '配置与大语言模型的连接参数。支持 OpenAI 兼容的 API。'
: 'Configure connection parameters for the language model. Supports OpenAI-compatible APIs.'}
? 'PageAgentCoreConfig = AgentConfig & { pageController: PageController }。AgentConfig 包含以下配置项:'
: 'PageAgentCoreConfig = AgentConfig & { pageController: PageController }. AgentConfig contains the following options:'}
</p>
{/* PageController */}
<h3 className="text-lg font-semibold mt-6 mb-3 text-gray-800 dark:text-gray-200">
PageController
</h3>
<APIReference
properties={[
{
name: 'pageController',
type: 'PageController',
required: true,
description: isZh ? (
<>
<Link
href="/advanced/page-controller"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
PageController
</Link>{' '}
DOM
</>
) : (
<>
<Link
href="/advanced/page-controller"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
PageController
</Link>{' '}
instance for DOM operations and element interaction.
</>
),
},
]}
/>
{/* LLM Config */}
<h3 className="text-lg font-semibold mt-6 mb-3 text-gray-800 dark:text-gray-200">
{isZh ? 'LLM 配置' : 'LLM Config'}
</h3>
<APIReference
properties={[
{
@@ -110,7 +146,6 @@ const result = await agent.execute('Fill in the form with test data')`}
{
name: 'temperature',
type: 'number',
defaultValue: '0',
description: isZh
? '模型温度参数,控制输出随机性'
: 'Model temperature, controls output randomness',
@@ -130,16 +165,11 @@ const result = await agent.execute('Fill in the form with test data')`}
},
]}
/>
</section>
{/* Agent Configuration */}
<section className="mb-10">
<Heading id="agentconfig">AgentConfig</Heading>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh
? '配置 Agent 的行为、生命周期钩子和扩展能力。'
: 'Configure agent behavior, lifecycle hooks, and extension capabilities.'}
</p>
{/* Agent Config */}
<h3 className="text-lg font-semibold mt-6 mb-3 text-gray-800 dark:text-gray-200">
{isZh ? 'Agent 配置' : 'Agent Config'}
</h3>
<APIReference
properties={[
{
@@ -151,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',
},
{
@@ -166,8 +196,8 @@ const result = await agent.execute('Fill in the form with test data')`}
name: 'instructions',
type: 'InstructionsConfig',
description: isZh
? '指导 Agent 行为的指令配置'
: 'Instructions to guide agent behavior',
? '指导 Agent 行为的指令配置,见下方类型定义'
: 'Instructions to guide agent behavior, see type definition below',
},
{
name: 'transformPageContent',
@@ -177,7 +207,15 @@ const result = await agent.execute('Fill in the form with test data')`}
: 'Transform page content before sending to LLM, useful for data masking',
},
{
name: 'experimentalScriptExecutionTool',
name: 'customSystemPrompt',
type: 'string',
status: 'experimental',
description: isZh
? '完全覆盖默认系统提示词。谨慎使用。'
: 'Completely override the default system prompt. Use with caution.',
},
{
name: 'experimentalScript\nExecutionTool',
type: 'boolean',
defaultValue: 'false',
status: 'experimental',
@@ -185,134 +223,58 @@ const result = await agent.execute('Fill in the form with test data')`}
? '启用实验性 JavaScript 执行工具'
: 'Enable experimental JavaScript execution tool',
},
]}
/>
<h3 className="text-lg font-semibold mt-6 mb-3">InstructionsConfig</h3>
<APIReference
properties={[
{
name: 'system',
type: 'string',
name: 'experimentalLlmsTxt',
type: 'boolean',
defaultValue: 'false',
status: 'experimental',
description: isZh
? '全局系统级指令,应用于所有任务'
: 'Global system-level instructions, applied to all tasks',
},
{
name: 'getPageInstructions',
type: '(url: string) => string | undefined | null',
description: isZh
? '动态页面级指令回调,在每个步骤前调用'
: 'Dynamic page-level instructions callback, called before each step',
? '从当前站点根目录获取 /llms.txt 并作为上下文提供给 LLM'
: 'Fetch /llms.txt from site origin and include as LLM context',
},
]}
/>
</section>
{/* Lifecycle Hooks */}
<section className="mb-10">
<Heading id="lifecycle-hooks">{isZh ? '生命周期钩子' : 'Lifecycle Hooks'}</Heading>
{/* Lifecycle Hooks */}
<h3 className="text-lg font-semibold mt-6 mb-3 text-gray-800 dark:text-gray-200">
{isZh ? '生命周期钩子' : 'Lifecycle Hooks'}
<span className="ml-2 text-xs font-normal text-amber-600 dark:text-amber-400">
experimental
</span>
</h3>
<div className="bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg p-4 mb-4">
<p className="text-amber-800 dark:text-amber-200 text-sm">
<strong> {isZh ? '警告' : 'Warning'}:</strong>{' '}
{isZh
? '这些接口高度实验性,可能在未来版本中发生变化。建议优先使用事件系统(Events)来监听 Agent 状态。'
: 'These APIs are highly experimental and may change in future versions. Prefer using the Events system for monitoring agent state.'}
? '这些接口高度实验性,可能在未来版本中发生变化。'
: 'These APIs are highly experimental and may change in future versions. '}
</p>
</div>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh
? '所有生命周期钩子都接收 agent 实例作为第一个参数,便于在回调中访问 Agent 状态和方法。'
: 'All lifecycle hooks receive the agent instance as first parameter, making it easy to access agent state and methods in callbacks.'}
</p>
<APIReference
properties={[
{
name: 'onBeforeStep',
type: '(agent: PageAgentCore, stepCount: number) => void | Promise<void>',
type: '(agent, stepCount) => void | Promise<void>',
description: isZh ? '每个步骤执行前调用' : 'Called before each step execution',
status: 'experimental',
},
{
name: 'onAfterStep',
type: '(agent: PageAgentCore, history: HistoricalEvent[]) => void | Promise<void>',
type: '(agent, history) => void | Promise<void>',
description: isZh ? '每个步骤执行后调用' : 'Called after each step execution',
status: 'experimental',
},
{
name: 'onBeforeTask',
type: '(agent: PageAgentCore) => void | Promise<void>',
type: '(agent) => void | Promise<void>',
description: isZh ? '任务开始前调用' : 'Called before task starts',
status: 'experimental',
},
{
name: 'onAfterTask',
type: '(agent: PageAgentCore, result: ExecutionResult) => void | Promise<void>',
type: '(agent, result) => void | Promise<void>',
description: isZh ? '任务结束后调用' : 'Called after task ends',
status: 'experimental',
},
{
name: 'onDispose',
type: '(agent: PageAgentCore, reason?: string) => void',
type: '(agent, reason?) => void',
description: isZh ? 'Agent 销毁时调用' : 'Called when agent is disposed',
status: 'experimental',
},
]}
/>
</section>
{/* PageController Configuration */}
<section className="mb-10">
<Heading id="pagecontrollerconfig">PageControllerConfig</Heading>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh
? '配置 DOM 提取、元素交互和视觉反馈。'
: 'Configure DOM extraction, element interaction, and visual feedback.'}
</p>
<APIReference
properties={[
{
name: 'pageController',
type: 'PageController',
status: 'experimental',
description: isZh
? '自定义 PageController 实例。如不提供,将创建默认实例。'
: 'Custom PageController instance. If not provided, a default one will be created.',
},
{
name: 'enableMask',
type: 'boolean',
defaultValue: 'true',
description: isZh
? '启用视觉遮罩覆盖层,阻止用户在自动化期间操作'
: 'Enable visual mask overlay that blocks user interaction during automation',
},
{
name: 'viewportExpansion',
type: 'number',
defaultValue: '0',
description: isZh
? '视口扩展像素数,-1 表示提取整个页面'
: 'Viewport expansion in pixels, -1 means extract entire page',
},
{
name: 'interactiveBlacklist',
type: '(Element | (() => Element))[]',
description: isZh ? '要排除的交互元素列表' : 'Elements to exclude from interaction',
},
{
name: 'interactiveWhitelist',
type: '(Element | (() => Element))[]',
description: isZh
? '要强制包含的交互元素列表'
: 'Elements to force include for interaction',
},
{
name: 'includeAttributes',
type: 'string[]',
description: isZh
? '在 DOM 提取中包含的额外属性'
: 'Additional attributes to include in DOM extraction',
},
]}
/>
@@ -369,16 +331,24 @@ const result = await agent.execute('Fill in the form with test data')`}
<section className="mb-10">
<Heading id="methods">{isZh ? '方法' : 'Methods'}</Heading>
<APIReference
variant="methods"
properties={[
{
name: 'execute(task: string)',
name: 'execute(task)',
type: 'Promise<ExecutionResult>',
description: isZh
? '执行任务并返回结果。包含 success、data 和 history 字段。'
: 'Execute a task and return result. Contains success, data, and history fields.',
},
{
name: 'dispose(reason?: string)',
name: 'stop()',
type: 'void',
description: isZh
? '停止当前任务。Agent 仍可复用。'
: 'Stop the current task. Agent remains reusable.',
},
{
name: 'dispose()',
type: 'void',
description: isZh
? '销毁 Agent 并清理资源'
@@ -443,11 +413,8 @@ const result = await agent.execute('Fill in the form with test data')`}
<CodeEditor
language="typescript"
code={`interface ExecutionResult {
/** Whether the task completed successfully */
success: boolean
/** Result description from the agent */
data: string
/** Full execution history */
history: HistoricalEvent[]
}`}
/>
@@ -467,50 +434,21 @@ const result = await agent.execute('Fill in the form with test data')`}
/>
</section>
<APIDivider title={isZh ? '无头模式' : 'Headless Mode'} />
{/* Headless Usage */}
{/* InstructionsConfig */}
<section className="mb-10">
<Heading id="headless-mode">{isZh ? '无头模式' : 'Headless Mode'}</Heading>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh
? '在非 DOM 环境中,你必须实现自定义的 PageController(例如远程操作页面或 Puppeteer)。'
: 'In non-DOM environments, you must implement a custom PageController (e.g., remote page control or Puppeteer).'}
</p>
<Heading id="instructionsconfig">InstructionsConfig</Heading>
<CodeEditor
language="typescript"
code={`import { PageAgentCore } from '@page-agent/core'
import type { PageController } from '@page-agent/page-controller'
code={`interface InstructionsConfig {
/** Global system-level instructions, applied to all tasks */
system?: string
class MyRemotePageController implements PageController {
// Implement required methods for DOM extraction and interaction
}
const agent = new PageAgentCore({
pageController: new MyRemotePageController(),
baseURL: 'https://api.openai.com/v1',
apiKey: 'your-api-key',
model: 'gpt-5.2',
language: 'en-US',
})
// Listen to events for UI display
agent.addEventListener('statuschange', () => {
console.log('Status:', agent.status)
})
agent.addEventListener('historychange', () => {
console.log('History:', agent.history)
})
agent.addEventListener('activity', (e) => {
const activity = (e as CustomEvent).detail
console.log('Activity:', activity.type)
})
// Execute task
const result = await agent.execute('Fill in the form with test data')`}
/**
* Dynamic page-level instructions callback.
* Called before each step to get instructions for the current page.
*/
getPageInstructions?: (url: string) => string | undefined
}`}
/>
</section>
</div>
@@ -2,7 +2,6 @@ import { Link } from 'wouter'
import CodeEditor from '@/components/CodeEditor'
import { Heading } from '@/components/Heading'
import { APIReference, TypeRef } from '@/components/ui/api-reference'
import { useLanguage } from '@/i18n/context'
export default function PageAgentDocs() {
@@ -14,8 +13,8 @@ export default function PageAgentDocs() {
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
{isZh
? 'PageAgent 是带有内置 UI 面板的完整 Agent 类。它继承自 PageAgentCore,并自动创建交互面板。'
: 'PageAgent is the complete Agent class with built-in UI panel. It extends PageAgentCore and automatically creates an interactive panel.'}
? 'PageAgent 是带有内置 UI 面板的完整 Agent 类。它继承自 PageAgentCore,并自动创建交互面板和 PageController。'
: 'PageAgent is the complete Agent class with built-in UI panel. It extends PageAgentCore and automatically creates an interactive panel and PageController.'}
</p>
{/* When to use */}
@@ -29,6 +28,11 @@ export default function PageAgentDocs() {
: 'In most cases, you should use PageAgent. It provides a complete out-of-the-box experience:'}
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-400 space-y-2 mb-6">
<li>
{isZh
? '自动创建 PageController,处理 DOM 提取和元素操作'
: 'Automatically creates PageController for DOM extraction and element actions'}
</li>
<li>
{isZh
? '内置 UI 面板,显示任务进度、Agent 思考过程和操作结果'
@@ -74,9 +78,8 @@ console.log(result.history) // Full execution history`}
<CodeEditor
language="typescript"
code={`class PageAgent extends PageAgentCore {
/** The UI panel instance */
panel: Panel
pageController: PageController
constructor(config: PageAgentConfig)
}`}
/>
@@ -90,7 +93,21 @@ console.log(result.history) // Full execution history`}
>
PageAgentCore
</Link>
API PageAgentCore
{' '}
<Link
href="/advanced/page-agent-core#configuration"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
AgentConfig
</Link>{' '}
{' '}
<Link
href="/advanced/page-controller#configuration"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
PageControllerConfig
</Link>
</>
) : (
<>
@@ -101,71 +118,34 @@ console.log(result.history) // Full execution history`}
>
PageAgentCore
</Link>
. All core methods and events are available. See PageAgentCore docs for detailed API
reference.
</>
)}
</p>
</section>
{/* Configuration */}
<section className="mb-10">
<Heading id="configuration">{isZh ? '配置' : 'Configuration'}</Heading>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh
? 'PageAgent 使用与 PageAgentCore 相同的配置接口。'
: 'PageAgent uses the same configuration interface as PageAgentCore.'}
</p>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh ? (
<>
{' '}
. All core methods and events are available. Config merges{' '}
<Link
href="/advanced/page-agent-core"
href="/advanced/page-agent-core#configuration"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
PageAgentCore
</Link>
</>
) : (
<>
See{' '}
<Link
href="/advanced/page-agent-core"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
PageAgentCore configuration docs
AgentConfig
</Link>{' '}
for complete reference.
and{' '}
<Link
href="/advanced/page-controller#configuration"
className="text-blue-600 dark:text-blue-400 hover:underline"
>
PageControllerConfig
</Link>
.
</>
)}
</p>
</section>
{/* Panel Property */}
{/* Panel */}
<section className="mb-10">
<Heading id="panel-property">{isZh ? 'Panel 属性' : 'Panel Property'}</Heading>
<Heading id="panel">{isZh ? 'UI 面板' : 'UI Panel'}</Heading>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh
? 'PageAgent 自动创建一个 Panel 实例。你可以通过 panel 属性访问它来控制 UI'
: 'PageAgent automatically creates a Panel instance. You can access it via the panel property to control the UI:'}
? 'PageAgent 自动创建一个 Panel 实例。你可以通过 panel 属性控制 UI'
: 'PageAgent automatically creates a Panel instance. You can control the UI via the panel property:'}
</p>
<APIReference
properties={[
{
name: 'panel',
type: 'Panel',
required: true,
description: isZh
? '内置的 UI 面板实例,用于显示任务进度和接收用户输入。'
: 'The built-in UI panel instance for displaying task progress and receiving user input.',
},
]}
/>
<h3 className="text-lg font-semibold mt-6 mb-3">{isZh ? 'Panel 方法' : 'Panel Methods'}</h3>
<CodeEditor
language="typescript"
code={`// Show/hide the panel
@@ -212,16 +192,16 @@ agent.panel.dispose()`}
</tr>
<tr className="bg-white dark:bg-gray-900">
<td className="px-4 py-3 text-gray-600 dark:text-gray-400">
{isZh ? 'Headless 模式' : 'Headless Mode'}
{isZh ? '自动创建 PageController' : 'Auto-creates PageController'}
</td>
<td className="px-4 py-3 text-center text-gray-400 dark:text-gray-600">-</td>
<td className="px-4 py-3 text-center text-green-600 dark:text-green-400"></td>
<td className="px-4 py-3 text-center text-gray-400 dark:text-gray-600">-</td>
</tr>
<tr className="bg-white dark:bg-gray-900">
<td className="px-4 py-3 text-gray-600 dark:text-gray-400">
{isZh ? '自定义 PageController' : 'Custom PageController'}
{isZh ? 'Headless 模式' : 'Headless Mode'}
</td>
<td className="px-4 py-3 text-center text-green-600 dark:text-green-400"></td>
<td className="px-4 py-3 text-center text-gray-400 dark:text-gray-600">-</td>
<td className="px-4 py-3 text-center text-green-600 dark:text-green-400"></td>
</tr>
<tr className="bg-white dark:bg-gray-900">
@@ -0,0 +1,286 @@
import { Link } from 'wouter'
import { APIDivider, APIReference } from '@/components/APIReference'
import CodeEditor from '@/components/CodeEditor'
import { Heading } from '@/components/Heading'
import { useLanguage } from '@/i18n/context'
export default function PageControllerDocs() {
const { isZh } = useLanguage()
return (
<div>
<h1 className="text-4xl font-bold mb-6">PageController</h1>
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
{isZh
? 'PageController 负责 DOM 提取和元素交互,独立于 LLM。它将页面状态结构化为 LLM 可消费的格式,并执行元素级操作。'
: 'PageController handles DOM extraction and element interaction, independent of LLM. It structures page state into LLM-consumable format and executes element-level actions.'}
</p>
{/* Basic Usage */}
<section className="mb-10">
<Heading id="basic-usage">{isZh ? '基本用法' : 'Basic Usage'}</Heading>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh
? 'PageAgent 接受 PageController 配置项:'
: 'PageAgent accepts PageController options:'}
</p>
<CodeEditor
language="typescript"
code={`import { PageAgent } from 'page-agent'
const agent = new PageAgent({
baseURL: 'https://api.openai.com/v1',
apiKey: 'your-api-key',
model: 'gpt-5.2',
// PageController options
enableMask: true,
viewportExpansion: 0,
})`}
/>
<p className="text-gray-600 dark:text-gray-400 mt-4">
{isZh
? 'PageAgentCore 接受 PageController 实例:'
: 'PageAgentCore accepts a PageController instance:'}
</p>
<CodeEditor
language="typescript"
code={`import { PageAgentCore } from '@page-agent/core'
import { PageController } from '@page-agent/page-controller'
const pageController = new PageController({
enableMask: true,
viewportExpansion: -1, // extract full page
})
const agent = new PageAgentCore({
pageController,
baseURL: 'https://api.openai.com/v1',
apiKey: 'your-api-key',
model: 'gpt-5.2',
})`}
/>
</section>
<APIDivider title={isZh ? '配置' : 'Configuration'} />
{/* Configuration */}
<section className="mb-10">
<Heading id="configuration">PageControllerConfig</Heading>
<APIReference
properties={[
{
name: 'enableMask',
type: 'boolean',
defaultValue: 'false',
description: isZh
? '启用视觉遮罩覆盖层,在自动化期间阻止用户操作页面。通过 PageAgent 创建时默认为 true。'
: 'Enable visual mask overlay that blocks user interaction during automation. Defaults to true when created via PageAgent.',
},
{
name: 'viewportExpansion',
type: 'number',
defaultValue: '0',
description: isZh
? '向视口外扩展提取的像素数。设为 -1 表示提取整个页面。'
: 'Pixels to expand extraction beyond viewport. Set to -1 to extract the entire page.',
},
{
name: 'interactiveBlacklist',
type: '(Element | (() => Element))[]',
description: isZh
? '要排除的交互元素列表。支持元素引用或返回元素的函数(延迟求值)。'
: 'Elements to exclude from interaction. Supports element references or functions returning elements (lazy evaluation).',
},
{
name: 'interactiveWhitelist',
type: '(Element | (() => Element))[]',
description: isZh
? '要强制包含的交互元素列表。支持元素引用或返回元素的函数。'
: 'Elements to force include for interaction. Supports element references or functions returning elements.',
},
{
name: 'includeAttributes',
type: 'string[]',
description: isZh
? '在 DOM 提取中包含的额外 HTML 属性(如 data-testid)。默认已包含常见属性如 role, aria-label 等。'
: 'Additional HTML attributes to include in DOM extraction (e.g. data-testid). Common attributes like role, aria-label are included by default.',
},
]}
/>
</section>
<APIDivider title={isZh ? '方法' : 'Methods'} />
{/* Methods */}
<section className="mb-10">
<Heading id="methods">{isZh ? '方法' : 'Methods'}</Heading>
<h3 className="text-lg font-semibold mt-6 mb-3">{isZh ? '状态查询' : 'State Queries'}</h3>
<APIReference
variant="methods"
properties={[
{
name: 'getBrowserState()',
type: 'Promise<BrowserState>',
description: isZh
? '获取结构化的浏览器状态(URL、标题、简化 HTML 等),自动调用 updateTree() 刷新 DOM。这是 Agent 在每步使用的主要方法。'
: 'Get structured browser state (URL, title, simplified HTML, etc.), automatically calls updateTree() to refresh DOM. This is the primary method the agent uses each step.',
},
{
name: 'updateTree()',
type: 'Promise<string>',
description: isZh
? '刷新 DOM 树并返回简化 HTML。通常不需要手动调用 —— getBrowserState() 会自动调用。'
: 'Refresh DOM tree and return simplified HTML. Usually not needed manually — getBrowserState() calls it automatically.',
},
{
name: 'getCurrentUrl()',
type: 'Promise<string>',
description: isZh ? '获取当前页面 URL。' : 'Get current page URL.',
},
]}
/>
<h3 className="text-lg font-semibold mt-6 mb-3">{isZh ? '元素操作' : 'Element Actions'}</h3>
<APIReference
variant="methods"
properties={[
{
name: 'clickElement(index)',
type: 'Promise<ActionResult>',
description: isZh
? '按索引点击元素。索引来自简化 HTML 中的 [N] 标记。'
: 'Click element by index. Index comes from [N] markers in simplified HTML.',
},
{
name: 'inputText(index, text)',
type: 'Promise<ActionResult>',
description: isZh ? '向输入框元素填入文本。' : 'Input text into a form element.',
},
{
name: 'selectOption(index, optionText)',
type: 'Promise<ActionResult>',
description: isZh ? '在下拉框中选择选项。' : 'Select option in a dropdown element.',
},
{
name: 'scroll(options)',
type: 'Promise<ActionResult>',
description: isZh
? '垂直滚动页面或指定元素。'
: 'Scroll page or specific element vertically.',
},
{
name: 'scrollHorizontally(options)',
type: 'Promise<ActionResult>',
description: isZh
? '水平滚动页面或指定元素。'
: 'Scroll page or specific element horizontally.',
},
]}
/>
<h3 className="text-lg font-semibold mt-6 mb-3">{isZh ? '遮罩控制' : 'Mask Control'}</h3>
<APIReference
variant="methods"
properties={[
{
name: 'showMask()',
type: 'Promise<void>',
description: isZh
? '显示视觉遮罩。需要 enableMask: true。'
: 'Show visual mask overlay. Requires enableMask: true.',
},
{
name: 'hideMask()',
type: 'Promise<void>',
description: isZh ? '隐藏视觉遮罩。' : 'Hide visual mask overlay.',
},
]}
/>
<h3 className="text-lg font-semibold mt-6 mb-3">{isZh ? '生命周期' : 'Lifecycle'}</h3>
<APIReference
variant="methods"
properties={[
{
name: 'dispose()',
type: 'void',
description: isZh
? '清理所有资源(DOM 高亮、遮罩等)。Agent 销毁时自动调用。'
: 'Clean up all resources (DOM highlights, mask, etc.). Called automatically when agent disposes.',
},
]}
/>
</section>
<APIDivider title={isZh ? '类型定义' : 'Type Definitions'} />
{/* BrowserState */}
<section className="mb-10">
<Heading id="browser-state">BrowserState</Heading>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh
? 'getBrowserState() 返回的结构化浏览器状态,直接用于构建 LLM prompt。'
: 'Structured browser state returned by getBrowserState(), used directly to build LLM prompts.'}
</p>
<CodeEditor
language="typescript"
code={`interface BrowserState {
url: string
title: string
header: string // page info + scroll position
content: string // simplified HTML of interactive elements
footer: string // scroll hint
}`}
/>
</section>
{/* ActionResult */}
<section className="mb-10">
<Heading id="action-result">ActionResult</Heading>
<CodeEditor
language="typescript"
code={`interface ActionResult {
success: boolean
message: string
}`}
/>
</section>
{/* Custom PageController */}
<section className="mb-10">
<Heading id="custom-implementation">
{isZh ? '自定义实现' : 'Custom Implementation'}
</Heading>
<p className="text-gray-600 dark:text-gray-400 mb-4">
{isZh
? '在非浏览器环境(如 Puppeteer、Playwright),你可以实现自定义 PageController。需要实现 Agent 使用的核心方法:'
: 'In non-browser environments (e.g. Puppeteer, Playwright), you can implement a custom PageController. Implement the core methods used by the agent:'}
</p>
<CodeEditor
language="typescript"
code={`import { PageAgentCore } from '@page-agent/core'
import type { PageController } from '@page-agent/page-controller'
class PuppeteerPageController implements PageController {
async getBrowserState() { /* ... */ }
async clickElement(index: number) { /* ... */ }
async inputText(index: number, text: string) { /* ... */ }
async scroll(options: { down: boolean; numPages: number }) { /* ... */ }
// ... other methods
}
const agent = new PageAgentCore({
pageController: new PuppeteerPageController(),
baseURL: 'https://api.openai.com/v1',
apiKey: 'your-api-key',
model: 'gpt-5.2',
})`}
/>
</section>
</div>
)
}
@@ -1,4 +1,3 @@
import BetaNotice from '@/components/BetaNotice'
import CodeEditor from '@/components/CodeEditor'
import { Heading } from '@/components/Heading'
import { useLanguage } from '@/i18n/context'
@@ -12,115 +11,100 @@ export default function CustomTools() {
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 leading-relaxed">
{isZh
? '通过注册自定义工具,扩展 AI Agent 的能力边界。使用 Zod 定义严格的输入接口,让 AI 安全调用你的业务逻辑。'
: 'Extend AI Agent capabilities by registering custom tools. Use Zod to define strict input schemas for safe business logic calls.'}
? '通过注册自定义工具,扩展 AI Agent 的能力边界。使用 Zod 定义输入接口,让 AI 安全调用你的业务逻辑。'
: 'Extend AI Agent capabilities by registering custom tools. Define input schemas with Zod for safe business logic invocation.'}
</p>
<div className="space-y-8">
<section>
<Heading id="tool-registration" className="text-2xl font-bold mb-4">
{isZh ? '工具注册' : 'Tool Registration'}
<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
? '每个自定义工具需要定义四个核心属性:name、description、input schema 和 execute 函数。'
: 'Each custom tool requires four core properties: name, description, input schema, and execute function.'}
? '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={`import * as zod from 'zod'
import { PageAgent, tool } from 'page-agent'
// override internal tool
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(),
}),
execute: async function (this: PageAgent, input) {
const answer = await do_some_thing(input.question)
return "✅ Received user answer: " + answer
},
})
}
// remove internal tool
const customTools = {
scroll: null, // never scroll
}
const pageAgent = new PageAgent({customTools})
`}
code={`// Zod 3 (>=3.25.0) or Zod 4
import { z } from 'zod/v4'`}
language="javascript"
/>
</section>
<section>
<Heading id="page-filter" className="text-2xl font-bold mb-4">
{isZh ? '页面过滤器' : 'Page Filter'}
<Heading id="define-tools" className="text-2xl font-bold mb-4">
{isZh ? '定义工具' : 'Define Tools'}
</Heading>
<BetaNotice />
<p className="text-gray-600 dark:text-gray-300 mb-4">
{isZh
? '通过 pageFilter 属性控制工具在哪些页面可见,提升安全性和用户体验。'
: 'Control tool visibility on specific pages via the pageFilter property to enhance security and UX.'}
? '使用 tool() 辅助函数定义自定义工具,每个工具包含 description、inputSchema 和 execute 三个属性。'
: 'Use the tool() helper to define custom tools with description, inputSchema, and execute.'}
</p>
<CodeEditor
code={`pageAgent.registerTool({
name: 'approveOrder',
description: '审批订单',
input: z.object({
orderId: z.string(),
approved: z.boolean()
}),
execute: async (params) => {
// 审批逻辑
code={`import { z } from 'zod/v4'
import { PageAgent, tool } from 'page-agent'
const pageAgent = new PageAgent({
customTools: {
//
add_to_cart: tool({
description: 'Add a product to the shopping cart by its product ID.',
inputSchema: z.object({
productId: z.string(),
quantity: z.number().min(1).default(1),
}),
execute: async function (input) {
await fetch('/api/cart', {
method: 'POST',
body: JSON.stringify(input),
})
return \`Added \${input.quantity}x \${input.productId} to cart.\`
},
}),
//
search_knowledge_base: tool({
description: 'Search the internal knowledge base and return relevant articles.',
inputSchema: z.object({
query: z.string(),
limit: z.number().max(10).default(3),
}),
execute: async function (input) {
const res = await fetch(
\`/api/kb?q=\${encodeURIComponent(input.query)}&limit=\${input.limit}\`
)
const articles = await res.json()
return JSON.stringify(articles)
},
}),
},
// 可选:页面过滤器
pageFilter: {
// 只在订单管理页面显示
include: ['/admin/orders', '/admin/orders/*'],
// 排除特定页面
exclude: ['/admin/orders/archived']
}
})`}
language="javascript"
/>
</section>
<section>
<Heading id="best-practices" className="text-2xl font-bold mb-4">
{isZh ? '最佳实践' : 'Best Practices'}
<Heading id="override-remove" className="text-2xl font-bold mb-4">
{isZh ? '覆盖与移除内置工具' : 'Override & Remove Built-in Tools'}
</Heading>
<div className="space-y-4">
<div className="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
<h3 className="text-lg font-semibold text-yellow-900 dark:text-yellow-300 mb-2">
{isZh ? '⚡ 性能优化' : '⚡ Performance Optimization'}
</h3>
<ul className="text-gray-600 dark:text-gray-300 space-y-1 text-sm">
<li>
{isZh
? '• 使用 pageFilter 减少不必要的工具加载'
: '• Use pageFilter to reduce unnecessary tool loading'}
</li>
<li>
{isZh
? '• 在 execute 函数中实现适当的缓存机制'
: '• Implement appropriate caching in execute functions'}
</li>
<li>
{isZh
? '• 避免在工具中执行耗时的同步操作'
: '• Avoid long-running sync operations in tools'}
</li>
</ul>
</div>
</div>
<p className="text-gray-600 dark:text-gray-300 mb-4">
{isZh
? '使用相同的名称可以覆盖内置工具的行为,设置为 null 则完全移除该工具。'
: 'Use the same name to override a built-in tool, or set it to null to remove it entirely.'}
</p>
<CodeEditor
code={`const pageAgent = new PageAgent({
customTools: {
scroll: null, // remove scroll tool
execute_javascript: null, // remove script execution
},
})`}
language="javascript"
/>
</section>
</div>
</div>
@@ -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: [
@@ -247,16 +248,34 @@ LLM_MODEL_NAME="qwen3:14b"`}
{isZh
? '确保上下文长度大于输入 token 数,否则 Ollama 会静默截断 prompt。普通页面约需 15k token,随步骤增加。默认 4k 上下文长度无法正常工作'
: 'Ensure context length exceeds input tokens, or Ollama will silently truncate prompts. ~15k tokens for a typical page, increases with steps. Default 4k context length will NOT work'}
<ul className="text-sm text-gray-700 dark:text-gray-300 space-y-2 list-disc pl-5">
<li>
<code className="text-xs bg-gray-200 dark:bg-gray-700 px-1 rounded">
$env:OLLAMA_CONTEXT_LENGTH=64000; ollama serve
</code>
</li>
</ul>
</li>
</ul>
</div>
<div className="mt-4">
<h3 className="font-semibold text-gray-900 dark:text-gray-100 mb-3">
{isZh ? '建议启动参数' : 'Recommended Startup'}
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-3">
{isZh
? '启动 Ollama 时建议配置以下环境变量:扩大上下文窗口、允许跨域访问、监听所有网络接口。'
: 'Start Ollama with these environment variables: larger context window, allow cross-origin access, and listen on all interfaces.'}
</p>
<div className="space-y-2">
<p className="text-xs font-medium text-gray-500 dark:text-gray-400">macOS / Linux</p>
<CodeEditor
code={`OLLAMA_CONTEXT_LENGTH=64000 OLLAMA_HOST=0.0.0.0:11434 OLLAMA_ORIGINS="*" ollama serve`}
/>
<p className="text-xs font-medium text-gray-500 dark:text-gray-400 pt-2">
Windows (PowerShell)
</p>
<CodeEditor
code={`$env:OLLAMA_CONTEXT_LENGTH=64000; $env:OLLAMA_HOST="0.0.0.0:11434"; $env:OLLAMA_ORIGINS="*"; ollama serve`}
/>
</div>
</div>
</section>
</div>
)
@@ -105,27 +105,6 @@ const pageAgentTool = {
</p>
</div>
</div>
<div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4 mb-6">
<h3 className="text-lg font-semibold text-yellow-900 dark:text-yellow-100 mb-2">
{isZh ? '⚠️ 注意事项' : '⚠️ Notes'}
</h3>
<ul className="text-yellow-800 dark:text-yellow-200 space-y-1 text-sm">
<li>
{' '}
{isZh
? '确保目标网站允许自动化操作'
: 'Ensure target website allows automated operations'}
</li>
<li> {isZh ? '实现适当的频率限制' : 'Implement appropriate rate limiting'}</li>
<li>
{' '}
{isZh
? '敏感操作建议要求人工确认'
: 'Recommend human confirmation for sensitive operations'}
</li>
</ul>
</div>
</div>
)
}
+14 -14
View File
@@ -6,15 +6,15 @@ import CustomUIDocs from './advanced/custom-ui/page'
import PageAgentCoreDocs from './advanced/page-agent-core/page'
// Advanced
import PageAgentDocs from './advanced/page-agent/page'
import PageControllerDocs from './advanced/page-controller/page'
import SecurityPermissions from './advanced/security-permissions/page'
// Features
import ChromeExtension from './features/chrome-extension/page'
import Instructions from './features/custom-instructions/page'
import CustomTools from './features/custom-tools/page'
import DataMasking from './features/data-masking/page'
import Models from './features/models/page'
import BestPractices from './integration/best-practices/page'
import SecurityPermissions from './integration/security-permissions/page'
import ThirdPartyAgent from './integration/third-party-agent/page'
import ThirdPartyAgent from './features/third-party-agent/page'
import Limitations from './introduction/limitations/page'
// Introduction
import Overview from './introduction/overview/page'
@@ -80,6 +80,11 @@ export default function DocsRouter() {
<ChromeExtension />
</DocsPage>
</Route>
<Route path="/features/third-party-agent">
<DocsPage>
<ThirdPartyAgent />
</DocsPage>
</Route>
{/* Advanced */}
<Route path="/advanced/page-agent">
@@ -92,26 +97,21 @@ export default function DocsRouter() {
<PageAgentCoreDocs />
</DocsPage>
</Route>
<Route path="/advanced/page-controller">
<DocsPage>
<PageControllerDocs />
</DocsPage>
</Route>
<Route path="/advanced/custom-ui">
<DocsPage>
<CustomUIDocs />
</DocsPage>
</Route>
<Route path="/integration/security-permissions">
<Route path="/advanced/security-permissions">
<DocsPage>
<SecurityPermissions />
</DocsPage>
</Route>
<Route path="/integration/best-practices">
<DocsPage>
<BestPractices />
</DocsPage>
</Route>
<Route path="/integration/third-party-agent">
<DocsPage>
<ThirdPartyAgent />
</DocsPage>
</Route>
{/* Default redirect or 404 */}
<Route path="/docs">
@@ -1,20 +0,0 @@
import BetaNotice from '@/components/BetaNotice'
import CodeEditor from '@/components/CodeEditor'
import { useLanguage } from '@/i18n/context'
export default function BestPractices() {
const { isZh } = useLanguage()
return (
<div>
<h1 className="text-4xl font-bold mb-6">{isZh ? '最佳实践' : 'Best Practices'}</h1>
<BetaNotice />
<p className="text-xl text-gray-600 dark:text-gray-300 mb-6 leading-relaxed">
{isZh
? '使用 page-agent 的最佳实践和常见问题解决方案。'
: 'Best practices and common solutions for using page-agent.'}
</p>
<CodeEditor code={`// TODO`} />
</div>
)
}
@@ -83,7 +83,7 @@ export default function LimitationsPage() {
</div>
{/* Interaction Limitations */}
<Heading id="interaction-capabilities" className="text-2xl font-bold mb-3">
<Heading id="interaction-capabilities" className="text-2xl font-bold mb-3 mt-6">
{isZh ? '交互能力' : 'Interaction Capabilities'}
</Heading>
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-6 mb-6">
@@ -128,33 +128,25 @@ export default function LimitationsPage() {
</div>
{/* Understanding Limitations */}
<Heading id="understanding" className="text-2xl font-bold mb-3">
<Heading id="understanding" className="text-2xl font-bold mb-3 mt-6">
{isZh ? '理解能力' : 'Understanding'}
</Heading>
<div className="bg-amber-50 dark:bg-amber-900/20 border-l-4 border-amber-400 p-4 mb-6">
<p className="text-amber-800 dark:text-amber-200 mb-2 font-medium">
{isZh
? 'Page Agent 不使用多模态模型,不截图,没有视觉能力。仅通过 DOM 结构理解页面。'
: 'Page Agent does not use multimodal models, does not take screenshots, and has no visual capability. It reads pages through DOM structure only.'}
</p>
<p className="text-amber-700 dark:text-amber-300 text-sm">
{isZh
? '图片、Canvas、WebGL、SVG 等视觉内容无法被识别。页面的语义化程度和可访问性直接影响 AI 的理解准确性。'
: 'Images, Canvas, WebGL, SVG and other visual content cannot be recognized. Page semantic quality and accessibility directly affect AI accuracy.'}
</p>
</div>
{/* Website Quality */}
<Heading id="page-quality-matters" className="text-2xl font-bold mb-3">
{isZh ? '网页质量影响' : 'Page Quality Matters'}
</Heading>
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-6">
<p className="text-gray-600 dark:text-gray-300 text-sm">
{isZh
? '反常识的交互逻辑、纯视觉的操作提示、快速出现消失的元素等都会降低自动化成功率。语义化的 HTML 和良好的可访问性会显著提升效果。'
: 'Counter-intuitive interactions, visual-only cues, and rapidly appearing/disappearing elements reduce automation success. Semantic HTML and good accessibility significantly improve results.'}
</p>
</div>
<p className="mb-2 font-medium">
{isZh
? 'Page Agent 不使用多模态模型,不截图,没有视觉能力。仅通过 DOM 结构理解页面。'
: 'Page Agent does not use multimodal models, does not take screenshots, and has no visual capability. It reads pages through DOM structure only.'}
</p>
<p className="mb-2 font-medium">
{isZh
? '图片、Canvas、WebGL、SVG 等视觉内容无法被识别。页面的语义化程度和可访问性直接影响 AI 的理解准确性。'
: 'Images, Canvas, WebGL, SVG and other visual content cannot be recognized. Page semantic quality and accessibility directly affect AI accuracy.'}
</p>
<p className="mb-2 font-medium">
{isZh
? '反常识的交互逻辑、纯视觉的操作提示、快速出现消失的元素等都会降低自动化成功率。语义化的 HTML 和良好的可访问性会显著提升效果。'
: 'Counter-intuitive interactions, visual-only cues, and rapidly appearing/disappearing elements reduce automation success. Semantic HTML and good accessibility significantly improve results.'}
</p>
</div>
</div>
)
@@ -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() {
+3 -3
View File
@@ -27,12 +27,12 @@ const SPA_ROUTES = [
'docs/features/custom-instructions',
'docs/features/models',
'docs/features/chrome-extension',
'docs/features/third-party-agent',
'docs/advanced/page-agent',
'docs/advanced/page-agent-core',
'docs/advanced/page-controller',
'docs/advanced/custom-ui',
'docs/integration/security-permissions',
'docs/integration/best-practices',
'docs/integration/third-party-agent',
'docs/advanced/security-permissions',
]
function spaRoutes() {