Compare commits

..

39 Commits

Author SHA1 Message Date
Simon f73831036d chore(version): bump version to 1.11.0 2026-07-03 23:57:03 +08:00
Simon f8af56032a chore(ui): remove legacy motion-css code (#583)
Unused CSS motion experiment with known performance issues; no references remain.
2026-07-03 23:51:29 +08:00
Erwann Robin 60fe4c4c84 chore: set the bookmark script to English locale (#580)
* chore: set the bookmark script to English locale

* chore: adapt the booklet to the locale

* refactor(demo): improve naming to better distinguish mirror and lang

---------

Co-authored-by: Simon <10131203+gaomeng1900@users.noreply.github.com>
2026-07-03 22:49:12 +08:00
Simon 4ce6d9e5c7 Merge pull request #581 from alibaba/refactor/llms
refactor(llms): rewrite model patching and deprecate temperature
2026-07-03 20:37:43 +08:00
Simon 9a75ead6ad fix: do not enable OpenRouter reasoning by default 2026-07-03 20:30:57 +08:00
Simon 10276c6e67 fix: new URL should always be in a try block 2026-07-03 20:14:11 +08:00
Simon 5aeb67f6d6 feat(llms): update model list; update patch 2026-07-03 03:04:43 +08:00
Simon 556bc7448a chore(llms): add real-api test for all supported models 2026-07-03 02:29:06 +08:00
Simon 04b84b6e8a fix(llms): model compatibility problem 2026-07-03 02:26:01 +08:00
Simon 2163a55cc7 docs: remove models that can not be tested anymore from the list 2026-07-03 02:25:03 +08:00
Simon a47e12c0e0 fix: model patch for GPT and Claude 2026-07-02 22:11:28 +08:00
Simon d1c3dc811e docs: update model list 2026-07-02 21:51:55 +08:00
Simon 8ad719744a feat: rewrite llm config and model patch 2026-07-02 21:31:44 +08:00
Simon 83fee37fb4 fix: ci 2026-07-02 03:13:39 +08:00
Simon b41535ef5e Merge pull request #579 from alibaba/chore/cspell-vscode-config
chore: move cSpell config to .vscode/cspell.json
2026-07-02 03:09:00 +08:00
Simon 8ad1174f20 chore: move cSpell config to .vscode/cspell.json
Centralize project dictionary and ignore paths; fix minor prompt typo and GA tag spell-check noise.
2026-07-02 03:05:38 +08:00
Simon 026171715c Merge pull request #578 from alibaba/fix/wrong-active-tab-multi-window
fix: wrong active tab/window in multi-window scenarios
2026-07-02 02:43:04 +08:00
Simon 8b902e1e10 docs(ext): add limitations section 2026-07-02 02:32:33 +08:00
Simon 1f45e74378 fix(extension): resolve active tab/window per caller context 2026-07-02 02:11:32 +08:00
Simon 585e7a4900 chore: update deps 2026-07-01 17:09:57 +08:00
Simon 04a397c19e Merge pull request #577 from alibaba/dependabot/npm_and_yarn/development-dependencies-333bd1d898
chore(deps-dev): bump the development-dependencies group with 16 updates
2026-06-30 19:08:33 +08:00
Simon ae4276c624 fix: code style 2026-06-30 19:00:58 +08:00
dependabot[bot] 7579def667 chore(deps-dev): bump the development-dependencies group with 16 updates
Bumps the development-dependencies group with 16 updates:

| Package | From | To |
| --- | --- | --- |
| [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) | `21.0.2` | `21.1.0` |
| [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) | `21.0.2` | `21.1.0` |
| [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react/tree/HEAD/plugins/eslint-plugin) | `5.9.2` | `5.10.0` |
| [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) | `4.3.1` | `4.3.2` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.0.0` | `26.0.1` |
| [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) | `6.0.2` | `6.0.3` |
| [eslint](https://github.com/eslint/eslint) | `10.5.0` | `10.6.0` |
| [prettier](https://github.com/prettier/prettier) | `3.8.4` | `3.9.3` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.62.0` | `8.62.1` |
| [unplugin-dts](https://github.com/qmhc/unplugin-dts/tree/HEAD/packages/unplugin-dts) | `1.0.2` | `1.0.3` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.0.16` | `8.1.0` |
| [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.21.0` | `1.22.0` |
| [motion](https://github.com/motiondivision/motion) | `12.40.0` | `12.42.0` |
| [simple-icons](https://github.com/simple-icons/simple-icons) | `16.24.0` | `16.24.1` |
| [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) | `4.3.1` | `4.3.2` |
| [wxt](https://github.com/wxt-dev/wxt) | `0.20.26` | `0.20.27` |


Updates `@commitlint/cli` from 21.0.2 to 21.1.0
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.1.0/@commitlint/cli)

Updates `@commitlint/config-conventional` from 21.0.2 to 21.1.0
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.1.0/@commitlint/config-conventional)

Updates `@eslint-react/eslint-plugin` from 5.9.2 to 5.10.0
- [Release notes](https://github.com/Rel1cx/eslint-react/releases)
- [Changelog](https://github.com/Rel1cx/eslint-react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Rel1cx/eslint-react/commits/v5.10.0/plugins/eslint-plugin)

Updates `@tailwindcss/vite` from 4.3.1 to 4.3.2
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.2/packages/@tailwindcss-vite)

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

Updates `@vitejs/plugin-react` from 6.0.2 to 6.0.3
- [Release notes](https://github.com/vitejs/vite-plugin-react/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@6.0.3/packages/plugin-react)

Updates `eslint` from 10.5.0 to 10.6.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.5.0...v10.6.0)

Updates `prettier` from 3.8.4 to 3.9.3
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.8.4...3.9.3)

Updates `typescript-eslint` from 8.62.0 to 8.62.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.62.1/packages/typescript-eslint)

Updates `unplugin-dts` from 1.0.2 to 1.0.3
- [Release notes](https://github.com/qmhc/unplugin-dts/releases)
- [Changelog](https://github.com/qmhc/unplugin-dts/blob/main/packages/unplugin-dts/CHANGELOG.md)
- [Commits](https://github.com/qmhc/unplugin-dts/commits/unplugin-dts@1.0.3/packages/unplugin-dts)

Updates `vite` from 8.0.16 to 8.1.0
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/create-vite@8.1.0/packages/vite)

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

Updates `motion` from 12.40.0 to 12.42.0
- [Changelog](https://github.com/motiondivision/motion/blob/main/CHANGELOG.md)
- [Commits](https://github.com/motiondivision/motion/compare/v12.40.0...v12.42.0)

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

Updates `tailwindcss` from 4.3.1 to 4.3.2
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.2/packages/tailwindcss)

Updates `wxt` from 0.20.26 to 0.20.27
- [Release notes](https://github.com/wxt-dev/wxt/releases)
- [Commits](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.26...wxt-v0.20.27)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-version: 21.1.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: "@commitlint/config-conventional"
  dependency-version: 21.1.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: "@eslint-react/eslint-plugin"
  dependency-version: 5.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: "@tailwindcss/vite"
  dependency-version: 4.3.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@types/node"
  dependency-version: 26.0.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@vitejs/plugin-react"
  dependency-version: 6.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: eslint
  dependency-version: 10.6.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: prettier
  dependency-version: 3.9.3
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.62.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: unplugin-dts
  dependency-version: 1.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: vite
  dependency-version: 8.1.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: lucide-react
  dependency-version: 1.22.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: motion
  dependency-version: 12.42.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: simple-icons
  dependency-version: 16.24.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: tailwindcss
  dependency-version: 4.3.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: wxt
  dependency-version: 0.20.27
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-30 00:43:38 +00:00
Simon 187d5deccb Merge pull request #568 from itxaiohanglover/fix/chat-latest-reasoning-effort
fix(llms): skip reasoning_effort patch for chat-latest models
2026-06-26 01:07:30 +08:00
Simon fb221b4535 Merge pull request #573 from alibaba/feat/assets-repo
fix(assets): migrate img.alicdn.com URLs to page-agent.github.io
2026-06-26 01:02:50 +08:00
Simon 5058c96a78 fix(assets): migrate img.alicdn.com URLs to page-agent.github.io 2026-06-26 00:56:37 +08:00
Simon 2b679d85cb fix(llms): omitting both reasoning_effort and temperature for chat-latest 2026-06-26 00:12:34 +08:00
artboy 57f64347ac fix(llms): skip reasoning_effort patch for *-chat-latest models
normalizeModelName reduces gpt-5.2-chat-latest to gpt-52-chat-latest,
which matches the gpt-52 prefix and gets reasoning_effort='none'.
But chat-latest models don't support reasoning_effort, causing
HTTP 400.

Fix: check for 'chat-latest' before applying reasoning_effort patches.

Fixes #563
2026-06-23 14:26:45 +08:00
Simon fb4b8fd067 Merge pull request #565 from alibaba/dependabot/github_actions/github-actions-640176b5ab
chore(deps): bump actions/checkout from 6 to 7 in the github-actions group
2026-06-23 14:24:52 +08:00
Simon 2bed20cd85 Merge pull request #566 from alibaba/dependabot/npm_and_yarn/development-dependencies-1075e1fe2e
chore(deps-dev): bump the development-dependencies group with 8 updates
2026-06-23 14:24:36 +08:00
Simon c48c7c524e Merge pull request #567 from alibaba/dependabot/npm_and_yarn/development-major-3bc154b33b
chore(deps-dev): bump @types/node from 25.9.3 to 26.0.0 in the development-major group
2026-06-23 14:24:20 +08:00
dependabot[bot] 32f27e73e3 chore(deps-dev): bump @types/node in the development-major group
Bumps the development-major group with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


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

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: development-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-23 00:30:20 +00:00
dependabot[bot] 71738f1966 chore(deps-dev): bump the development-dependencies group with 8 updates
Bumps the development-dependencies group with 8 updates:

| Package | From | To |
| --- | --- | --- |
| [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react/tree/HEAD/plugins/eslint-plugin) | `5.9.0` | `5.9.2` |
| [globals](https://github.com/sindresorhus/globals) | `17.6.0` | `17.7.0` |
| [happy-dom](https://github.com/capricorn86/happy-dom) | `20.10.4` | `20.10.6` |
| [lint-staged](https://github.com/lint-staged/lint-staged) | `17.0.7` | `17.0.8` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.61.1` | `8.62.0` |
| [@types/chrome](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/chrome) | `0.1.43` | `0.2.0` |
| [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.18.0` | `1.21.0` |
| [simple-icons](https://github.com/simple-icons/simple-icons) | `16.23.0` | `16.24.0` |


Updates `@eslint-react/eslint-plugin` from 5.9.0 to 5.9.2
- [Release notes](https://github.com/Rel1cx/eslint-react/releases)
- [Changelog](https://github.com/Rel1cx/eslint-react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Rel1cx/eslint-react/commits/v5.9.2/plugins/eslint-plugin)

Updates `globals` from 17.6.0 to 17.7.0
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](https://github.com/sindresorhus/globals/compare/v17.6.0...v17.7.0)

Updates `happy-dom` from 20.10.4 to 20.10.6
- [Release notes](https://github.com/capricorn86/happy-dom/releases)
- [Commits](https://github.com/capricorn86/happy-dom/compare/v20.10.4...v20.10.6)

Updates `lint-staged` from 17.0.7 to 17.0.8
- [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/v17.0.7...v17.0.8)

Updates `typescript-eslint` from 8.61.1 to 8.62.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.62.0/packages/typescript-eslint)

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

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

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

---
updated-dependencies:
- dependency-name: "@eslint-react/eslint-plugin"
  dependency-version: 5.9.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: globals
  dependency-version: 17.7.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: happy-dom
  dependency-version: 20.10.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: lint-staged
  dependency-version: 17.0.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.62.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: "@types/chrome"
  dependency-version: 0.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: lucide-react
  dependency-version: 1.21.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: simple-icons
  dependency-version: 16.24.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-23 00:29:32 +00:00
dependabot[bot] e44455ba80 chore(deps): bump actions/checkout in the github-actions group
Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 6 to 7
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-23 00:27:34 +00:00
Simon d2ee260d82 Merge pull request #560 from alibaba/dependabot/npm_and_yarn/development-dependencies-d854c95bb8
chore(deps-dev): bump the development-dependencies group across 1 directory with 17 updates
2026-06-17 20:53:39 +08:00
dependabot[bot] 1f738bc41c chore(deps-dev): bump the development-dependencies group across 1 directory with 17 updates
Bumps the development-dependencies group with 16 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react/tree/HEAD/plugins/eslint-plugin) | `5.8.16` | `5.9.0` |
| [@microsoft/api-extractor](https://github.com/microsoft/rushstack/tree/HEAD/apps/api-extractor) | `7.58.8` | `7.58.9` |
| [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) | `4.3.0` | `4.3.1` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `25.9.2` | `25.9.3` |
| [eslint](https://github.com/eslint/eslint) | `10.4.1` | `10.5.0` |
| [happy-dom](https://github.com/capricorn86/happy-dom) | `20.10.2` | `20.10.4` |
| [prettier](https://github.com/prettier/prettier) | `3.8.3` | `3.8.4` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.61.0` | `8.61.1` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.8` | `4.1.9` |
| [@radix-ui/react-hover-card](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/hover-card) | `1.1.16` | `1.1.17` |
| [@radix-ui/react-label](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/label) | `2.1.9` | `2.1.10` |
| [@radix-ui/react-separator](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/separator) | `1.1.9` | `1.1.10` |
| [@radix-ui/react-slot](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/slot) | `1.2.5` | `1.3.0` |
| [@radix-ui/react-switch](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/switch) | `1.3.0` | `1.3.1` |
| [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.17.0` | `1.18.0` |
| [@radix-ui/react-tooltip](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/tooltip) | `1.2.9` | `1.2.10` |



Updates `@eslint-react/eslint-plugin` from 5.8.16 to 5.9.0
- [Release notes](https://github.com/Rel1cx/eslint-react/releases)
- [Changelog](https://github.com/Rel1cx/eslint-react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Rel1cx/eslint-react/commits/v5.9.0/plugins/eslint-plugin)

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

Updates `@tailwindcss/vite` from 4.3.0 to 4.3.1
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.1/packages/@tailwindcss-vite)

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

Updates `eslint` from 10.4.1 to 10.5.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.4.1...v10.5.0)

Updates `happy-dom` from 20.10.2 to 20.10.4
- [Release notes](https://github.com/capricorn86/happy-dom/releases)
- [Commits](https://github.com/capricorn86/happy-dom/compare/v20.10.2...v20.10.4)

Updates `prettier` from 3.8.3 to 3.8.4
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.8.3...3.8.4)

Updates `typescript-eslint` from 8.61.0 to 8.61.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.61.1/packages/typescript-eslint)

Updates `vitest` from 4.1.8 to 4.1.9
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/HEAD/packages/vitest)

Updates `@radix-ui/react-hover-card` from 1.1.16 to 1.1.17
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/hover-card/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/hover-card)

Updates `@radix-ui/react-label` from 2.1.9 to 2.1.10
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/label/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/label)

Updates `@radix-ui/react-separator` from 1.1.9 to 1.1.10
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/separator/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/separator)

Updates `@radix-ui/react-slot` from 1.2.5 to 1.3.0
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/slot/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/slot)

Updates `@radix-ui/react-switch` from 1.3.0 to 1.3.1
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/switch/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/switch)

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

Updates `tailwindcss` from 4.3.0 to 4.3.1
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.1/packages/tailwindcss)

Updates `@radix-ui/react-tooltip` from 1.2.9 to 1.2.10
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/tooltip/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/tooltip)

---
updated-dependencies:
- dependency-name: "@eslint-react/eslint-plugin"
  dependency-version: 5.9.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: "@microsoft/api-extractor"
  dependency-version: 7.58.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@tailwindcss/vite"
  dependency-version: 4.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@types/node"
  dependency-version: 25.9.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: eslint
  dependency-version: 10.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: happy-dom
  dependency-version: 20.10.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: prettier
  dependency-version: 3.8.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.61.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: vitest
  dependency-version: 4.1.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@radix-ui/react-hover-card"
  dependency-version: 1.1.17
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@radix-ui/react-label"
  dependency-version: 2.1.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@radix-ui/react-separator"
  dependency-version: 1.1.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@radix-ui/react-slot"
  dependency-version: 1.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: "@radix-ui/react-switch"
  dependency-version: 1.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: lucide-react
  dependency-version: 1.18.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: tailwindcss
  dependency-version: 4.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@radix-ui/react-tooltip"
  dependency-version: 1.2.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-16 05:00:12 +00:00
Simon 2802ab3dea Merge pull request #559 from alibaba/fix/hoist-happy-dom
fix(deps): hoist happy-dom to root devDependencies
2026-06-16 12:33:51 +08:00
Simon e6a2e07640 fix(deps): hoist happy-dom to root devDependencies 2026-06-16 12:30:55 +08:00
Simon 235fa1bc26 chore: fix lint error caused CI fail 2026-06-15 22:59:34 +08:00
46 changed files with 2059 additions and 2186 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
node-version: [24]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
id-token: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v6
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v6
+82
View File
@@ -0,0 +1,82 @@
{
"version": "0.2",
"language": "en",
"globRoot": "..",
"ignorePaths": [
"**/node_modules/**",
"**/dist/**",
"**/dist-ssr/**",
"**/.output/**",
"**/.wxt/**",
"**/*.svg"
],
"words": [
"activedescendant",
"adex",
"agentactivity",
"agentic",
"akldabonmimlicnjlflnapfeklbfemhj",
"aliyun",
"Bailian",
"Bento",
"BYOK",
"Chromewebstore",
"contenteditable",
"deepseek",
"executionresult",
"fieldsets",
"gaomeng",
"Googlechrome",
"Grabbable",
"Gregor",
"haspopup",
"highspeed",
"historicalevent",
"historychange",
"HITL",
"hrefs",
"IDBP",
"innerhtml",
"instructionsconfig",
"kimi",
"languagedetector",
"Lian",
"llms",
"magicui",
"maxlength",
"menuitemcheckbox",
"menuitemradio",
"modelcontextprotocol",
"multipageagent",
"nesw",
"npmmirror",
"Ollama",
"onwarn",
"opensource",
"openrouter",
"pageagent",
"pageagentcore",
"pageagentext",
"postpublish",
"qwen",
"reactid",
"reactroot",
"retryable",
"shadcn",
"sidepanel",
"spinbutton",
"staticity",
"statuschange",
"Supabase",
"toggleable",
"trivago",
"Uncapitalize",
"unparseable",
"valuenow",
"valuetext",
"WCAG",
"webgl",
"wouter",
"Zunic"
]
}
-20
View File
@@ -1,24 +1,4 @@
{
"cSpell.words": [
"agentic",
"contenteditable",
"deepseek",
"historychange",
"HITL",
"innerhtml",
"languagedetector",
"llms",
"magicui",
"npmmirror",
"onwarn",
"opensource",
"qwen",
"retryable",
"shadcn",
"sidepanel",
"statuschange",
"wouter"
],
"files.exclude": {
"packages/*/node_modules": true
},
+6 -6
View File
@@ -1,8 +1,8 @@
# Page Agent
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://img.alicdn.com/imgextra/i4/O1CN01qKig1P1FnhpFKNdi6_!!6000000000532-2-tps-1280-256.png">
<img alt="Page Agent Banner" src="https://img.alicdn.com/imgextra/i1/O1CN01NCMKXj1Gn4tkFTsxf_!!6000000000666-2-tps-1280-256.png">
<source media="(prefers-color-scheme: dark)" srcset="https://page-agent.github.io/assets/readme/banner-dark.png">
<img alt="Page Agent Banner" src="https://page-agent.github.io/assets/readme/banner-light.png">
</picture>
[![License: MIT](https://img.shields.io/badge/License-MIT-auto.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/) [![Bundle Size](https://img.shields.io/bundlephobia/minzip/page-agent)](https://bundlephobia.com/package/page-agent) [![Downloads](https://img.shields.io/npm/dt/page-agent.svg)](https://www.npmjs.com/package/page-agent) [![GitHub stars](https://img.shields.io/github/stars/alibaba/page-agent.svg)](https://github.com/alibaba/page-agent)
@@ -50,10 +50,10 @@ Fastest way to try PageAgent with our free Demo LLM:
> **⚠️ 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).
| Mirrors | URL |
| ------- | ---------------------------------------------------------------------------------- |
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.10.0/dist/iife/page-agent.demo.js |
| China | https://registry.npmmirror.com/page-agent/1.10.0/files/dist/iife/page-agent.demo.js |
| Mirrors | URL |
| ------- | ----------------------------------------------------------------------------------- |
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.11.0/dist/iife/page-agent.demo.js |
| China | https://registry.npmmirror.com/page-agent/1.11.0/files/dist/iife/page-agent.demo.js |
Add `?autoInit=false` to load the script without creating the demo agent automatically. You can then instantiate it with `new window.PageAgent(...)`.
+6 -6
View File
@@ -1,8 +1,8 @@
# Page Agent
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://img.alicdn.com/imgextra/i4/O1CN01qKig1P1FnhpFKNdi6_!!6000000000532-2-tps-1280-256.png">
<img alt="Page Agent Banner" src="https://img.alicdn.com/imgextra/i1/O1CN01NCMKXj1Gn4tkFTsxf_!!6000000000666-2-tps-1280-256.png">
<source media="(prefers-color-scheme: dark)" srcset="https://page-agent.github.io/assets/readme/banner-dark.png">
<img alt="Page Agent Banner" src="https://page-agent.github.io/assets/readme/banner-light.png">
</picture>
[![License: MIT](https://img.shields.io/badge/License-MIT-auto.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/) [![Bundle Size](https://img.shields.io/bundlephobia/minzip/page-agent)](https://bundlephobia.com/package/page-agent) [![Downloads](https://img.shields.io/npm/dt/page-agent.svg)](https://www.npmjs.com/package/page-agent) [![GitHub stars](https://img.shields.io/github/stars/alibaba/page-agent.svg)](https://github.com/alibaba/page-agent)
@@ -49,10 +49,10 @@ https://github.com/user-attachments/assets/a1f2eae2-13fb-4aae-98cf-a3fc1620a6c2
> **⚠️ 仅用于技术评估。** 该 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)。
| Mirrors | URL |
| ------- | ---------------------------------------------------------------------------------- |
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.10.0/dist/iife/page-agent.demo.js |
| China | https://registry.npmmirror.com/page-agent/1.10.0/files/dist/iife/page-agent.demo.js |
| Mirrors | URL |
| ------- | ----------------------------------------------------------------------------------- |
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.11.0/dist/iife/page-agent.demo.js |
| China | https://registry.npmmirror.com/page-agent/1.11.0/files/dist/iife/page-agent.demo.js |
在 URL 后添加 `?autoInit=false` 可只加载脚本,不自动创建 Demo Agent;之后可通过 `new window.PageAgent(...)` 手动初始化。
+1 -1
View File
@@ -91,7 +91,7 @@ npm run build:ext
- Add a new bookmark
```javascript
javascript:(function(){var s=document.createElement('script');s.src=`http://localhost:5174/page-agent.demo.js?t=${Math.random()}`;s.onload=()=>console.log(%27PageAgent ready!%27);document.head.appendChild(s);})();
javascript:(function(){var s=document.createElement('script');s.src=`http://localhost:5174/page-agent.demo.js?lang=en-US&t=${Math.random()}`;s.onload=()=>console.log(%27PageAgent ready!%27);document.head.appendChild(s);})();
```
- Click the bookmark on any page to load Page-Agent
+1
View File
@@ -53,6 +53,7 @@ export default defineConfig([
'@typescript-eslint/use-unknown-in-catch-callback-variable': 'off',
'@typescript-eslint/no-unnecessary-type-parameters': 'off',
'@typescript-eslint/require-await': 'off',
'@typescript-eslint/no-deprecated': 'off',
'@eslint-react/dom-no-missing-button-type': 'off',
'@eslint-react/no-nested-component-definitions': 'off',
'@eslint-react/no-array-index-key': 'off',
+1338 -1272
View File
File diff suppressed because it is too large Load Diff
+18 -23
View File
@@ -1,7 +1,7 @@
{
"name": "root",
"private": true,
"version": "1.10.0",
"version": "1.11.0",
"type": "module",
"workspaces": [
"packages/page-controller",
@@ -43,38 +43,33 @@
"prepare": "husky || true"
},
"devDependencies": {
"@commitlint/cli": "^21.0.1",
"@commitlint/config-conventional": "^21.0.1",
"@eslint-react/eslint-plugin": "^5.8.16",
"@commitlint/cli": "^21.2.0",
"@commitlint/config-conventional": "^21.2.0",
"@eslint-react/eslint-plugin": "^5.10.0",
"@eslint/js": "^10.0.1",
"@microsoft/api-extractor": "^7.58.8",
"@tailwindcss/vite": "^4.3.0",
"@microsoft/api-extractor": "^7.58.9",
"@tailwindcss/vite": "^4.3.2",
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/node": "^25.9.2",
"@vitejs/plugin-react": "^6.0.2",
"@types/node": "^26.0.1",
"@vitejs/plugin-react": "^6.0.3",
"chalk": "^5.6.2",
"concurrently": "^10.0.3",
"dotenv": "^17.4.2",
"eslint": "^10.4.0",
"globals": "^17.6.0",
"eslint": "^10.6.0",
"globals": "^17.7.0",
"happy-dom": "^20.10.6",
"husky": "^9.1.7",
"lint-staged": "^17.0.5",
"prettier": "^3.8.3",
"lint-staged": "^17.0.8",
"prettier": "^3.9.4",
"typescript": "^6.0.3",
"typescript-eslint": "^8.61.0",
"unplugin-dts": "^1.0.1",
"vite": "^8.0.14",
"typescript-eslint": "^8.62.1",
"unplugin-dts": "^1.0.3",
"vite": "^8.1.2",
"vite-plugin-css-injected-by-js": "^5.0.1",
"vitest": "^4.1.8"
"vitest": "^4.1.9"
},
"overrides": {
"typescript": "^6.0.3",
"node-notifier": {
"uuid": "11.1.1"
},
"web-ext-run": {
"tmp": "0.2.7"
}
"typescript": "^6.0.3"
},
"lint-staged": {
"*.{js,ts,cjs,cts,mjs,mts}": [
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@page-agent/core",
"private": false,
"version": "1.10.0",
"version": "1.11.0",
"type": "module",
"main": "./src/PageAgentCore.ts",
"types": "./src/PageAgentCore.ts",
@@ -55,8 +55,8 @@
},
"dependencies": {
"chalk": "^5.6.2",
"@page-agent/llms": "1.10.0",
"@page-agent/page-controller": "1.10.0"
"@page-agent/llms": "1.11.0",
"@page-agent/page-controller": "1.11.0"
},
"peerDependencies": {
"zod": "^3.25.0 || ^4.0.0"
+1 -1
View File
@@ -37,7 +37,7 @@ and system messages wrapped in <sys> tag.
<user_request>
USER REQUEST: This is your ultimate objective and always remains visible.
- This has the highest priority. Make the user happy.
- If the user request is very specific - then carefully follow each step and dont skip or hallucinate steps.
- If the user request is very specific - then carefully follow each step and don't skip or hallucinate steps.
- If the task is open ended you can plan yourself how to get it done.
</user_request>
+1 -5
View File
@@ -255,11 +255,7 @@ export interface AgentErrorEvent {
* Union type for all history events
*/
export type HistoricalEvent =
| AgentStepEvent
| ObservationEvent
| UserTakeoverEvent
| RetryEvent
| AgentErrorEvent
AgentStepEvent | ObservationEvent | UserTakeoverEvent | RetryEvent | AgentErrorEvent
/**
* Agent lifecycle status.
+4
View File
@@ -96,6 +96,10 @@ Returns: `Promise<ExecutionResult>`
Stop the current task.
## Limitations
- **Normal browser windows only.** This extension relies on tab group API which does not work for pop-up window or PWA App window.
## Types
Install `@page-agent/core` for complete types:
+19 -19
View File
@@ -1,7 +1,7 @@
{
"name": "@page-agent/ext",
"private": true,
"version": "1.10.0",
"version": "1.11.0",
"type": "module",
"scripts": {
"dev": "wxt",
@@ -10,38 +10,38 @@
"postinstall": "wxt prepare"
},
"devDependencies": {
"@radix-ui/react-hover-card": "^1.1.16",
"@radix-ui/react-hover-card": "^1.1.18",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.9",
"@radix-ui/react-separator": "^1.1.9",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.3.0",
"@tailwindcss/vite": "^4.3.0",
"@types/chrome": "^0.1.43",
"@radix-ui/react-label": "^2.1.11",
"@radix-ui/react-separator": "^1.1.11",
"@radix-ui/react-slot": "^1.3.0",
"@radix-ui/react-switch": "^1.3.2",
"@tailwindcss/vite": "^4.3.2",
"@types/chrome": "^0.2.1",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"@wxt-dev/module-react": "^1.2.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"idb": "^8.0.3",
"lucide-react": "^1.16.0",
"motion": "^12.40.0",
"lucide-react": "^1.22.0",
"motion": "^12.42.2",
"next-themes": "^0.4.6",
"react": "^19.2.6",
"react-dom": "^19.2.6",
"react": "^19.2.7",
"react-dom": "^19.2.7",
"rough-notation": "^0.5.1",
"simple-icons": "^16.23.0",
"simple-icons": "^16.24.1",
"sonner": "^2.0.7",
"tailwind-merge": "^3.6.0",
"tailwindcss": "^4.3.0",
"tailwindcss": "^4.3.2",
"tw-animate-css": "^1.4.0",
"wxt": "^0.20.26"
"wxt": "^0.20.27"
},
"dependencies": {
"@page-agent/core": "1.10.0",
"@page-agent/llms": "1.10.0",
"@page-agent/page-controller": "1.10.0",
"@page-agent/ui": "1.10.0",
"@page-agent/core": "1.11.0",
"@page-agent/llms": "1.11.0",
"@page-agent/page-controller": "1.11.0",
"@page-agent/ui": "1.11.0",
"ai-motion": "^0.4.8",
"chalk": "^5.6.2"
},
@@ -7,6 +7,39 @@ const PREFIX = '[TabsController.background]'
const debug = console.debug.bind(console, `\x1b[90m${PREFIX}\x1b[0m`)
/**
* Resolve active tab.
*
* - `tabs.query({ active: true })` does not work in multi-window scenarios.
* - Extension pages (side panel, hub tab) can resolve their own windowId.
* We just find the active tab within that window.
* - Content scripts (PAGE_AGENT_EXT) can't self-report a windowId.
* Chrome populates `sender.tab` for every content-script message,
* which is the tab hosting the script.
*/
async function resolveActiveTab(
payload: { windowId?: number } | undefined,
sender: chrome.runtime.MessageSender
): Promise<chrome.tabs.Tab> {
const windowId = payload?.windowId
if (windowId != null) {
debug('get_active_tab: resolving via caller-reported windowId', windowId)
const [tab] = await chrome.tabs.query({ active: true, windowId })
if (!tab) throw new Error(`No active tab found in window ${windowId}.`)
return tab
}
if (sender.tab) {
debug('get_active_tab: resolving via sender.tab (content script)', sender.tab.id)
return sender.tab
}
throw new Error(
'Cannot resolve active tab: caller reported no windowId and is not a content script (no sender.tab).'
)
}
export function handleTabControlMessage(
message: { type: 'TAB_CONTROL'; action: TabAction; payload: any },
sender: chrome.runtime.MessageSender,
@@ -16,12 +49,11 @@ export function handleTabControlMessage(
switch (action as TabAction) {
case 'get_active_tab': {
debug('get_active_tab')
chrome.tabs
.query({ active: true })
.then((tabs) => {
debug('get_active_tab: success', tabs)
sendResponse({ success: true, tab: tabs[0] })
debug('get_active_tab', payload)
resolveActiveTab(payload, sender)
.then((tab) => {
debug('get_active_tab: success', tab)
sendResponse({ success: true, tab })
})
.catch((error) => {
sendResponse({ error: error instanceof Error ? error.message : String(error) })
@@ -46,7 +78,7 @@ export function handleTabControlMessage(
case 'open_new_tab': {
debug('open_new_tab', payload)
chrome.tabs
.create({ url: payload.url, active: false })
.create({ url: payload.url, windowId: payload.windowId, active: false })
.then((newTab) => {
debug('open_new_tab: success', newTab)
sendResponse({ success: true, tabId: newTab.id })
+20 -2
View File
@@ -15,6 +15,20 @@ function sendMessage(message: {
})
}
/**
* Resolve the window hosting this script's own context, when knowable.
*
* Extension pages (side panel, hub tab) have `chrome.windows` access and can
* identify their own window directly via `getCurrent()`.
* Content scripts have no `chrome.windows` access; they resolve `undefined`
* here and the background script falls back to `sender.tab` instead.
*/
async function getOwnWindowId(): Promise<number | undefined> {
if (typeof chrome.windows === 'undefined') return undefined
const win = await chrome.windows.getCurrent()
return win.id
}
/**
* Controller for managing browser tabs.
* - live in the agent env (extension page or content script)
@@ -57,6 +71,7 @@ export class TabsController {
const activeTabResult = await sendMessage({
type: 'TAB_CONTROL',
action: 'get_active_tab',
payload: { windowId: await getOwnWindowId() },
})
this.initialTabId = activeTabResult.tab?.id
@@ -124,7 +139,7 @@ export class TabsController {
const result = await sendMessage({
type: 'TAB_CONTROL',
action: 'open_new_tab',
payload: { url },
payload: { url, windowId: this.windowId },
})
if (!result.success) {
@@ -307,7 +322,10 @@ export class TabsController {
if (message.action === 'created') {
const tab = message.payload.tab as chrome.tabs.Tab
const shouldTrack = this.experimentalIncludeAllTabs || tab.groupId === this.tabGroupId
const shouldTrack =
tab.groupId === this.tabGroupId ||
// @note Never track tabs from other windows.
(this.experimentalIncludeAllTabs && tab.windowId === this.windowId)
if (shouldTrack && tab.id != null) {
this.addTab({ id: tab.id, isInitial: false })
this.switchToTab(tab.id)
@@ -37,7 +37,7 @@ and system messages wrapped in <sys> tag.
<user_request>
USER REQUEST: This is your ultimate objective and always remains visible.
- This has the highest priority. Make the user happy.
- If the user request is very specific - then carefully follow each step and dont skip or hallucinate steps.
- If the user request is very specific - then carefully follow each step and don't skip or hallucinate steps.
- If the task is open ended you can plan yourself how to get it done.
</user_request>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@page-agent/llms",
"version": "1.10.0",
"version": "1.11.0",
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
+16 -5
View File
@@ -4,17 +4,24 @@
import * as z from 'zod/v4'
import { InvokeError, InvokeErrorTypes } from './errors'
import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
import type {
InvokeOptions,
InvokeResult,
LLMClient,
Message,
ResolvedLLMConfig,
Tool,
} from './types'
import { modelPatch, zodToOpenAITool } from './utils'
/**
* Client for OpenAI compatible APIs
*/
export class OpenAIClient implements LLMClient {
config: Required<LLMConfig>
config: ResolvedLLMConfig
private fetch: typeof globalThis.fetch
constructor(config: Required<LLMConfig>) {
constructor(config: ResolvedLLMConfig) {
this.config = config
this.fetch = config.customFetch
}
@@ -39,14 +46,18 @@ export class OpenAIClient implements LLMClient {
const requestBody: Record<string, unknown> = {
model: this.config.model,
temperature: this.config.temperature,
messages,
tools: openaiTools,
parallel_tool_calls: false,
tool_choice: toolChoice,
}
// Only sent if the caller explicitly set it. Most new models throw if this is set.
if (this.config.temperature !== undefined) {
requestBody.temperature = this.config.temperature
}
modelPatch(requestBody, this.config.baseURL)
modelPatch(requestBody)
let transformedBody: Record<string, unknown> | undefined
try {
transformedBody = this.config.transformRequestBody(requestBody)
-3
View File
@@ -1,3 +0,0 @@
// Internal constants
export const LLM_MAX_RETRIES = 2
export const DEFAULT_TEMPERATURE = 0.7 // higher randomness helps auto-recovery
+41 -24
View File
@@ -1,34 +1,23 @@
import { OpenAIClient } from './OpenAIClient'
import { DEFAULT_TEMPERATURE, LLM_MAX_RETRIES } from './constants'
import { InvokeError, InvokeErrorTypes } from './errors'
import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
import type {
InvokeOptions,
InvokeResult,
LLMClient,
LLMConfig,
Message,
ResolvedLLMConfig,
Tool,
} from './types'
export { InvokeError, InvokeErrorTypes }
export type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool }
export function parseLLMConfig(config: LLMConfig): Required<LLMConfig> {
// Runtime validation as defensive programming (types already guarantee these)
if (!config.baseURL || !config.model) {
throw new Error(
'[PageAgent] LLM configuration required. Please provide: baseURL, model. ' +
'See: https://alibaba.github.io/page-agent/docs/features/models'
)
}
return {
baseURL: config.baseURL,
model: config.model,
apiKey: config.apiKey || '',
temperature: config.temperature ?? DEFAULT_TEMPERATURE,
maxRetries: config.maxRetries ?? LLM_MAX_RETRIES,
transformRequestBody: config.transformRequestBody ?? ((requestBody) => requestBody),
disableNamedToolChoice: config.disableNamedToolChoice ?? false,
customFetch: (config.customFetch ?? fetch).bind(globalThis), // fetch will be illegal unless bound
}
}
/**
* LLM module
*/
export class LLM extends EventTarget {
config: Required<LLMConfig>
config: ResolvedLLMConfig
client: LLMClient
constructor(config: LLMConfig) {
@@ -90,3 +79,31 @@ async function withRetry<T>(
}
}
}
export function parseLLMConfig(config: LLMConfig): ResolvedLLMConfig {
// Runtime validation as defensive programming (types already guarantee these)
if (!config.baseURL || !config.model) {
throw new Error(
'[PageAgent] LLM configuration required. Please provide: baseURL, model. ' +
'See: https://alibaba.github.io/page-agent/docs/features/models'
)
}
if (config.temperature !== undefined) {
console.warn(
'[PageAgent] LLMConfig.temperature is deprecated and will be removed in a future version. ' +
'Use transformRequestBody to set it only for models you have verified accept it.'
)
}
return {
baseURL: config.baseURL,
model: config.model,
apiKey: config.apiKey || '',
temperature: config.temperature,
maxRetries: config.maxRetries ?? 2,
transformRequestBody: config.transformRequestBody ?? ((requestBody) => requestBody),
disableNamedToolChoice: config.disableNamedToolChoice ?? false,
customFetch: (config.customFetch ?? fetch).bind(globalThis), // fetch will be illegal unless bound
}
}
+224
View File
@@ -0,0 +1,224 @@
/**
* Live compatibility test — hits real provider APIs.
*
* Purpose: verify request formatting and `modelPatch` (see `utils.ts`) work
* for every model below, across the providers that serve them. Not a
* correctness/quality eval — the tool call is trivial and forced via
* `toolChoiceName`, so a failure here means the request/response shape is
* wrong for that model, not that the model is "dumb".
*
* Tests `OpenAIClient` directly (not the `LLM` retry wrapper), so a failure
* always reflects the very first request/response — no retry can mask it.
*
* Each provider's tests skip (not fail) when its `TESTING_*_KEY` env var is
* absent, so this stays CI-safe. To actually run these, put keys in the
* repo-root `.env`:
*
* TESTING_OPENROUTER_KEY=...
* TESTING_DEEPSEEK_KEY=...
* TESTING_ALIYUN_KEY=...
*/
import { config as dotenvConfig } from 'dotenv'
import { dirname, resolve } from 'path'
import { fileURLToPath } from 'url'
import { describe, expect, it } from 'vitest'
import * as z from 'zod/v4'
import { OpenAIClient } from './OpenAIClient'
import { parseLLMConfig } from './index'
import type { Message, Tool } from './types'
const __dirname = dirname(fileURLToPath(import.meta.url))
dotenvConfig({ path: resolve(__dirname, '../../../.env'), quiet: true })
const TEST_TIMEOUT = 30_000
/**
* Mirrors `packages/website/src/pages/docs/features/models/page.tsx`.
* This package cannot depend on the website, so the list is duplicated here.
* Keep both lists in sync manually when models are added or renamed.
*/
const MODEL_GROUPS: Record<string, string[]> = {
Qwen: [
'qwen3.7-max',
'qwen3.7-plus',
'qwen3.6-max',
'qwen3.6-plus',
'qwen3.6-flash',
'qwen3.5-plus',
'qwen3.5-flash',
'qwen3-max',
],
OpenAI: [
'gpt-5.5',
'gpt-5.4',
'gpt-5.4-mini',
'gpt-5.4-nano',
'gpt-5.2',
'gpt-5.1',
'gpt-5',
'gpt-5-mini',
'gpt-4.1',
'gpt-4.1-mini',
],
DeepSeek: ['deepseek-v4-pro', 'deepseek-v4-flash', 'deepseek-3.2'],
Google: [
'gemini-3.5-flash',
'gemini-3.1-pro',
'gemini-3.1-flash-lite',
'gemini-2.5-pro',
'gemini-2.5-flash',
],
Anthropic: [
'claude-sonnet-5',
'claude-opus-4-8',
'claude-opus-4-7',
'claude-opus-4-6',
'claude-opus-4-5',
'claude-sonnet-4-5',
'claude-haiku-4-5',
],
MiniMax: ['MiniMax-M3', 'MiniMax-M2.7', 'MiniMax-M2.5'],
xAI: ['grok-4.3', 'grok-build-0.1'],
MoonshotAI: ['kimi-k2.7-code', 'kimi-k2.6', 'kimi-k2.5'],
'Z.AI': ['glm-5.2', 'glm-5.1', 'glm-5', 'glm-4.7'],
}
/**
* OpenRouter lists every model as `<vendor-slug>/<model-id>`, lowercase.
* See the commented-out entries in the repo-root `.env` for real examples,
* e.g. `x-ai/grok-4.1-fast`, `qwen/qwen3-coder-next`, `deepseek/deepseek-v3.2-exp`.
*/
const OPENROUTER_VENDOR_SLUG: Record<string, string> = {
Qwen: 'qwen',
OpenAI: 'openai',
DeepSeek: 'deepseek',
Google: 'google',
Anthropic: 'anthropic',
MiniMax: 'minimax',
xAI: 'x-ai',
MoonshotAI: 'moonshotai',
'Z.AI': 'z-ai',
}
/**
* Overrides for models whose OpenRouter id doesn't match the
* `<vendor-slug>/<lowercased-name>` heuristic — dated snapshots, "-preview"
* suffixes, "v"-prefixed versions, or dots instead of hyphens in the
* version number. Verified against `GET https://openrouter.ai/api/v1/models`
* on 2026-07-03; re-check when models are added to `MODEL_GROUPS`.
*/
const OPENROUTER_ID_OVERRIDES: Record<string, string> = {
'qwen3.6-max': 'qwen/qwen3.6-max-preview',
'qwen3.5-plus': 'qwen/qwen3.5-plus-20260420',
'qwen3.5-flash': 'qwen/qwen3.5-flash-02-23',
'deepseek-3.2': 'deepseek/deepseek-v3.2',
'gemini-3.1-pro': 'google/gemini-3.1-pro-preview',
'claude-opus-4-8': 'anthropic/claude-opus-4.8',
'claude-opus-4-7': 'anthropic/claude-opus-4.7',
'claude-opus-4-6': 'anthropic/claude-opus-4.6',
'claude-opus-4-5': 'anthropic/claude-opus-4.5',
'claude-sonnet-4-5': 'anthropic/claude-sonnet-4.5',
'claude-haiku-4-5': 'anthropic/claude-haiku-4.5',
}
/**
* Derive a model's OpenRouter id from its brand (a `MODEL_GROUPS` key) and
* native model name.
*/
function toOpenRouterModelId(brand: string, model: string): string {
if (model in OPENROUTER_ID_OVERRIDES) return OPENROUTER_ID_OVERRIDES[model]
const slug = OPENROUTER_VENDOR_SLUG[brand]
if (!slug) throw new Error(`No OpenRouter vendor slug mapped for brand "${brand}"`)
return `${slug}/${model.toLowerCase()}`
}
const PROVIDERS = {
openrouter: {
baseURL: 'https://openrouter.ai/api/v1',
apiKey: process.env.TESTING_OPENROUTER_KEY,
},
aliyun: {
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
apiKey: process.env.TESTING_ALIYUN_KEY,
},
deepseek: {
baseURL: 'https://api.deepseek.com',
apiKey: process.env.TESTING_DEEPSEEK_KEY,
},
} as const
const ECHO_TOOL: Tool<{ message: string }, string> = {
description: 'Echo back the given message in uppercase.',
inputSchema: z.object({ message: z.string() }),
execute: async ({ message }) => message.toUpperCase(),
}
const PROMPT: Message[] = [
{
role: 'user',
content: 'Call the "echo" tool with message set to "ping". You must call the tool.',
},
]
async function expectEchoToolCall(baseURL: string, apiKey: string, model: string) {
const client = new OpenAIClient(parseLLMConfig({ baseURL, apiKey, model }))
const result = await client.invoke(PROMPT, { echo: ECHO_TOOL }, new AbortController().signal, {
toolChoiceName: 'echo',
})
expect(result.toolResult).toBe('PING')
}
describe.concurrent('OpenRouter — all listed models', () => {
const { baseURL, apiKey } = PROVIDERS.openrouter
for (const [brand, models] of Object.entries(MODEL_GROUPS)) {
for (const model of models) {
const id = toOpenRouterModelId(brand, model)
it.skipIf(!apiKey)(
id,
async () => {
await expectEchoToolCall(baseURL, apiKey!, id)
},
TEST_TIMEOUT
)
}
}
})
// Aliyun native ids that don't match the display name in MODEL_GROUPS.
const ALIYUN_ID_OVERRIDES: Record<string, string> = {
'qwen3.6-max': 'qwen3.6-max-preview',
}
describe.concurrent('Aliyun DashScope — Qwen native', () => {
const { baseURL, apiKey } = PROVIDERS.aliyun
for (const model of MODEL_GROUPS.Qwen) {
const id = ALIYUN_ID_OVERRIDES[model] ?? model
it.skipIf(!apiKey)(
id,
async () => {
await expectEchoToolCall(baseURL, apiKey!, id)
},
TEST_TIMEOUT
)
}
})
describe.concurrent('DeepSeek — native', () => {
const { baseURL, apiKey } = PROVIDERS.deepseek
// deepseek-3.2 isn't served on DeepSeek's own API (only via OpenRouter
// resellers) — its official API only accepts deepseek-v4-pro/-flash.
const nativeModels = MODEL_GROUPS.DeepSeek.filter((model) => model !== 'deepseek-3.2')
for (const model of nativeModels) {
it.skipIf(!apiKey)(
model,
async () => {
await expectEchoToolCall(baseURL, apiKey!, model)
},
TEST_TIMEOUT
)
}
})
+7
View File
@@ -92,7 +92,12 @@ export interface LLMConfig {
model: string
apiKey?: string
/**
* @deprecated No longer a standard parameter; many models reject it outright.
* Use `transformRequestBody` to set it only for models you've verified.
*/
temperature?: number
maxRetries?: number
/**
@@ -118,3 +123,5 @@ export interface LLMConfig {
*/
customFetch?: typeof globalThis.fetch
}
export type ResolvedLLMConfig = Required<Omit<LLMConfig, 'temperature'>> & { temperature?: number }
+10 -157
View File
@@ -1,162 +1,15 @@
import { describe, expect, it } from 'vitest'
import { modelPatch } from './utils'
import { modelPatch, normalizeModelName } from './utils'
/**
* Baseline request body used as starting point for each provider test.
* Mirrors what OpenAIClient builds before calling modelPatch.
*/
function baseBody(model: string) {
return {
model,
temperature: 0.7,
messages: [],
tools: [],
parallel_tool_calls: false,
tool_choice: 'required' as unknown,
}
}
describe('modelPatch', () => {
it('returns body unchanged when model is missing', () => {
const body = { temperature: 0.7 }
expect(modelPatch(body)).toBe(body)
expect(body).toEqual({ temperature: 0.7 })
})
it('qwen: bumps temperature and disables thinking', () => {
const body = baseBody('qwen-max')
modelPatch(body)
expect(body.temperature).toBe(1.0)
expect(body).toMatchObject({ enable_thinking: false })
})
it('qwen: keeps higher caller-provided temperature', () => {
const body = baseBody('qwen-max')
body.temperature = 1.5
modelPatch(body)
expect(body.temperature).toBe(1.5)
})
it('claude: disables thinking and converts tool_choice "required" -> { type: "any" }', () => {
const body = baseBody('claude-3-5-sonnet')
modelPatch(body)
expect(body).toMatchObject({
thinking: { type: 'disabled' },
tool_choice: { type: 'any' },
})
})
it('claude: converts named tool_choice to { type: "tool", name }', () => {
const body = baseBody('claude-3-5-sonnet')
body.tool_choice = { type: 'function', function: { name: 'doStuff' } }
modelPatch(body)
expect(body.tool_choice).toEqual({ type: 'tool', name: 'doStuff' })
})
it('claude-opus-4-7: drops temperature', () => {
const body = baseBody('claude-opus-4-7')
modelPatch(body)
expect(body).not.toHaveProperty('temperature')
})
it('claude-opus-47 (alt id form): drops temperature', () => {
// Provider sometimes ships ids with the dot stripped; modelPatch normalizes.
const body = baseBody('claude-opus-47-20251029')
modelPatch(body)
expect(body).not.toHaveProperty('temperature')
})
it('claude-opus-4-8: drops temperature', () => {
const body = baseBody('claude-opus-4-8')
modelPatch(body)
expect(body).not.toHaveProperty('temperature')
})
it('claude-opus-48 (alt id form): drops temperature', () => {
const body = baseBody('claude-opus-48-20251210')
modelPatch(body)
expect(body).not.toHaveProperty('temperature')
})
it('grok: removes tool_choice and disables reasoning/thinking', () => {
const body = baseBody('grok-4')
modelPatch(body)
expect(body).not.toHaveProperty('tool_choice')
expect(body).toMatchObject({
thinking: { type: 'disabled', effort: 'minimal' },
reasoning: { enabled: false, effort: 'low' },
})
})
it('gpt-5: sets verbosity=low and reasoning_effort=low', () => {
const body = baseBody('gpt-5')
modelPatch(body)
expect(body).toMatchObject({ verbosity: 'low', reasoning_effort: 'low' })
})
it('gpt-5-mini: low effort, temperature=1', () => {
const body = baseBody('gpt-5-mini')
modelPatch(body)
expect(body).toMatchObject({
verbosity: 'low',
reasoning_effort: 'low',
temperature: 1,
})
})
it('gpt-5.1 (gpt-51): disables reasoning', () => {
const body = baseBody('gpt-5.1')
modelPatch(body)
expect(body).toMatchObject({ verbosity: 'low', reasoning_effort: 'none' })
})
it('gpt-5.4 (gpt-54): drops reasoning_effort', () => {
const body = baseBody('gpt-5.4')
modelPatch(body)
expect(body).toMatchObject({ verbosity: 'low' })
expect(body).not.toHaveProperty('reasoning_effort')
})
it('gpt-5.5 (gpt-55): drops reasoning_effort and temperature', () => {
const body = baseBody('gpt-5.5')
modelPatch(body)
expect(body).toMatchObject({ verbosity: 'low' })
expect(body).not.toHaveProperty('reasoning_effort')
expect(body).not.toHaveProperty('temperature')
})
it('gemini: sets reasoning_effort=minimal', () => {
const body = baseBody('gemini-2.5-pro')
modelPatch(body)
expect(body).toMatchObject({ reasoning_effort: 'minimal' })
})
it('deepseek: removes tool_choice', () => {
const body = baseBody('deepseek-chat')
modelPatch(body)
expect(body).not.toHaveProperty('tool_choice')
})
it('minimax: clamps temperature into (0, 1] and removes parallel_tool_calls', () => {
const body = baseBody('minimax-m2')
body.temperature = 0
modelPatch(body)
expect(body.temperature).toBeGreaterThan(0)
expect(body.temperature).toBeLessThanOrEqual(1)
expect(body).not.toHaveProperty('parallel_tool_calls')
})
it('minimax: caps temperature at 1', () => {
const body = baseBody('minimax-m2')
body.temperature = 2
modelPatch(body)
expect(body.temperature).toBe(1)
})
it('normalizes provider-prefixed model id (openai/gpt-5)', () => {
const body = baseBody('openai/gpt-5')
modelPatch(body)
expect(body).toMatchObject({ verbosity: 'low', reasoning_effort: 'low' })
describe('normalizeModelName', () => {
it.each([
['gpt-5.2', 'gpt-52'],
['gpt_5_2', 'gpt52'],
['GPT-52-2026-01-01', 'gpt-52-2026-01-01'],
['openai/gpt-5.2-chat', 'gpt-52-chat'],
['claude_sonnet4_5', 'claudesonnet45'],
])('%s -> %s', (input, expected) => {
expect(normalizeModelName(input)).toBe(expected)
})
})
+130 -67
View File
@@ -24,101 +24,152 @@ export function zodToOpenAITool(name: string, tool: Tool) {
}
/**
* Patch model specific parameters
* @note in-place modification
* Patch model specific parameters. Only patches known models.
*
* @purpose
* - Reconcile the differences in the parameter schema each model accepts.
* - Disable thinking/reasoning, or lower it to the minimum where a full disable is impossible.
* - Minimize returned tokens.
* - Raise temperature for known smaller models to improve auto-recovery odds.
* @note Honor temperature if explicitly set by the user
*
* @todo Need vendor-specific patches.
* Local and 3rd-party hosted models may have different schema.
*/
export function modelPatch(body: Record<string, any>) {
export function modelPatch(body: Record<string, any>, baseURL?: string) {
const model: string = body.model || ''
if (!model) return body
const provider = getProvider(baseURL)
const modelName = normalizeModelName(model)
if (modelName.startsWith('qwen')) {
debug('Applying Qwen patch: use higher temperature for auto fixing')
body.temperature = Math.max(body.temperature || 0, 1.0)
debug('Patch Qwen: disable thinking')
body.enable_thinking = false
if (body.temperature === undefined && !/max|plus/.test(modelName)) {
debug('Patch Qwen: raise temperature to 1.0')
body.temperature = 1.0
}
}
if (modelName.startsWith('claude')) {
debug('Applying Claude patch: disable thinking')
if (modelName.startsWith('deepseek')) {
debug('Patch DeepSeek: disable thinking, remove tool_choice')
body.thinking = { type: 'disabled' }
// Convert tool_choice to Claude format
if (body.tool_choice === 'required') {
// 'required' -> { type: 'any' } (must call some tool)
debug('Applying Claude patch: convert tool_choice "required" to { type: "any" }')
body.tool_choice = { type: 'any' }
} else if (body.tool_choice?.function?.name) {
// { type: 'function', function: { name: '...' } } -> { type: 'tool', name: '...' }
debug('Applying Claude patch: convert tool_choice format')
body.tool_choice = { type: 'tool', name: body.tool_choice.function.name }
}
// TODO: Claude naming pattern has changed
// needs proper handling
if (
modelName.startsWith('claude-opus-4-7') ||
modelName.startsWith('claude-opus-47') ||
modelName.startsWith('claude-opus-4-8') ||
modelName.startsWith('claude-opus-48')
) {
debug('Applying Claude-4.7/4.8 patch: remove temperature')
delete body.temperature
}
}
if (modelName.startsWith('grok')) {
debug('Applying Grok patch: removing tool_choice')
delete body.tool_choice
debug('Applying Grok patch: disable reasoning and thinking')
body.thinking = { type: 'disabled', effort: 'minimal' }
body.reasoning = { enabled: false, effort: 'low' }
}
if (modelName.startsWith('gpt')) {
debug('Applying GPT patch: set verbosity to low')
body.verbosity = 'low'
if (modelName.startsWith('gpt-5')) {
body.verbosity = 'low'
if (modelName.startsWith('gpt-52')) {
debug('Applying GPT-52 patch: disable reasoning')
body.reasoning_effort = 'none'
} else if (modelName.startsWith('gpt-51')) {
debug('Applying GPT-51 patch: disable reasoning')
body.reasoning_effort = 'none'
} else if (modelName.startsWith('gpt-54')) {
debug('Applying GPT-5.4 patch: remove reasoning_effort')
delete body.reasoning_effort
} else if (modelName.startsWith('gpt-55')) {
debug('Applying GPT-5.4 patch: remove reasoning_effort and temperature')
// gpt-5 gpt-5-mini gpt-5-nano only supports "minimal";
// 5.1+ supports "none".
body.reasoning_effort = /^gpt-5(-|$)/.test(modelName) ? 'minimal' : 'none'
debug(`Patch GPT-5: verbosity=low, reasoning_effort=${body.reasoning_effort}`)
}
if (modelName.includes('chat-latest')) {
debug('Omitting reasoning_effort and temperature for chat-latest')
delete body.reasoning_effort
delete body.temperature
} else if (modelName.startsWith('gpt-5-mini')) {
debug('Applying GPT-5-mini patch: set reasoning effort to low, temperature to 1')
body.reasoning_effort = 'low'
body.temperature = 1
} else if (modelName.startsWith('gpt-5')) {
debug('Applying GPT-5 patch: set reasoning effort to low')
}
}
if (modelName.startsWith('claude')) {
if (/opus|sonnet|haiku/.test(modelName)) {
debug('Patch Claude: disable thinking')
body.thinking = { type: 'disabled' }
if (provider !== 'openrouter') {
// Convert tool_choice to Claude format
if (body.tool_choice === 'required') {
// 'required' -> { type: 'any' } (must call some tool)
debug('Applying Claude patch: convert tool_choice "required" to { type: "any" }')
body.tool_choice = { type: 'any' }
} else if (body.tool_choice?.function?.name) {
// { type: 'function', function: { name: '...' } } -> { type: 'tool', name: '...' }
debug('Applying Claude patch: convert tool_choice format')
body.tool_choice = { type: 'tool', name: body.tool_choice.function.name }
}
}
} else {
debug('Patch Claude: reasoning_effort=low')
body.reasoning_effort = 'low'
// Fable and mythos can not disable adaptive thinking.
// Claude does not support tool_choice with extended thinking.
// These 2 concepts are blurred. Basically no tool_choice with thinking.
delete body.tool_choice
}
}
if (modelName.startsWith('gemini')) {
debug('Applying Gemini patch: set reasoning effort to minimal')
body.reasoning_effort = 'minimal'
debug('Patch Gemini: reasoning_effort=low')
body.reasoning_effort = 'low'
if (/^gemini-25(?!.*pro)/.test(modelName)) {
debug('Patch Gemini 2.5 non-Pro: reasoning_effort=none')
body.reasoning_effort = 'none'
} else if (
modelName.startsWith('gemini-35-flash') ||
modelName.startsWith('gemini-31-flash-lite') ||
modelName.startsWith('gemini-3-flash')
) {
debug('Patch Gemini 3.x Flash/Lite: reasoning_effort=minimal')
body.reasoning_effort = 'minimal'
}
}
if (modelName.startsWith('deepseek')) {
debug('Applying DeepSeek patch: remove tool_choice')
delete body.tool_choice
if (modelName.startsWith('glm')) {
debug('Patch GLM: disable thinking')
body.thinking = { type: 'disabled' }
}
if (modelName.startsWith('grok')) {
if (/^grok-4-?3/.test(modelName)) {
debug('Patch Grok 4.3: reasoning_effort=none')
body.reasoning_effort = 'none'
} else if (modelName.startsWith('grok-3-mini') || modelName.startsWith('grok-code-fast')) {
debug('Patch Grok mini/code: reasoning_effort=low')
body.reasoning_effort = 'low'
}
}
if (modelName.startsWith('kimi')) {
if (!modelName.includes('code')) {
// kimi-k2.7-code cannot disable thinking
debug('Patch Kimi: disable thinking')
body.thinking = { type: 'disabled' }
}
}
if (modelName.startsWith('minimax')) {
debug('Applying MiniMax patch: clamp temperature to (0, 1]')
// MiniMax API rejects temperature = 0; clamp to a small positive value
body.temperature = Math.max(body.temperature || 0, 0.01)
if (body.temperature > 1) body.temperature = 1
// MiniMax does not support parallel_tool_calls
debug('Patch MiniMax: remove parallel_tool_calls')
delete body.parallel_tool_calls
if (modelName.includes('m3')) {
// Only M3 can disable thinking
debug('Patch MiniMax: disable thinking')
body.thinking = { type: 'disabled' }
}
}
// provider patches
if (provider === 'openrouter') {
// openrouter use reasoning object instead of reasoning_effort
const reasoningEffort = body.reasoning_effort
const reasoningDisabled =
body.thinking?.type === 'disabled' ||
body.enable_thinking === false ||
reasoningEffort === 'none'
if (reasoningDisabled) {
body.reasoning = { enabled: false }
} else if (reasoningEffort) {
body.reasoning = { enabled: true, effort: reasoningEffort }
}
}
return body
@@ -139,7 +190,7 @@ export function modelPatch(body: Record<string, any>) {
* They should be treated as the same model.
* Normalize them to `gpt-52`
*/
function normalizeModelName(modelName: string): string {
export function normalizeModelName(modelName: string): string {
let normalizedName = modelName.toLowerCase()
// remove prefix before '/'
@@ -155,3 +206,15 @@ function normalizeModelName(modelName: string): string {
return normalizedName
}
export function getProvider(baseURL?: string): 'openrouter' | undefined {
if (!baseURL) return undefined
try {
const url = new URL(baseURL)
const hostname = url.hostname
if (hostname === 'openrouter.ai') return 'openrouter'
return undefined
} catch (e) {
return undefined
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@page-agent/mcp",
"private": false,
"version": "1.10.0",
"version": "1.11.0",
"type": "module",
"bin": {
"page-agent-mcp": "src/index.js"
+3 -6
View File
@@ -3,10 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
rel="icon"
href="https://img.alicdn.com/imgextra/i1/O1CN01mRGret1QrKiu7CFJI_!!6000000002029-2-tps-64-64.png"
/>
<link rel="icon" href="https://page-agent.github.io/assets/brand/page-agent-color-64.png" />
<title>Page Agent MCP Launcher</title>
<style>
* {
@@ -145,7 +142,7 @@
<div class="card">
<img
class="logo"
src="https://img.alicdn.com/imgextra/i3/O1CN01JPT4Fj1FJTfmHfNxO_!!6000000000466-49-tps-512-512.webp"
src="https://page-agent.github.io/assets/brand/page-agent-color-512.webp"
alt="Page Agent"
/>
<div class="badge">Page Agent MCP Launcher</div>
@@ -164,7 +161,7 @@
<a class="store-btn" href="__STORE_URL__" target="_blank">
<img
src="https://img.alicdn.com/imgextra/i3/O1CN01JpW0Vo1sR3FpiZKFM_!!6000000005762-55-tps-192-192.svg"
src="https://page-agent.github.io/assets/third-party/chrome-web-store-192.svg"
alt=""
/>
<span data-i18n="install_btn">Install from Chrome Web Store</span>
+5 -5
View File
@@ -1,7 +1,7 @@
{
"name": "page-agent",
"private": false,
"version": "1.10.0",
"version": "1.11.0",
"type": "module",
"main": "./src/PageAgent.ts",
"types": "./src/PageAgent.ts",
@@ -54,10 +54,10 @@
"postpublish": "node ../../scripts/post-publish.js"
},
"dependencies": {
"@page-agent/core": "1.10.0",
"@page-agent/llms": "1.10.0",
"@page-agent/page-controller": "1.10.0",
"@page-agent/ui": "1.10.0",
"@page-agent/core": "1.11.0",
"@page-agent/llms": "1.11.0",
"@page-agent/page-controller": "1.11.0",
"@page-agent/ui": "1.11.0",
"chalk": "^5.6.2"
},
"peerDependencies": {
+1 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@page-agent/page-controller",
"version": "1.10.0",
"version": "1.11.0",
"type": "module",
"main": "./src/PageController.ts",
"types": "./src/PageController.ts",
@@ -48,8 +48,5 @@
},
"dependencies": {
"ai-motion": "^0.4.8"
},
"devDependencies": {
"happy-dom": "^20.10.2"
}
}
-2
View File
@@ -208,9 +208,7 @@ export async function inputTextElement(element: HTMLElement, text: string) {
selection?.removeAllRanges()
selection?.addRange(range)
// eslint-disable-next-line @typescript-eslint/no-deprecated
doc.execCommand('delete', false)
// eslint-disable-next-line @typescript-eslint/no-deprecated
doc.execCommand('insertText', false, text)
}
@@ -598,14 +598,12 @@ export default (
isAnyRectVisible = true
// Viewport check for this rect
if (
!(
rect.bottom < -viewportExpansion ||
rect.top > window.innerHeight + viewportExpansion ||
rect.right < -viewportExpansion ||
rect.left > window.innerWidth + viewportExpansion
)
) {
if (!(
rect.bottom < -viewportExpansion ||
rect.top > window.innerHeight + viewportExpansion ||
rect.right < -viewportExpansion ||
rect.left > window.innerWidth + viewportExpansion
)) {
isAnyRectInViewport = true
break // Found a visible rect in viewport, no need to check others
}
@@ -985,12 +983,10 @@ export default (
rect.height > 0 &&
!(
// Only check non-empty rects
(
rect.bottom < -viewportExpansion ||
rect.top > window.innerHeight + viewportExpansion ||
rect.right < -viewportExpansion ||
rect.left > window.innerWidth + viewportExpansion
)
rect.bottom < -viewportExpansion ||
rect.top > window.innerHeight + viewportExpansion ||
rect.right < -viewportExpansion ||
rect.left > window.innerWidth + viewportExpansion
)
) {
isAnyRectInViewport = true
@@ -1103,14 +1099,12 @@ export default (
for (const rect of rects) {
if (rect.width === 0 || rect.height === 0) continue // Skip empty rects
if (
!(
rect.bottom < -viewportExpansion ||
rect.top > window.innerHeight + viewportExpansion ||
rect.right < -viewportExpansion ||
rect.left > window.innerWidth + viewportExpansion
)
) {
if (!(
rect.bottom < -viewportExpansion ||
rect.top > window.innerHeight + viewportExpansion ||
rect.right < -viewportExpansion ||
rect.left > window.innerWidth + viewportExpansion
)) {
return true // Found at least one rect in the viewport
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@page-agent/ui",
"version": "1.10.0",
"version": "1.11.0",
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
@@ -1,64 +0,0 @@
import styles from './motion.module.css'
export function createMotion() {
const wrapper = document.createElement('div')
wrapper.className = styles.wrapper
{
const colorWrapper = document.createElement('div')
colorWrapper.className = styles.colorWrapper
wrapper.appendChild(colorWrapper)
const layerA = document.createElement('div')
layerA.className = styles.colorLayer + ' ' + styles.layerA
colorWrapper.appendChild(layerA)
const layerB = document.createElement('div')
layerB.className = styles.colorLayer + ' ' + styles.layerB
colorWrapper.appendChild(layerB)
const layerC = document.createElement('div')
layerC.className = styles.colorLayer + ' ' + styles.layerC
colorWrapper.appendChild(layerC)
}
{
const borderWrapper = document.createElement('div')
borderWrapper.className = styles.borderWrapper
wrapper.appendChild(borderWrapper)
const layerA = document.createElement('div')
layerA.className = styles.borderLayer + ' ' + styles.layerA
borderWrapper.appendChild(layerA)
const layerB = document.createElement('div')
layerB.className = styles.borderLayer + ' ' + styles.layerB
borderWrapper.appendChild(layerB)
const layerC = document.createElement('div')
layerC.className = styles.borderLayer + ' ' + styles.layerC
borderWrapper.appendChild(layerC)
}
function show() {
wrapper.classList.remove(styles.exit)
wrapper.classList.remove(styles.entry)
// Force reflow to restart animation
void wrapper.offsetHeight
wrapper.classList.add(styles.entry)
}
function hide() {
wrapper.classList.remove(styles.entry)
wrapper.classList.remove(styles.exit)
// Force reflow to restart animation
void wrapper.offsetHeight
wrapper.classList.add(styles.exit)
}
return {
element: wrapper,
show,
hide,
}
}
@@ -1,397 +0,0 @@
.wrapper {
position: absolute;
inset: 0;
pointer-events: none;
transform-origin: center;
--color-1: rgb(57, 182, 255);
--color-2: rgb(189, 69, 251);
--color-3: rgb(255, 87, 51);
--color-4: rgb(255, 214, 0);
--blend-mode: screen;
}
.colorLayer {
position: absolute;
inset: 0;
/* 变亮混合模式 */
/* mix-blend-mode: screen; */
/* mix-blend-mode: overlay; */
/* mix-blend-mode: multiply; */
mix-blend-mode: add;
/* 边框遮罩 - 中间透明,边缘不透明 */
mask-image: url(https://img.alicdn.com/imgextra/i2/O1CN01iW1wfX1C0ICvoPbTq_!!6000000000018-2-tps-512-512.png);
mask-repeat: no-repeat;
mask-size: calc(100% + 10px) calc(100% + 10px);
}
.borderWrapper {
position: absolute;
inset: 0;
/* filter: blur(10px); */
}
.borderLayer {
position: absolute;
inset: 0;
/* 变亮混合模式 */
/* mix-blend-mode: overlay; */
mix-blend-mode: add;
mask-image:
linear-gradient(
to right,
black 0px,
black 2px,
transparent 2px,
transparent calc(100% - 2px),
black calc(100% - 2px),
black 100%
),
linear-gradient(
to top,
black 0px,
black 2px,
transparent 2px,
transparent calc(100% - 2px),
black calc(100% - 2px),
black 100%
);
mask-composite: add;
mask-repeat: no-repeat;
mask-size: 100% 100%;
/* filter: blur(100px); */
}
.blueLayer {
&.colorLayer {
mask-position: left -5px top -5px;
}
&::after {
content: '';
position: absolute;
/* inset: 0; */
width: calc(max(100vw, 100vh) * 1.5);
height: 600px;
top: calc(50% - 300px);
left: 50%;
filter: blur(100px);
background: rgb(57, 182, 255);
animation: rotate-clockwise 4s linear infinite;
animation-delay: -3s;
}
}
.purpleLayer {
&.colorLayer {
mask-position: left -3px top -7px;
}
&::after {
content: '';
position: absolute;
/* inset: 0; */
width: calc(max(100vw, 100vh) * 1.5);
height: 600px;
top: calc(50% - 300px);
left: 50%;
filter: blur(100px);
background: rgb(189, 69, 251);
animation: rotate-clockwise 4s linear infinite;
animation-delay: -2s;
}
}
.orangeLayer {
/* opacity: 0.5; */
&.colorLayer {
mask-position: left -7px top -2px;
}
&::after {
content: '';
position: absolute;
/* inset: 0; */
width: calc(max(100vw, 100vh) * 1.5);
height: 600px;
top: calc(50% - 300px);
left: 50%;
filter: blur(100px);
background: rgb(255, 87, 51);
animation: rotate-counter-clockwise 3s linear infinite;
animation-delay: -2s;
}
}
.yellowLayer {
/* opacity: 0.5; */
&.colorLayer {
mask-position: left -6px top -4px;
}
&::after {
content: '';
position: absolute;
/* inset: 0; */
width: calc(max(100vw, 100vh) * 1.5);
height: 600px;
top: calc(50% - 300px);
left: 50%;
filter: blur(100px);
background: rgb(255, 214, 0);
animation: rotate-counter-clockwise 4s linear infinite;
animation-delay: -1s;
}
}
/* 旋转动画 */
@keyframes rotate-clockwise {
0% {
transform: translateX(-50%) rotate(0deg);
}
100% {
transform: translateX(-50%) rotate(360deg);
}
}
@keyframes rotate-counter-clockwise {
0% {
transform: translateX(-50%) rotate(0deg);
}
100% {
transform: translateX(-50%) rotate(-360deg);
}
}
@keyframes wrapper-entry {
from {
transform: scale(1.1);
}
to {
transform: scale(1);
}
}
/*
rgb(57, 182, 255)
rgb(189, 69, 251)
rgb(255, 87, 51)
rgb(255, 214, 0)
*/
@keyframes mask-running {
from {
transform: translateX(0%);
}
to {
transform: translateX(100%);
}
}
@keyframes mask-running-reverse {
from {
transform: translateX(100%);
}
to {
transform: translateX(0%);
}
}
.colorWrapper {
position: absolute;
inset: 0;
.colorLayer {
position: absolute;
inset: 0;
mix-blend-mode: var(--blend-mode);
/* 边框遮罩 - 中间透明,边缘不透明 */
mask-image: url(https://img.alicdn.com/imgextra/i2/O1CN01iW1wfX1C0ICvoPbTq_!!6000000000018-2-tps-512-512.png);
mask-repeat: no-repeat;
mask-size: 100% 100%;
}
}
.borderWrapper {
position: absolute;
inset: 0;
--blend-mode: lighten;
.borderLayer {
position: absolute;
inset: 0;
mix-blend-mode: var(--blend-mode);
mask-border: url(https://img.alicdn.com/imgextra/i3/O1CN01bFjRug1yssyWEUbKL_!!6000000006635-2-tps-256-256.png)
25;
-webkit-mask-box-image: url(https://img.alicdn.com/imgextra/i3/O1CN01bFjRug1yssyWEUbKL_!!6000000006635-2-tps-256-256.png)
25;
mask-repeat: no-repeat;
mask-size: 100% 100%;
background-color: var(--color-2);
}
}
.entry .colorWrapper,
.entry .borderWrapper {
animation: wrapper-entry 0.8s ease-in-out forwards;
}
.exit .colorWrapper,
.exit .borderWrapper {
animation: wrapper-entry 0.8s ease-in-out reverse forwards;
}
.layerA {
position: absolute;
inset: 0;
&::before {
mix-blend-mode: var(--blend-mode);
content: '';
display: block;
position: absolute;
width: 100%;
height: 100%;
left: -100%;
top: 0;
background-image: linear-gradient(
to right bottom,
transparent,
var(--color-1),
transparent,
var(--color-1),
transparent
);
animation: mask-running 2s linear infinite;
}
&::after {
mix-blend-mode: var(--blend-mode);
content: '';
display: block;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
background-image: linear-gradient(
to right bottom,
transparent,
var(--color-1),
transparent,
var(--color-1),
transparent
);
animation: mask-running 2s linear infinite;
}
}
.layerB {
position: absolute;
inset: 0;
&::before {
mix-blend-mode: var(--blend-mode);
content: '';
display: block;
position: absolute;
width: 100%;
height: 100%;
left: -100%;
top: 0;
background: linear-gradient(
to right top,
transparent,
var(--color-2),
transparent,
var(--color-2),
transparent
);
animation: mask-running-reverse 3s linear infinite;
}
&::after {
mix-blend-mode: var(--blend-mode);
content: '';
display: block;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
background: linear-gradient(
to right top,
transparent,
var(--color-2),
transparent,
var(--color-2),
transparent
);
animation: mask-running-reverse 3s linear infinite;
}
}
.layerC {
position: absolute;
inset: 0;
opacity: 0.5;
&::before {
mix-blend-mode: var(--blend-mode);
content: '';
display: block;
position: absolute;
width: 100%;
height: 100%;
left: -100%;
top: 0;
background: linear-gradient(
to right top,
transparent,
var(--color-3),
transparent,
var(--color-3),
transparent
);
animation: mask-running 1s linear infinite;
}
&::after {
mix-blend-mode: var(--blend-mode);
content: '';
display: block;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
background: linear-gradient(
to right top,
transparent,
var(--color-3),
transparent,
var(--color-3),
transparent
);
animation: mask-running 1s linear infinite;
}
}
-5
View File
@@ -1,5 +0,0 @@
This is the CSS implementation of ai-motion.
Easy to use but Terrible performance. Causing full screen glitching in some browsers.
Use it only in a small area.
+5 -3
View File
@@ -4,8 +4,8 @@
<meta charset="UTF-8" />
<link
rel="icon"
type="image/svg+xml"
href="https://img.alicdn.com/imgextra/i2/O1CN012eGDRI1X6nnMt9clU_!!6000000002875-49-tps-64-64.webp"
type="image/webp"
href="https://page-agent.github.io/assets/brand/page-agent-color-64.webp"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PageAgent - The GUI Agent Living in Your Webpage</title>
@@ -19,7 +19,7 @@
/>
<meta
property="og:image"
content="https://img.alicdn.com/imgextra/i3/O1CN01JPT4Fj1FJTfmHfNxO_!!6000000000466-49-tps-512-512.webp"
content="https://page-agent.github.io/assets/brand/page-agent-color-512.webp"
/>
<meta property="og:url" content="https://alibaba.github.io/page-agent" />
<meta property="og:type" content="website" />
@@ -33,6 +33,7 @@
<meta property="og:locale:alternate" content="zh_CN" />
<!-- Google tag (gtag.js) -->
<!-- cspell:disable -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-HCGRJTN3HM"></script>
<script>
window.dataLayer = window.dataLayer || []
@@ -43,6 +44,7 @@
gtag('config', 'G-HCGRJTN3HM')
</script>
<!-- cspell:enable -->
</head>
<body>
<div id="root">
+11 -11
View File
@@ -1,7 +1,7 @@
{
"name": "@page-agent/website",
"private": true,
"version": "1.10.0",
"version": "1.11.0",
"type": "module",
"scripts": {
"dev": "vite --host 0.0.0.0",
@@ -10,24 +10,24 @@
},
"devDependencies": {
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-separator": "^1.1.9",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.3.0",
"@radix-ui/react-tooltip": "^1.2.9",
"@radix-ui/react-separator": "^1.1.11",
"@radix-ui/react-slot": "^1.3.0",
"@radix-ui/react-switch": "^1.3.2",
"@radix-ui/react-tooltip": "^1.2.11",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^1.16.0",
"motion": "^12.40.0",
"lucide-react": "^1.22.0",
"motion": "^12.42.2",
"next-themes": "^0.4.6",
"react": "^19.2.6",
"react-dom": "^19.2.6",
"react": "^19.2.7",
"react-dom": "^19.2.7",
"rough-notation": "^0.5.1",
"simple-icons": "^16.23.0",
"simple-icons": "^16.24.1",
"sonner": "^2.0.7",
"tailwind-merge": "^3.6.0",
"tailwindcss": "^4.3.0",
"tailwindcss": "^4.3.2",
"tw-animate-css": "^1.4.0",
"wouter": "^3.10.0"
}
+1 -1
View File
@@ -31,7 +31,7 @@ export default function Header() {
onClick={() => setMobileMenuOpen(false)}
>
<img
src="https://img.alicdn.com/imgextra/i2/O1CN01HB8ylu1uozANEMZw2_!!6000000006085-49-tps-128-128.webp"
src="https://page-agent.github.io/assets/brand/page-agent-color-128.webp"
alt="PageAgent Logo"
className="w-10 h-10 rounded-xl group-hover:scale-110 transition-transform duration-200"
/>
+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.10.0/dist/iife/page-agent.demo.js'
'https://cdn.jsdelivr.net/npm/page-agent@1.11.0/dist/iife/page-agent.demo.js'
export const CDN_DEMO_CN_URL =
'https://registry.npmmirror.com/page-agent/1.10.0/files/dist/iife/page-agent.demo.js'
'https://registry.npmmirror.com/page-agent/1.11.0/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 -1
View File
@@ -9,13 +9,15 @@ export function useGitHubStars() {
useEffect(() => {
if (cached !== null) return
fetch(STATS_URL)
const controller = new AbortController()
fetch(STATS_URL, { signal: controller.signal })
.then((r) => r.json())
.then((data) => {
cached = data.stargazers_count ?? null
setStars(cached)
})
.catch(() => {})
return () => controller.abort()
}, [])
return stars
@@ -143,13 +143,6 @@ const result = await agent.execute('Fill in the form with test data')`}
required: false,
description: 'LLM AK',
},
{
name: 'temperature',
type: 'number',
description: isZh
? '模型温度参数,控制输出随机性'
: 'Model temperature, controls output randomness',
},
{
name: 'maxRetries',
type: 'number',
@@ -6,9 +6,9 @@ import { Heading } from '@/components/Heading'
import { useLanguage } from '@/i18n/context'
const BASELINE = new Set([
'gpt-5.1',
'gpt-5.4-mini',
'claude-haiku-4.5',
'gpt-5.4-nano',
'claude-haiku-4-5',
'gemini-3.5-flash',
'deepseek-v4-flash',
'qwen3.5-plus',
@@ -25,9 +25,8 @@ const MODEL_GROUPS: Record<string, string[]> = {
'qwen3.6-flash',
'qwen3.5-plus',
'qwen3.5-flash',
'qwen3-coder-next',
'qwen-3-max',
'qwen-3-plus',
'qwen3-max',
// 'qwen3-coder-next', // low success rate
],
OpenAI: [
'gpt-5.5',
@@ -44,24 +43,29 @@ const MODEL_GROUPS: Record<string, string[]> = {
DeepSeek: ['deepseek-v4-pro', 'deepseek-v4-flash', 'deepseek-3.2'],
Google: [
'gemini-3.5-flash',
'gemini-3.1-pro',
'gemini-3.1-flash-lite',
'gemini-3-pro',
'gemini-3-flash',
'gemini-2.5',
'gemini-2.5-pro',
'gemini-2.5-flash',
],
Anthropic: [
'claude-opus-4.8',
'claude-opus-4.7',
'claude-opus-4.6',
'claude-opus-4.5',
'claude-sonnet-4.5',
'claude-haiku-4.5',
'claude-sonnet-3.5',
'claude-sonnet-5',
'claude-fable-5',
'claude-opus-4-8',
'claude-opus-4-7',
'claude-opus-4-6',
'claude-opus-4-5',
'claude-sonnet-4-5',
'claude-haiku-4-5',
],
MiniMax: ['MiniMax-M2.7', 'MiniMax-M2.7-highspeed', 'MiniMax-M2.5', 'MiniMax-M2.5-highspeed'],
xAI: ['grok-4.1-fast', 'grok-4', 'grok-code-fast'],
MoonshotAI: ['kimi-k2.5'],
'Z.AI': ['glm-5', 'glm-4.7'],
MiniMax: [
// 'MiniMax-M3', low success rate
'MiniMax-M2.7',
'MiniMax-M2.5',
],
xAI: ['grok-4.3', 'grok-build-0.1'],
MoonshotAI: ['kimi-k2.7-code', 'kimi-k2.6', 'kimi-k2.5'],
'Z.AI': ['glm-5.2', 'glm-5.1', 'glm-5', 'glm-4.7'],
}
const ModelBadge = ({ model, baseline }: { model: string; baseline?: boolean }) => (
@@ -91,7 +95,7 @@ export default function Models() {
<section className="mb-10">
<Heading id="tested-models" className="text-2xl font-semibold mb-3">
{isZh ? '已测试模型' : 'Tested Models'}
{isZh ? '模型列表' : 'Model List'}
</Heading>
<div className="bg-linear-to-br from-emerald-50 to-cyan-50 dark:from-emerald-950/30 dark:to-cyan-950/30 rounded-xl p-6 border border-emerald-200/50 dark:border-emerald-800/50">
<div className="grid grid-cols-[5rem_1fr] gap-x-3 gap-y-3 items-start">
@@ -124,8 +128,8 @@ export default function Models() {
</li>
<li>
{isZh
? 'ToolCall 能力较弱的模型可能返回错误的格式,常见错误能够自动恢复,建议设置较高的 temperature'
: 'Models with weaker ToolCall capabilities may return incorrect formats. Common errors usually auto-recover. Higher temperature recommended'}
? 'ToolCall 能力较弱的模型可能返回错误的格式,常见错误能够自动恢复'
: 'Models with weaker ToolCall capabilities may return incorrect formats. Common errors usually auto-recover'}
</li>
<li>
{isZh
@@ -281,7 +285,7 @@ LLM_MODEL_NAME="qwen3.5-plus"`}
code={`const pageAgent = new PageAgent({
baseURL: 'https://your-claude-proxy.example/v1',
apiKey: 'your-api-key',
model: 'claude-sonnet-4.5',
model: 'claude-sonnet-5',
transformRequestBody: (requestBody) => ({
...requestBody,
cache_control: { type: 'ephemeral' },
@@ -17,11 +17,17 @@ import { useLanguage } from '../../i18n/context'
let pageAgentModule: Promise<typeof import('page-agent')> | null = null
function getInjection(useCN?: boolean) {
const cdn = useCN ? CDN_DEMO_CN_URL : CDN_DEMO_URL
/**
* Get the bookmarklet injection script
* @param cdnSource Which CDN mirror to use
* @param isZh Whether to use Chinese language
*/
function getInjection(cdnSource: 'china' | 'international', isZh?: boolean) {
const cdn = cdnSource === 'china' ? CDN_DEMO_CN_URL : CDN_DEMO_URL
const locale = isZh ? 'zh-CN' : 'en-US'
const injection = encodeURI(
`javascript:(function(){var s=document.createElement('script');s.src=\`${cdn}?t=\${Math.random()}\`;s.setAttribute('crossorigin', true);s.type="text/javascript";s.onload=()=>console.log('PageAgent script loaded!');document.body.appendChild(s);})();`
`javascript:(function(){var s=document.createElement('script');s.src=\`${cdn}?lang=${locale}&t=\${Math.random()}\`;s.setAttribute('crossorigin', true);s.type="text/javascript";s.onload=()=>console.log('PageAgent script loaded!');document.body.appendChild(s);})();`
)
return `
@@ -298,7 +304,7 @@ export default function HeroSection() {
</select>
<div
dangerouslySetInnerHTML={{
__html: getInjection(cdnSource === 'china'),
__html: getInjection(cdnSource, isZh),
}}
></div>
</div>
@@ -40,7 +40,7 @@ export default function OneMoreThingSection() {
className="group inline-flex items-center gap-3 px-8 py-4 bg-linear-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 text-white font-medium rounded-2xl shadow-lg hover:shadow-xl transition-all duration-300 hover:scale-105"
>
<img
src="https://img.alicdn.com/imgextra/i3/O1CN01JpW0Vo1sR3FpiZKFM_!!6000000005762-55-tps-192-192.svg"
src="https://page-agent.github.io/assets/third-party/chrome-web-store-192.svg"
alt="Chrome Web Store"
className="w-7 h-7"
/>