Compare commits

...

18 Commits

Author SHA1 Message Date
Simon 0d3cef5578 chore(version): bump version to 0.0.21 2026-01-13 15:18:50 +08:00
Simon 1e737f3a71 docs: models recommendation 2026-01-13 15:17:43 +08:00
Simon 2f2bdb4697 fix: update Qwen patch condition for model name 2026-01-13 15:07:01 +08:00
Simon 3fa7c8b85a feat: improve qwen3 compatibility (#106)
* feat: improve qwen3 compatibility

* fix: qwen

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-13 15:04:03 +08:00
dependabot[bot] 7efce91a91 chore(deps-dev): bump the development-dependencies group with 11 updates (#101)
Bumps the development-dependencies group with 11 updates:

| Package | From | To |
| --- | --- | --- |
| [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) | `20.3.0` | `20.3.1` |
| [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) | `20.3.0` | `20.3.1` |
| [@trivago/prettier-plugin-sort-imports](https://github.com/trivago/prettier-plugin-sort-imports) | `6.0.1` | `6.0.2` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `25.0.3` | `25.0.6` |
| [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/HEAD/packages/plugins/eslint-plugin-react-dom) | `2.5.1` | `2.5.5` |
| [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/HEAD/packages/plugins/eslint-plugin-react-x) | `2.5.1` | `2.5.5` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.52.0` | `8.53.0` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `7.3.0` | `7.3.1` |
| [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `19.2.7` | `19.2.8` |
| [i18next](https://github.com/i18next/i18next) | `25.7.3` | `25.7.4` |
| [react-i18next](https://github.com/i18next/react-i18next) | `16.5.1` | `16.5.2` |


Updates `@commitlint/cli` from 20.3.0 to 20.3.1
- [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/v20.3.1/@commitlint/cli)

Updates `@commitlint/config-conventional` from 20.3.0 to 20.3.1
- [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/v20.3.1/@commitlint/config-conventional)

Updates `@trivago/prettier-plugin-sort-imports` from 6.0.1 to 6.0.2
- [Release notes](https://github.com/trivago/prettier-plugin-sort-imports/releases)
- [Changelog](https://github.com/trivago/prettier-plugin-sort-imports/blob/main/CHANGELOG.md)
- [Commits](https://github.com/trivago/prettier-plugin-sort-imports/compare/v6.0.1...v6.0.2)

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

Updates `eslint-plugin-react-dom` from 2.5.1 to 2.5.5
- [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/v2.5.5/packages/plugins/eslint-plugin-react-dom)

Updates `eslint-plugin-react-x` from 2.5.1 to 2.5.5
- [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/v2.5.5/packages/plugins/eslint-plugin-react-x)

Updates `typescript-eslint` from 8.52.0 to 8.53.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.53.0/packages/typescript-eslint)

Updates `vite` from 7.3.0 to 7.3.1
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.1/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.1/packages/vite)

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

Updates `i18next` from 25.7.3 to 25.7.4
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v25.7.3...v25.7.4)

Updates `react-i18next` from 16.5.1 to 16.5.2
- [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/react-i18next/compare/v16.5.1...v16.5.2)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-version: 20.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@commitlint/config-conventional"
  dependency-version: 20.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@trivago/prettier-plugin-sort-imports"
  dependency-version: 6.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@types/node"
  dependency-version: 25.0.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: eslint-plugin-react-dom
  dependency-version: 2.5.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: eslint-plugin-react-x
  dependency-version: 2.5.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.53.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: vite
  dependency-version: 7.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: "@types/react"
  dependency-version: 19.2.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: i18next
  dependency-version: 25.7.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
- dependency-name: react-i18next
  dependency-version: 16.5.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 14:30:02 +08:00
dependabot[bot] a11d370f2b chore(deps): bump the production-dependencies group with 3 updates (#100)
Bumps the production-dependencies group with 3 updates: [ai-motion](https://github.com/gaomeng1900/ai-motion), [motion](https://github.com/motiondivision/motion) and [simple-icons](https://github.com/simple-icons/simple-icons).


Updates `ai-motion` from 0.4.7 to 0.4.8
- [Release notes](https://github.com/gaomeng1900/ai-motion/releases)
- [Commits](https://github.com/gaomeng1900/ai-motion/compare/v0.4.7...v0.4.8)

Updates `motion` from 12.24.0 to 12.26.1
- [Changelog](https://github.com/motiondivision/motion/blob/main/CHANGELOG.md)
- [Commits](https://github.com/motiondivision/motion/compare/v12.24.0...v12.26.1)

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

---
updated-dependencies:
- dependency-name: ai-motion
  dependency-version: 0.4.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: production-dependencies
- dependency-name: motion
  dependency-version: 12.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
- dependency-name: simple-icons
  dependency-version: 16.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: production-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 14:27:23 +08:00
dependabot[bot] d7bfa42f85 chore(deps-dev): bump globals from 16.5.0 to 17.0.0 (#102)
Bumps [globals](https://github.com/sindresorhus/globals) from 16.5.0 to 17.0.0.
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](https://github.com/sindresorhus/globals/compare/v16.5.0...v17.0.0)

---
updated-dependencies:
- dependency-name: globals
  dependency-version: 17.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 14:26:37 +08:00
Simon 551ba49280 Merge pull request #104 from alibaba/refactor/mv-brain-outof-llms
refactor: move agent logic out of `llms`
2026-01-13 14:24:29 +08:00
Simon adab24bb22 Merge branch 'refactor/mv-brain-outof-llms' of https://github.com/alibaba/page-agent into refactor/mv-brain-outof-llms 2026-01-13 14:22:48 +08:00
Simon 64aea7e84c chore: typing 2026-01-13 14:21:57 +08:00
Simon 67f6bd619e chore: rename autoFixer for better understanding 2026-01-13 14:20:52 +08:00
Simon 35d1fd1166 chore: better erroring
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-13 14:16:51 +08:00
Simon d5b8019fb1 chore: better docs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-13 14:14:56 +08:00
Simon 526cb4dbb3 chore: rename OpenAIClient; clean up old notes 2026-01-13 13:58:21 +08:00
Simon 6dc56c57c6 chore: rm legacy llm client 2026-01-13 13:56:30 +08:00
Simon a3263fdd3a chore: rename OpenAICompatibleClient 2026-01-13 13:51:09 +08:00
Simon 14974c0257 feat!: mv brain from llms to agent; redo toolCall auto fixer 2026-01-13 13:49:19 +08:00
Simon e70ae40096 chore: guardrail 2026-01-12 13:55:39 +08:00
19 changed files with 618 additions and 756 deletions
+1
View File
@@ -123,4 +123,5 @@ Query params configure `PageAgentConfig` in `src/umd.ts`.
- Explicit typing for exported/public APIs
- ESLint relaxes some unsafe rules for rapid iteration
- Every change you make should not only implement the desired functionality but also improve the quality of the codebase
- All code and comments must be in English.
+247 -247
View File
@@ -1,12 +1,12 @@
{
"name": "root",
"version": "0.0.20",
"version": "0.0.21",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "root",
"version": "0.0.20",
"version": "0.0.21",
"license": "MIT",
"workspaces": [
"packages/page-controller",
@@ -16,28 +16,28 @@
"packages/website"
],
"devDependencies": {
"@commitlint/cli": "^20.3.0",
"@commitlint/config-conventional": "^20.3.0",
"@commitlint/cli": "^20.3.1",
"@commitlint/config-conventional": "^20.3.1",
"@eslint/js": "^9.39.2",
"@microsoft/api-extractor": "^7.55.2",
"@trivago/prettier-plugin-sort-imports": "^6.0.1",
"@types/node": "^25.0.3",
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/node": "^25.0.7",
"concurrently": "^9.2.1",
"dotenv": "^17.2.3",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react-dom": "^2.5.1",
"eslint-plugin-react-dom": "^2.5.7",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.26",
"eslint-plugin-react-x": "^2.5.1",
"globals": "^16.4.0",
"eslint-plugin-react-x": "^2.5.7",
"globals": "^17.0.0",
"husky": "^9.1.7",
"lint-staged": "^16.2.4",
"prettier": "^3.7.4",
"typescript": "^5.9.3",
"typescript-eslint": "^8.52.0",
"typescript-eslint": "^8.53.0",
"unplugin-dts": "^1.0.0-beta.6",
"vite": "^7.3.0",
"vite": "^7.3.1",
"vite-plugin-css-injected-by-js": "^3.5.2"
},
"engines": {
@@ -316,17 +316,17 @@
}
},
"node_modules/@commitlint/cli": {
"version": "20.3.0",
"resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.3.0.tgz",
"integrity": "sha512-HXO8YVfqdBK+MnlX2zqNrv6waGYPs6Ysjm5W2Y0GMagWXwiIKx7C8dcIX9ca+QdHq4WA0lcMnZLQ0pzQh1piZg==",
"version": "20.3.1",
"resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.3.1.tgz",
"integrity": "sha512-NtInjSlyev/+SLPvx/ulz8hRE25Wf5S9dLNDcIwazq0JyB4/w1ROF/5nV0ObPTX8YpRaKYeKtXDYWqumBNHWsw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@commitlint/format": "^20.2.0",
"@commitlint/lint": "^20.3.0",
"@commitlint/load": "^20.3.0",
"@commitlint/read": "^20.2.0",
"@commitlint/types": "^20.2.0",
"@commitlint/format": "^20.3.1",
"@commitlint/lint": "^20.3.1",
"@commitlint/load": "^20.3.1",
"@commitlint/read": "^20.3.1",
"@commitlint/types": "^20.3.1",
"tinyexec": "^1.0.0",
"yargs": "^17.0.0"
},
@@ -338,13 +338,13 @@
}
},
"node_modules/@commitlint/config-conventional": {
"version": "20.3.0",
"resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.3.0.tgz",
"integrity": "sha512-g1OXVl6E2v0xF1Ru2RpxQ+Vfy7XUcUsCmLKzGUrhFLS4hSNykje0QSy6djBtzOiOBQCepBrmIlqx/gRlzrSh5A==",
"version": "20.3.1",
"resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.3.1.tgz",
"integrity": "sha512-NCzwvxepstBZbmVXsvg49s+shCxlJDJPWxXqONVcAtJH9wWrOlkMQw/zyl+dJmt8lyVopt5mwQ3mR5M2N2rUWg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@commitlint/types": "^20.2.0",
"@commitlint/types": "^20.3.1",
"conventional-changelog-conventionalcommits": "^7.0.2"
},
"engines": {
@@ -352,13 +352,13 @@
}
},
"node_modules/@commitlint/config-validator": {
"version": "20.2.0",
"resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.2.0.tgz",
"integrity": "sha512-SQCBGsL9MFk8utWNSthdxd9iOD1pIVZSHxGBwYIGfd67RTjxqzFOSAYeQVXOu3IxRC3YrTOH37ThnTLjUlyF2w==",
"version": "20.3.1",
"resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.3.1.tgz",
"integrity": "sha512-ErVLC/IsHhcvxCyh+FXo7jy12/nkQySjWXYgCoQbZLkFp4hysov8KS6CdxBB0cWjbZWjvNOKBMNoUVqkmGmahw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@commitlint/types": "^20.2.0",
"@commitlint/types": "^20.3.1",
"ajv": "^8.11.0"
},
"engines": {
@@ -366,13 +366,13 @@
}
},
"node_modules/@commitlint/ensure": {
"version": "20.2.0",
"resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.2.0.tgz",
"integrity": "sha512-+8TgIGv89rOWyt3eC6lcR1H7hqChAKkpawytlq9P1i/HYugFRVqgoKJ8dhd89fMnlrQTLjA5E97/4sF09QwdoA==",
"version": "20.3.1",
"resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.3.1.tgz",
"integrity": "sha512-h664FngOEd7bHAm0j8MEKq+qm2mH+V+hwJiIE2bWcw3pzJMlO0TPKtk0ATyRAtV6jQw+xviRYiIjjSjfajiB5w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@commitlint/types": "^20.2.0",
"@commitlint/types": "^20.3.1",
"lodash.camelcase": "^4.3.0",
"lodash.kebabcase": "^4.1.1",
"lodash.snakecase": "^4.1.1",
@@ -394,13 +394,13 @@
}
},
"node_modules/@commitlint/format": {
"version": "20.2.0",
"resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.2.0.tgz",
"integrity": "sha512-PhNoLNhxpfIBlW/i90uZ3yG3hwSSYx7n4d9Yc+2FAorAHS0D9btYRK4ZZXX+Gm3W5tDtu911ow/eWRfcRVgNWg==",
"version": "20.3.1",
"resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.3.1.tgz",
"integrity": "sha512-jfsjGPFTd2Yti2YHwUH4SPRPbWKAJAwrfa3eNa9bXEdrXBb9mCwbIrgYX38LdEJK9zLJ3AsLBP4/FLEtxyu2AA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@commitlint/types": "^20.2.0",
"@commitlint/types": "^20.3.1",
"chalk": "^5.3.0"
},
"engines": {
@@ -408,13 +408,13 @@
}
},
"node_modules/@commitlint/is-ignored": {
"version": "20.2.0",
"resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.2.0.tgz",
"integrity": "sha512-Lz0OGeZCo/QHUDLx5LmZc0EocwanneYJUM8z0bfWexArk62HKMLfLIodwXuKTO5y0s6ddXaTexrYHs7v96EOmw==",
"version": "20.3.1",
"resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.3.1.tgz",
"integrity": "sha512-tWwAoh93QvAhxgp99CzCuHD86MgxE4NBtloKX+XxQxhfhSwHo7eloiar/yzx53YW9eqSLP95zgW2KDDk4/WX+A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@commitlint/types": "^20.2.0",
"@commitlint/types": "^20.3.1",
"semver": "^7.6.0"
},
"engines": {
@@ -422,32 +422,32 @@
}
},
"node_modules/@commitlint/lint": {
"version": "20.3.0",
"resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.3.0.tgz",
"integrity": "sha512-X19HOGU5nRo6i9DIY0kG0mhgtvpn1UGO1D6aLX1ILLyeqSM5yJyMcrRqNj8SLgeSeUDODhLY9QYsBIG0LdNHkA==",
"version": "20.3.1",
"resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.3.1.tgz",
"integrity": "sha512-LaOtrQ24+6SfUaWg8A+a+Wc77bvLbO5RIr6iy9F7CI3/0iq1uPEWgGRCwqWTuLGHkZDAcwaq0gZ01zpwZ1jCGw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@commitlint/is-ignored": "^20.2.0",
"@commitlint/parse": "^20.2.0",
"@commitlint/rules": "^20.3.0",
"@commitlint/types": "^20.2.0"
"@commitlint/is-ignored": "^20.3.1",
"@commitlint/parse": "^20.3.1",
"@commitlint/rules": "^20.3.1",
"@commitlint/types": "^20.3.1"
},
"engines": {
"node": ">=v18"
}
},
"node_modules/@commitlint/load": {
"version": "20.3.0",
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.3.0.tgz",
"integrity": "sha512-amkdVZTXp5R65bsRXRSCwoNXbJHR2aAIY/RGFkoyd63t8UEwqEgT3f0MgeLqYw4hwXyq+TYXKdaW133E29pnGQ==",
"version": "20.3.1",
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.3.1.tgz",
"integrity": "sha512-YDD9XA2XhgYgbjju8itZ/weIvOOobApDqwlPYCX5NLO/cPtw2UMO5Cmn44Ks8RQULUVI5fUT6roKvyxcoLbNmw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@commitlint/config-validator": "^20.2.0",
"@commitlint/config-validator": "^20.3.1",
"@commitlint/execute-rule": "^20.0.0",
"@commitlint/resolve-extends": "^20.2.0",
"@commitlint/types": "^20.2.0",
"@commitlint/resolve-extends": "^20.3.1",
"@commitlint/types": "^20.3.1",
"chalk": "^5.3.0",
"cosmiconfig": "^9.0.0",
"cosmiconfig-typescript-loader": "^6.1.0",
@@ -470,13 +470,13 @@
}
},
"node_modules/@commitlint/parse": {
"version": "20.2.0",
"resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.2.0.tgz",
"integrity": "sha512-LXStagGU1ivh07X7sM+hnEr4BvzFYn1iBJ6DRg2QsIN8lBfSzyvkUcVCDwok9Ia4PWiEgei5HQjju6xfJ1YaSQ==",
"version": "20.3.1",
"resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.3.1.tgz",
"integrity": "sha512-TuUTdbLpyUNLgDzLDYlI2BeTE6V/COZbf3f8WwsV0K6eq/2nSpNTMw7wHtXb+YxeY9wwxBp/Ldad4P+YIxHJoA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@commitlint/types": "^20.2.0",
"@commitlint/types": "^20.3.1",
"conventional-changelog-angular": "^7.0.0",
"conventional-commits-parser": "^5.0.0"
},
@@ -485,14 +485,14 @@
}
},
"node_modules/@commitlint/read": {
"version": "20.2.0",
"resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.2.0.tgz",
"integrity": "sha512-+SjF9mxm5JCbe+8grOpXCXMMRzAnE0WWijhhtasdrpJoAFJYd5UgRTj/oCq5W3HJTwbvTOsijEJ0SUGImECD7Q==",
"version": "20.3.1",
"resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.3.1.tgz",
"integrity": "sha512-nCmJAdIg3OdNVUpQW0Idk/eF/vfOo2W2xzmvRmNeptLrzFK7qhwwl/kIwy1Q1LZrKHUFNj7PGNpIT5INbgZWzA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@commitlint/top-level": "^20.0.0",
"@commitlint/types": "^20.2.0",
"@commitlint/types": "^20.3.1",
"git-raw-commits": "^4.0.0",
"minimist": "^1.2.8",
"tinyexec": "^1.0.0"
@@ -502,14 +502,14 @@
}
},
"node_modules/@commitlint/resolve-extends": {
"version": "20.2.0",
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.2.0.tgz",
"integrity": "sha512-KVoLDi9BEuqeq+G0wRABn4azLRiCC22/YHR2aCquwx6bzCHAIN8hMt3Nuf1VFxq/c8ai6s8qBxE8+ZD4HeFTlQ==",
"version": "20.3.1",
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.3.1.tgz",
"integrity": "sha512-iGTGeyaoDyHDEZNjD8rKeosjSNs8zYanmuowY4ful7kFI0dnY4b5QilVYaFQJ6IM27S57LAeH5sKSsOHy4bw5w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@commitlint/config-validator": "^20.2.0",
"@commitlint/types": "^20.2.0",
"@commitlint/config-validator": "^20.3.1",
"@commitlint/types": "^20.3.1",
"global-directory": "^4.0.1",
"import-meta-resolve": "^4.0.0",
"lodash.mergewith": "^4.6.2",
@@ -520,16 +520,16 @@
}
},
"node_modules/@commitlint/rules": {
"version": "20.3.0",
"resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.3.0.tgz",
"integrity": "sha512-TGgXN/qBEhbzVD13crE1l7YSMJRrbPbUL0OBZALbUM5ER36RZmiZRu2ud2W/AA7HO9YLBRbyx6YVi2t/2Be0yQ==",
"version": "20.3.1",
"resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.3.1.tgz",
"integrity": "sha512-/uic4P+4jVNpqQxz02+Y6vvIC0A2J899DBztA1j6q3f3MOKwydlNrojSh0dQmGDxxT1bXByiRtDhgFnOFnM6Pg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@commitlint/ensure": "^20.2.0",
"@commitlint/ensure": "^20.3.1",
"@commitlint/message": "^20.0.0",
"@commitlint/to-lines": "^20.0.0",
"@commitlint/types": "^20.2.0"
"@commitlint/types": "^20.3.1"
},
"engines": {
"node": ">=v18"
@@ -559,9 +559,9 @@
}
},
"node_modules/@commitlint/types": {
"version": "20.2.0",
"resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.2.0.tgz",
"integrity": "sha512-KTy0OqRDLR5y/zZMnizyx09z/rPlPC/zKhYgH8o/q6PuAjoQAKlRfY4zzv0M64yybQ//6//4H1n14pxaLZfUnA==",
"version": "20.3.1",
"resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.3.1.tgz",
"integrity": "sha512-VmIFV/JkBRhDRRv7N5B7zEUkNZIx9Mp+8Pe65erz0rKycXLsi8Epcw0XJ+btSeRXgTzE7DyOyA9bkJ9mn/yqVQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1057,16 +1057,16 @@
}
},
"node_modules/@eslint-react/ast": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@eslint-react/ast/-/ast-2.5.1.tgz",
"integrity": "sha512-l+pEb3kS/RhFN+8ZOhL5+P5+Yj7iw+ta7+abUBNH+5PMyiixlnAJlJ7Wy+rsTGxjUJtzEff3CzSr06V7qwJuwQ==",
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/@eslint-react/ast/-/ast-2.5.7.tgz",
"integrity": "sha512-yQvKvyIzo9DPKFUOtFSEfpX0He763G1jFf/LQcIY9xoSOWP9pQ2X8ADNE6ZnmIc0L5zZLQ9nF/H0+MGh2NF3fA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-react/eff": "2.5.1",
"@typescript-eslint/types": "^8.51.0",
"@typescript-eslint/typescript-estree": "^8.51.0",
"@typescript-eslint/utils": "^8.51.0",
"@eslint-react/eff": "2.5.7",
"@typescript-eslint/types": "^8.53.0",
"@typescript-eslint/typescript-estree": "^8.53.0",
"@typescript-eslint/utils": "^8.53.0",
"string-ts": "^2.3.1"
},
"engines": {
@@ -1078,19 +1078,19 @@
}
},
"node_modules/@eslint-react/core": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@eslint-react/core/-/core-2.5.1.tgz",
"integrity": "sha512-48YQYhjHga0TAv7FI0uGGT+X3SITec89fJLKSTqIEa7jTLpXTV56B+B/V/IcfZ0s8I0lNVEmtHFTXaibAxmfyw==",
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/@eslint-react/core/-/core-2.5.7.tgz",
"integrity": "sha512-LqY4YV4Z/uiymdy2OLXkjsEVnoDjoFsxJZI4GWsZ9aBgGWYAsuoKKUG0bdMLp1U9jwMaY0OY9bYgBB5R1ANkoQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-react/ast": "2.5.1",
"@eslint-react/eff": "2.5.1",
"@eslint-react/shared": "2.5.1",
"@eslint-react/var": "2.5.1",
"@typescript-eslint/scope-manager": "^8.51.0",
"@typescript-eslint/types": "^8.51.0",
"@typescript-eslint/utils": "^8.51.0",
"@eslint-react/ast": "2.5.7",
"@eslint-react/eff": "2.5.7",
"@eslint-react/shared": "2.5.7",
"@eslint-react/var": "2.5.7",
"@typescript-eslint/scope-manager": "^8.53.0",
"@typescript-eslint/types": "^8.53.0",
"@typescript-eslint/utils": "^8.53.0",
"birecord": "^0.1.1",
"ts-pattern": "^5.9.0"
},
@@ -1103,9 +1103,9 @@
}
},
"node_modules/@eslint-react/eff": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@eslint-react/eff/-/eff-2.5.1.tgz",
"integrity": "sha512-henDz7sTB67Q+YPb+Kiy0UpD7eHLvDtB2nOrRtHLTatS/LFr7wVr4J2muQPkPzY9KFF8pv811nrwW58FuDphPg==",
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/@eslint-react/eff/-/eff-2.5.7.tgz",
"integrity": "sha512-oxd8eP+dykyOUmQUEItnOZhitlKyo9yFq8Ne0k9+CJeV0Pk1db1+lNcDWswWYoeb1IFEloqHo6y63fVxQXvJew==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1113,14 +1113,14 @@
}
},
"node_modules/@eslint-react/shared": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@eslint-react/shared/-/shared-2.5.1.tgz",
"integrity": "sha512-JSTnpZdBu57ZvrJkBwUZXgkBW8iPiQfSiQMYHEjVFR2NHWFgcOd9qYJLX7mJLrdcw/j+GgRQeOAGok0a0xcxGA==",
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/@eslint-react/shared/-/shared-2.5.7.tgz",
"integrity": "sha512-ZjKO3rNDoLN2ZTn3PxmMcgoASPNiL3umK1BHQvYwCXqqnXlLEouu7sxK6htOvDu1NC+Dbwsqpu4JGUY7t0XQTw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-react/eff": "2.5.1",
"@typescript-eslint/utils": "^8.51.0",
"@eslint-react/eff": "2.5.7",
"@typescript-eslint/utils": "^8.53.0",
"ts-pattern": "^5.9.0",
"zod": "^4.3.5"
},
@@ -1133,17 +1133,17 @@
}
},
"node_modules/@eslint-react/var": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@eslint-react/var/-/var-2.5.1.tgz",
"integrity": "sha512-O3rbdK8pHKZ4NYPD39Zq7zhWu76dvhyeroqG8nQzf667F/FdHGpGhsCPJOeIU5L/ooYO/3/Qz2vJD1pB5JeROQ==",
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/@eslint-react/var/-/var-2.5.7.tgz",
"integrity": "sha512-VWoyZENbK+OV70s/sE9E6T4HD0WO6lsg2CnYjjpKBKBDOBW67SRdIlM7dUPKzqQKsj6p4yNh59eQ5iKaH82LLg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-react/ast": "2.5.1",
"@eslint-react/eff": "2.5.1",
"@typescript-eslint/scope-manager": "^8.51.0",
"@typescript-eslint/types": "^8.51.0",
"@typescript-eslint/utils": "^8.51.0",
"@eslint-react/ast": "2.5.7",
"@eslint-react/eff": "2.5.7",
"@typescript-eslint/scope-manager": "^8.53.0",
"@typescript-eslint/types": "^8.53.0",
"@typescript-eslint/utils": "^8.53.0",
"ts-pattern": "^5.9.0"
},
"engines": {
@@ -3458,9 +3458,9 @@
}
},
"node_modules/@trivago/prettier-plugin-sort-imports": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-6.0.1.tgz",
"integrity": "sha512-6B13DCWDfAfh4AEJ43gRgeCSAQmlKG5LHqHzHc0lbUwgBy0rX7o41US+46Fd4XiXBx+JDGEz3NBadCbUls0dUQ==",
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-6.0.2.tgz",
"integrity": "sha512-3DgfkukFyC/sE/VuYjaUUWoFfuVjPK55vOFDsxD56XXynFMCZDYFogH2l/hDfOsQAm1myoU/1xByJ3tWqtulXA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -3556,9 +3556,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.0.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
"integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
"version": "25.0.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.7.tgz",
"integrity": "sha512-C/er7DlIZgRJO7WtTdYovjIFzGsz0I95UlMyR9anTb4aCpBSRWe5Jc1/RvLKUfzmOxHPGjSE5+63HgLtndxU4w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3566,9 +3566,9 @@
}
},
"node_modules/@types/react": {
"version": "19.2.7",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
"version": "19.2.8",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.8.tgz",
"integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==",
"devOptional": true,
"license": "MIT",
"dependencies": {
@@ -3586,17 +3586,17 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.52.0.tgz",
"integrity": "sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==",
"version": "8.53.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.0.tgz",
"integrity": "sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.52.0",
"@typescript-eslint/type-utils": "8.52.0",
"@typescript-eslint/utils": "8.52.0",
"@typescript-eslint/visitor-keys": "8.52.0",
"@typescript-eslint/scope-manager": "8.53.0",
"@typescript-eslint/type-utils": "8.53.0",
"@typescript-eslint/utils": "8.53.0",
"@typescript-eslint/visitor-keys": "8.53.0",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.4.0"
@@ -3609,7 +3609,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.52.0",
"@typescript-eslint/parser": "^8.53.0",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
@@ -3625,16 +3625,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.52.0.tgz",
"integrity": "sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==",
"version": "8.53.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.0.tgz",
"integrity": "sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.52.0",
"@typescript-eslint/types": "8.52.0",
"@typescript-eslint/typescript-estree": "8.52.0",
"@typescript-eslint/visitor-keys": "8.52.0",
"@typescript-eslint/scope-manager": "8.53.0",
"@typescript-eslint/types": "8.53.0",
"@typescript-eslint/typescript-estree": "8.53.0",
"@typescript-eslint/visitor-keys": "8.53.0",
"debug": "^4.4.3"
},
"engines": {
@@ -3650,14 +3650,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.52.0.tgz",
"integrity": "sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==",
"version": "8.53.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.53.0.tgz",
"integrity": "sha512-Bl6Gdr7NqkqIP5yP9z1JU///Nmes4Eose6L1HwpuVHwScgDPPuEWbUVhvlZmb8hy0vX9syLk5EGNL700WcBlbg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.52.0",
"@typescript-eslint/types": "^8.52.0",
"@typescript-eslint/tsconfig-utils": "^8.53.0",
"@typescript-eslint/types": "^8.53.0",
"debug": "^4.4.3"
},
"engines": {
@@ -3672,14 +3672,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.52.0.tgz",
"integrity": "sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==",
"version": "8.53.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.0.tgz",
"integrity": "sha512-kWNj3l01eOGSdVBnfAF2K1BTh06WS0Yet6JUgb9Cmkqaz3Jlu0fdVUjj9UI8gPidBWSMqDIglmEXifSgDT/D0g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.52.0",
"@typescript-eslint/visitor-keys": "8.52.0"
"@typescript-eslint/types": "8.53.0",
"@typescript-eslint/visitor-keys": "8.53.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3690,9 +3690,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.52.0.tgz",
"integrity": "sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==",
"version": "8.53.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.0.tgz",
"integrity": "sha512-K6Sc0R5GIG6dNoPdOooQ+KtvT5KCKAvTcY8h2rIuul19vxH5OTQk7ArKkd4yTzkw66WnNY0kPPzzcmWA+XRmiA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3707,15 +3707,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.52.0.tgz",
"integrity": "sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==",
"version": "8.53.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.53.0.tgz",
"integrity": "sha512-BBAUhlx7g4SmcLhn8cnbxoxtmS7hcq39xKCgiutL3oNx1TaIp+cny51s8ewnKMpVUKQUGb41RAUWZ9kxYdovuw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.52.0",
"@typescript-eslint/typescript-estree": "8.52.0",
"@typescript-eslint/utils": "8.52.0",
"@typescript-eslint/types": "8.53.0",
"@typescript-eslint/typescript-estree": "8.53.0",
"@typescript-eslint/utils": "8.53.0",
"debug": "^4.4.3",
"ts-api-utils": "^2.4.0"
},
@@ -3732,9 +3732,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.52.0.tgz",
"integrity": "sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==",
"version": "8.53.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.0.tgz",
"integrity": "sha512-Bmh9KX31Vlxa13+PqPvt4RzKRN1XORYSLlAE+sO1i28NkisGbTtSLFVB3l7PWdHtR3E0mVMuC7JilWJ99m2HxQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3746,16 +3746,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.52.0.tgz",
"integrity": "sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==",
"version": "8.53.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.0.tgz",
"integrity": "sha512-pw0c0Gdo7Z4xOG987u3nJ8akL9093yEEKv8QTJ+Bhkghj1xyj8cgPaavlr9rq8h7+s6plUJ4QJYw2gCZodqmGw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.52.0",
"@typescript-eslint/tsconfig-utils": "8.52.0",
"@typescript-eslint/types": "8.52.0",
"@typescript-eslint/visitor-keys": "8.52.0",
"@typescript-eslint/project-service": "8.53.0",
"@typescript-eslint/tsconfig-utils": "8.53.0",
"@typescript-eslint/types": "8.53.0",
"@typescript-eslint/visitor-keys": "8.53.0",
"debug": "^4.4.3",
"minimatch": "^9.0.5",
"semver": "^7.7.3",
@@ -3800,16 +3800,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.52.0.tgz",
"integrity": "sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==",
"version": "8.53.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.0.tgz",
"integrity": "sha512-XDY4mXTez3Z1iRDI5mbRhH4DFSt46oaIFsLg+Zn97+sYrXACziXSQcSelMybnVZ5pa1P6xYkPr5cMJyunM1ZDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.52.0",
"@typescript-eslint/types": "8.52.0",
"@typescript-eslint/typescript-estree": "8.52.0"
"@typescript-eslint/scope-manager": "8.53.0",
"@typescript-eslint/types": "8.53.0",
"@typescript-eslint/typescript-estree": "8.53.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3824,13 +3824,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.52.0.tgz",
"integrity": "sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==",
"version": "8.53.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.0.tgz",
"integrity": "sha512-LZ2NqIHFhvFwxG0qZeLL9DvdNAHPGCY5dIRwBhyYeU+LfLhcStE1ImjsuTG/WaVh3XysGaeLW8Rqq7cGkPCFvw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.52.0",
"@typescript-eslint/types": "8.53.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -3911,9 +3911,9 @@
}
},
"node_modules/ai-motion": {
"version": "0.4.7",
"resolved": "https://registry.npmjs.org/ai-motion/-/ai-motion-0.4.7.tgz",
"integrity": "sha512-yuFtVgWeaxKyvfSFocA01Mr4UbRvwwy7HWZ6Mq9NM/yccAkqp3UCGdWX6AflNB8cU27PgwTBiMRfx6C9di7xkQ==",
"version": "0.4.8",
"resolved": "https://registry.npmjs.org/ai-motion/-/ai-motion-0.4.8.tgz",
"integrity": "sha512-9nQVjf8lrYQWSW0sZdb9bES4x8Ei1w9C0S62po2KvRbMjslmjm0WMeszyW771SEauDnu4YGWqurLZ/gCkCTosQ==",
"license": "MIT"
},
"node_modules/ajv": {
@@ -4860,20 +4860,20 @@
}
},
"node_modules/eslint-plugin-react-dom": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-dom/-/eslint-plugin-react-dom-2.5.1.tgz",
"integrity": "sha512-7x3k0bQwtTOSxjReH11buYL6aSMopBzSaWr2bU/LPOuy1dDxvqE6HC2Y6QPEyZjLJ1ppUSKwRmcX85uiF0jHAA==",
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-dom/-/eslint-plugin-react-dom-2.5.7.tgz",
"integrity": "sha512-KHCU7KRXeLYlrrXKtInpCqHzt+nYhIGpYIuMM4Q0FyGBOcswRS2Gv9PhN+yQqDI9PRBhnjCuRf+q2Y3ZrYWjsw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-react/ast": "2.5.1",
"@eslint-react/core": "2.5.1",
"@eslint-react/eff": "2.5.1",
"@eslint-react/shared": "2.5.1",
"@eslint-react/var": "2.5.1",
"@typescript-eslint/scope-manager": "^8.51.0",
"@typescript-eslint/types": "^8.51.0",
"@typescript-eslint/utils": "^8.51.0",
"@eslint-react/ast": "2.5.7",
"@eslint-react/core": "2.5.7",
"@eslint-react/eff": "2.5.7",
"@eslint-react/shared": "2.5.7",
"@eslint-react/var": "2.5.7",
"@typescript-eslint/scope-manager": "^8.53.0",
"@typescript-eslint/types": "^8.53.0",
"@typescript-eslint/utils": "^8.53.0",
"compare-versions": "^6.1.1",
"string-ts": "^2.3.1",
"ts-pattern": "^5.9.0"
@@ -4917,21 +4917,21 @@
}
},
"node_modules/eslint-plugin-react-x": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-x/-/eslint-plugin-react-x-2.5.1.tgz",
"integrity": "sha512-w3KtEG8M7sFRh/psUmep3H+i2O/5Nn9wI8GXjlqzwp8zLb4ax66Ue+1oecLTzY/0j/M1xRfB3hzk5oio54RwLA==",
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-x/-/eslint-plugin-react-x-2.5.7.tgz",
"integrity": "sha512-EU+h7eUHNg9ikLaGOa0Tjsc/o4FO2KdyT3M97TkQmaKc9tzpcy8nlPQp/czBMbUuxAgbj2495wzOvCZ0fEK4mw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-react/ast": "2.5.1",
"@eslint-react/core": "2.5.1",
"@eslint-react/eff": "2.5.1",
"@eslint-react/shared": "2.5.1",
"@eslint-react/var": "2.5.1",
"@typescript-eslint/scope-manager": "^8.51.0",
"@typescript-eslint/type-utils": "^8.51.0",
"@typescript-eslint/types": "^8.51.0",
"@typescript-eslint/utils": "^8.51.0",
"@eslint-react/ast": "2.5.7",
"@eslint-react/core": "2.5.7",
"@eslint-react/eff": "2.5.7",
"@eslint-react/shared": "2.5.7",
"@eslint-react/var": "2.5.7",
"@typescript-eslint/scope-manager": "^8.53.0",
"@typescript-eslint/type-utils": "^8.53.0",
"@typescript-eslint/types": "^8.53.0",
"@typescript-eslint/utils": "^8.53.0",
"compare-versions": "^6.1.1",
"is-immutable-type": "^5.0.1",
"string-ts": "^2.3.1",
@@ -5310,13 +5310,13 @@
"license": "ISC"
},
"node_modules/framer-motion": {
"version": "12.24.0",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.24.0.tgz",
"integrity": "sha512-ggTMRkIDPc76lHmM+dRT1MmVfFV6t/y+jkWjWuzR7FG5xRvnAAl/5wFPjzSkLE8Nu5E5uIQRCNxmIXtWJVo6XQ==",
"version": "12.26.1",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.26.1.tgz",
"integrity": "sha512-Uzc8wGldU4FpmGotthjjcj0SZhigcODjqvKT7lzVZHsmYkzQMFfMIv0vHQoXCeoe/Ahxqp4by4A6QbzFA/lblw==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.24.0",
"motion-utils": "^12.23.28",
"motion-dom": "^12.24.11",
"motion-utils": "^12.24.10",
"tslib": "^2.4.0"
},
"peerDependencies": {
@@ -5457,9 +5457,9 @@
}
},
"node_modules/globals": {
"version": "16.5.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
"integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
"version": "17.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.0.0.tgz",
"integrity": "sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -5543,9 +5543,9 @@
}
},
"node_modules/i18next": {
"version": "25.7.3",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.7.3.tgz",
"integrity": "sha512-2XaT+HpYGuc2uTExq9TVRhLsso+Dxym6PWaKpn36wfBmTI779OQ7iP/XaZHzrnGyzU4SHpFrTYLKfVyBfAhVNA==",
"version": "25.7.4",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.7.4.tgz",
"integrity": "sha512-hRkpEblXXcXSNbw8mBNq9042OEetgyB/ahc/X17uV/khPwzV+uB8RHceHh3qavyrkPJvmXFKXME2Sy1E0KjAfw==",
"dev": true,
"funding": [
{
@@ -6523,12 +6523,12 @@
}
},
"node_modules/motion": {
"version": "12.24.0",
"resolved": "https://registry.npmjs.org/motion/-/motion-12.24.0.tgz",
"integrity": "sha512-FAnpl/DhCFct3p+T2hCjAY95w+3EggUk3b8Ql4jQ6mmdRbEaGD1000goqWeEYVoD7mqx9H0As9ORmRADD4LQAw==",
"version": "12.26.1",
"resolved": "https://registry.npmjs.org/motion/-/motion-12.26.1.tgz",
"integrity": "sha512-IVhzx9HOQTiJ9ykthMOlZPnLwrkXziN5Q/yebsqBYlFJb2rHP8yhmKc8O/YUT9byPJlxOeqkzfNYCrVKZx8vqg==",
"license": "MIT",
"dependencies": {
"framer-motion": "^12.24.0",
"framer-motion": "^12.26.1",
"tslib": "^2.4.0"
},
"peerDependencies": {
@@ -6549,18 +6549,18 @@
}
},
"node_modules/motion-dom": {
"version": "12.24.0",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.24.0.tgz",
"integrity": "sha512-RD2kZkFd/GH4fITI8IJvypGgn0vIu5vkrJaXIAkYqORGs5P0CKDHKNvswmoY1H+tbUAOPSh6VtUqoAmc/3Gvig==",
"version": "12.24.11",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.24.11.tgz",
"integrity": "sha512-DlWOmsXMJrV8lzZyd+LKjG2CXULUs++bkq8GZ2Sr0R0RRhs30K2wtY+LKiTjhmJU3W61HK+rB0GLz6XmPvTA1A==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.23.28"
"motion-utils": "^12.24.10"
}
},
"node_modules/motion-utils": {
"version": "12.23.28",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.28.tgz",
"integrity": "sha512-0W6cWd5Okoyf8jmessVK3spOmbyE0yTdNKujHctHH9XdAE4QDuZ1/LjSXC68rrhsJU+TkzXURC5OdSWh9ibOwQ==",
"version": "12.24.10",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.24.10.tgz",
"integrity": "sha512-x5TFgkCIP4pPsRLpKoI86jv/q8t8FQOiM/0E8QKBzfMozWHfkKap2gA1hOki+B5g3IsBNpxbUnfOum1+dgvYww==",
"license": "MIT"
},
"node_modules/ms": {
@@ -6935,9 +6935,9 @@
}
},
"node_modules/react-i18next": {
"version": "16.5.1",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.1.tgz",
"integrity": "sha512-Hks6UIRZWW4c+qDAnx1csVsCGYeIR4MoBGQgJ+NUoNnO6qLxXuf8zu0xdcinyXUORgGzCdRsexxO1Xzv3sTdnw==",
"version": "16.5.2",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.2.tgz",
"integrity": "sha512-GG/SBVxx9dvrO1uCs8VYdKfOP8NEBUhNP+2VDQLCifRJ8DL1qPq296k2ACNGyZMDe7iyIlz/LMJTQOs8HXSRvw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7174,9 +7174,9 @@
}
},
"node_modules/simple-icons": {
"version": "16.4.0",
"resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-16.4.0.tgz",
"integrity": "sha512-8CKtCvx1Zq3L0CBsR4RR1MjGCXkXbzdspwl2yCxs8oWkstbzj2+DatRKDee/tuj3Ffd/2CDzwEky9RgG2yggew==",
"version": "16.5.0",
"resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-16.5.0.tgz",
"integrity": "sha512-72nn0oHADKx6Hknu7q6M0vfL8LiCUMKABOHane2+4xdqaFBSHfNNBjuZioihiqVQMz7IvVle4NKAM0IlXvl/9A==",
"funding": [
{
"type": "opencollective",
@@ -7577,16 +7577,16 @@
}
},
"node_modules/typescript-eslint": {
"version": "8.52.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.52.0.tgz",
"integrity": "sha512-atlQQJ2YkO4pfTVQmQ+wvYQwexPDOIgo+RaVcD7gHgzy/IQA+XTyuxNM9M9TVXvttkF7koBHmcwisKdOAf2EcA==",
"version": "8.53.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.53.0.tgz",
"integrity": "sha512-xHURCQNxZ1dsWn0sdOaOfCSQG0HKeqSj9OexIxrz6ypU6wHYOdX2I3D2b8s8wFSsSOYJb+6q283cLiLlkEsBYw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.52.0",
"@typescript-eslint/parser": "8.52.0",
"@typescript-eslint/typescript-estree": "8.52.0",
"@typescript-eslint/utils": "8.52.0"
"@typescript-eslint/eslint-plugin": "8.53.0",
"@typescript-eslint/parser": "8.53.0",
"@typescript-eslint/typescript-estree": "8.53.0",
"@typescript-eslint/utils": "8.53.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -7772,9 +7772,9 @@
}
},
"node_modules/vite": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz",
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8142,7 +8142,7 @@
},
"packages/llms": {
"name": "@page-agent/llms",
"version": "0.0.20",
"version": "0.0.21",
"license": "MIT",
"dependencies": {
"chalk": "^5.6.2",
@@ -8150,32 +8150,32 @@
}
},
"packages/page-agent": {
"version": "0.0.20",
"version": "0.0.21",
"license": "MIT",
"dependencies": {
"@page-agent/llms": "0.0.20",
"@page-agent/page-controller": "0.0.20",
"@page-agent/ui": "0.0.20",
"@page-agent/llms": "0.0.21",
"@page-agent/page-controller": "0.0.21",
"@page-agent/ui": "0.0.21",
"chalk": "^5.6.2",
"zod": "^4.3.5"
}
},
"packages/page-controller": {
"name": "@page-agent/page-controller",
"version": "0.0.20",
"version": "0.0.21",
"license": "MIT"
},
"packages/ui": {
"name": "@page-agent/ui",
"version": "0.0.20",
"version": "0.0.21",
"license": "MIT",
"dependencies": {
"ai-motion": "^0.4.7"
"ai-motion": "^0.4.8"
}
},
"packages/website": {
"name": "@page-agent/website",
"version": "0.0.20",
"version": "0.0.21",
"dependencies": {
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-separator": "^1.1.8",
@@ -8185,23 +8185,23 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.562.0",
"motion": "^12.24.0",
"motion": "^12.26.1",
"next-themes": "^0.4.6",
"rough-notation": "^0.5.1",
"simple-icons": "^16.4.0",
"simple-icons": "^16.5.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.0"
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.18",
"@types/react": "^19.2.2",
"@types/react": "^19.2.8",
"@types/react-dom": "^19.2.1",
"@vitejs/plugin-react-swc": "^4.1.0",
"i18next": "^25.7.3",
"i18next": "^25.7.4",
"i18next-browser-languagedetector": "^8.2.0",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react-i18next": "^16.5.1",
"react-i18next": "^16.5.2",
"tailwindcss": "^4.1.14",
"tw-animate-css": "^1.4.0",
"wouter": "^3.9.0"
+10 -10
View File
@@ -1,7 +1,7 @@
{
"name": "root",
"private": true,
"version": "0.0.20",
"version": "0.0.21",
"type": "module",
"workspaces": [
"packages/page-controller",
@@ -34,28 +34,28 @@
"prepare": "husky"
},
"devDependencies": {
"@commitlint/cli": "^20.3.0",
"@commitlint/config-conventional": "^20.3.0",
"@commitlint/cli": "^20.3.1",
"@commitlint/config-conventional": "^20.3.1",
"@eslint/js": "^9.39.2",
"@microsoft/api-extractor": "^7.55.2",
"@trivago/prettier-plugin-sort-imports": "^6.0.1",
"@types/node": "^25.0.3",
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/node": "^25.0.7",
"concurrently": "^9.2.1",
"dotenv": "^17.2.3",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react-dom": "^2.5.1",
"eslint-plugin-react-dom": "^2.5.7",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.26",
"eslint-plugin-react-x": "^2.5.1",
"globals": "^16.4.0",
"eslint-plugin-react-x": "^2.5.7",
"globals": "^17.0.0",
"husky": "^9.1.7",
"lint-staged": "^16.2.4",
"prettier": "^3.7.4",
"typescript": "^5.9.3",
"typescript-eslint": "^8.52.0",
"typescript-eslint": "^8.53.0",
"unplugin-dts": "^1.0.0-beta.6",
"vite": "^7.3.0",
"vite": "^7.3.1",
"vite-plugin-css-injected-by-js": "^3.5.2"
},
"lint-staged": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@page-agent/llms",
"version": "0.0.20",
"version": "0.0.21",
"type": "module",
"main": "./dist/lib/page-agent-llms.js",
"module": "./dist/lib/page-agent-llms.js",
+88 -74
View File
@@ -1,60 +1,58 @@
/**
* OpenAI Client implementation
* @note This client is only for demonstrating how to implement a LLM client.
* @note Use OpenAILenientClient instead.
*/
import { InvokeError, InvokeErrorType } from './errors'
import type { InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
import { modelPatch, zodToOpenAITool } from './utils'
/**
* @deprecated Use OpenAILenientClient instead.
* Client for OpenAI compatible APIs
*/
export class OpenAIClient implements LLMClient {
config: LLMConfig
config: Required<LLMConfig>
private fetch: typeof globalThis.fetch
constructor(config: LLMConfig) {
constructor(config: Required<LLMConfig>) {
this.config = config
this.fetch = config.customFetch
}
async invoke(
messages: Message[],
tools: Record<string, Tool>,
abortSignal?: AbortSignal
abortSignal?: AbortSignal,
options?: InvokeOptions
): Promise<InvokeResult> {
// 1. Convert tools to OpenAI format
const openaiTools = Object.entries(tools).map(([name, tool]) => zodToOpenAITool(name, tool))
const openaiTools = Object.entries(tools).map(([name, t]) => zodToOpenAITool(name, t))
// Build request body
const requestBody: Record<string, unknown> = {
model: this.config.model,
temperature: this.config.temperature,
messages,
tools: openaiTools,
parallel_tool_calls: false,
// Require tool call: specific tool if provided, otherwise any tool
tool_choice: options?.toolChoiceName
? { type: 'function', function: { name: options.toolChoiceName } }
: 'required',
}
// 2. Call API
let response: Response
try {
response = await fetch(`${this.config.baseURL}/chat/completions`, {
response = await this.fetch(`${this.config.baseURL}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.config.apiKey}`,
},
body: JSON.stringify(
modelPatch({
model: this.config.model,
temperature: this.config.temperature,
messages,
tools: openaiTools,
// tool_choice: 'required',
tool_choice: { type: 'function', function: { name: 'AgentOutput' } },
// model specific params
// reasoning_effort: 'minimal',
// verbosity: 'low',
parallel_tool_calls: false,
})
),
body: JSON.stringify(modelPatch(requestBody)),
signal: abortSignal,
})
} catch (error: unknown) {
// Network error
console.error(error)
throw new InvokeError(InvokeErrorType.NETWORK_ERROR, 'Network request failed', error)
}
@@ -92,77 +90,94 @@ export class OpenAIClient implements LLMClient {
)
}
// 4. Parse and validate response
const data = await response.json()
// 4. Check finish_reason
const choice = data.choices?.[0]
if (!choice) {
throw new InvokeError(InvokeErrorType.UNKNOWN, 'No choices in response', data)
}
// Check finish_reason
switch (choice.finish_reason) {
case 'tool_calls':
// ✅ Normal
case 'function_call': // gemini
case 'stop': // some models use this even with tool calls
break
case 'length':
// ⚠️ Token limit reached
throw new InvokeError(
InvokeErrorType.CONTEXT_LENGTH,
'Response truncated: max tokens reached',
data
'Response truncated: max tokens reached'
)
case 'content_filter':
// ❌ Content filtered
throw new InvokeError(
InvokeErrorType.CONTENT_FILTER,
'Content filtered by safety system',
data
)
case 'stop':
// ❌ Did not call tool (we require tool call)
throw new InvokeError(InvokeErrorType.NO_TOOL_CALL, 'Model did not call any tool', data)
throw new InvokeError(InvokeErrorType.CONTENT_FILTER, 'Content filtered by safety system')
default:
throw new InvokeError(
InvokeErrorType.UNKNOWN,
`Unexpected finish_reason: ${choice.finish_reason}`,
data
`Unexpected finish_reason: ${choice.finish_reason}`
)
}
// 5. Parse tool call
const toolCall = choice.message?.tool_calls?.[0]
if (!toolCall) {
throw new InvokeError(InvokeErrorType.NO_TOOL_CALL, 'No tool call found in response', data)
}
// Apply normalizeResponse if provided (for fixing format issues automatically)
const normalizedData = options?.normalizeResponse ? options.normalizeResponse(data) : data
const normalizedChoice = (normalizedData as any).choices?.[0]
const toolName = toolCall.function.name
const tool = tools[toolName]
if (!tool) {
throw new InvokeError(InvokeErrorType.UNKNOWN, `Tool ${toolName} not found`, data)
}
// 6. Parse and validate arguments
let toolArgs: unknown
try {
toolArgs = JSON.parse(toolCall.function.arguments)
} catch (e) {
throw new InvokeError(InvokeErrorType.INVALID_TOOL_ARGS, 'Invalid JSON in tool arguments', e)
}
// Validate against zod schema
const validation = tool.inputSchema.safeParse(toolArgs)
if (!validation.success) {
// Get tool name from response
const toolCallName = normalizedChoice?.message?.tool_calls?.[0]?.function?.name
if (!toolCallName) {
throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS,
`Tool arguments validation failed: ${validation.error.message}`,
validation.error
InvokeErrorType.NO_TOOL_CALL,
'No tool call found in response',
normalizedData
)
}
// 7. Execute tool
const tool = tools[toolCallName]
if (!tool) {
throw new InvokeError(
InvokeErrorType.UNKNOWN,
`Tool "${toolCallName}" not found in tools`,
normalizedData
)
}
// Extract and parse tool arguments
const argString = normalizedChoice.message?.tool_calls?.[0]?.function?.arguments
if (!argString) {
throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS,
'No tool call arguments found',
normalizedData
)
}
let parsedArgs: unknown
try {
parsedArgs = JSON.parse(argString)
} catch (error) {
throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS,
'Failed to parse tool arguments as JSON',
error
)
}
// Validate with schema
const validation = tool.inputSchema.safeParse(parsedArgs)
if (!validation.success) {
console.error(validation.error)
throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS,
'Tool arguments validation failed',
validation.error
)
}
const toolInput = validation.data
// 5. Execute tool
let toolResult: unknown
try {
toolResult = await tool.execute(validation.data)
toolResult = await tool.execute(toolInput)
} catch (e) {
throw new InvokeError(
InvokeErrorType.TOOL_EXECUTION_ERROR,
@@ -171,12 +186,11 @@ export class OpenAIClient implements LLMClient {
)
}
// 8. Return result (including cache tokens)
// Return result
return {
toolCall: {
// id: toolCall.id,
name: toolName,
args: validation.data as Record<string, unknown>,
name: toolCallName,
args: toolInput,
},
toolResult,
usage: {
-124
View File
@@ -1,124 +0,0 @@
/**
* OpenAI Client implementation
*/
import { InvokeError, InvokeErrorType } from './errors'
import type { InvokeResult, LLMClient, LLMConfig, MacroToolInput, Message, Tool } from './types'
import { lenientParseMacroToolCall, modelPatch, zodToOpenAITool } from './utils'
export class OpenAIClient implements LLMClient {
config: Required<LLMConfig>
private fetch: typeof globalThis.fetch
constructor(config: Required<LLMConfig>) {
this.config = config
this.fetch = config.customFetch
}
async invoke(
messages: Message[],
tools: { AgentOutput: Tool<MacroToolInput> },
abortSignal?: AbortSignal
): Promise<InvokeResult> {
// 1. Convert tools to OpenAI format
const openaiTools = Object.entries(tools).map(([name, tool]) => zodToOpenAITool(name, tool))
// 2. Call API
let response: Response
try {
response = await this.fetch(`${this.config.baseURL}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.config.apiKey}`,
},
body: JSON.stringify(
modelPatch({
model: this.config.model,
temperature: this.config.temperature,
messages,
tools: openaiTools,
// tool_choice: 'required',
tool_choice: { type: 'function', function: { name: 'AgentOutput' } },
parallel_tool_calls: false,
})
),
signal: abortSignal,
})
} catch (error: unknown) {
// Network error
console.error(error)
throw new InvokeError(InvokeErrorType.NETWORK_ERROR, 'Network request failed', error)
}
// 3. Handle HTTP errors
if (!response.ok) {
const errorData = await response.json().catch()
const errorMessage =
(errorData as { error?: { message?: string } }).error?.message || response.statusText
if (response.status === 401 || response.status === 403) {
throw new InvokeError(
InvokeErrorType.AUTH_ERROR,
`Authentication failed: ${errorMessage}`,
errorData
)
}
if (response.status === 429) {
throw new InvokeError(
InvokeErrorType.RATE_LIMIT,
`Rate limit exceeded: ${errorMessage}`,
errorData
)
}
if (response.status >= 500) {
throw new InvokeError(
InvokeErrorType.SERVER_ERROR,
`Server error: ${errorMessage}`,
errorData
)
}
throw new InvokeError(
InvokeErrorType.UNKNOWN,
`HTTP ${response.status}: ${errorMessage}`,
errorData
)
}
// parse response
const data = await response.json()
const tool = tools.AgentOutput
const macroToolInput = lenientParseMacroToolCall(data, tool.inputSchema as any)
// Execute tool
let toolResult: unknown
try {
toolResult = await tool.execute(macroToolInput)
} catch (e) {
throw new InvokeError(
InvokeErrorType.TOOL_EXECUTION_ERROR,
`Tool execution failed: ${(e as Error).message}`,
e
)
}
// Return result (including cache tokens)
return {
toolCall: {
// id: toolCall.id,
name: 'AgentOutput',
args: macroToolInput,
},
toolResult,
usage: {
promptTokens: data.usage?.prompt_tokens ?? 0,
completionTokens: data.usage?.completion_tokens ?? 0,
totalTokens: data.usage?.total_tokens ?? 0,
cachedTokens: data.usage?.prompt_tokens_details?.cached_tokens,
reasoningTokens: data.usage?.completion_tokens_details?.reasoning_tokens,
},
rawResponse: data,
}
}
}
+6 -56
View File
@@ -1,37 +1,4 @@
/**
* @topic LLM 与主流程的隔离
* @reasoning
* 将 llm 的调用和主流程分开是复杂的,
* 因为 agent 的 tool call 通常集成在 llm 模块中,而而先得到 llm 返回,然后处理工具调用
* tools 和 llm 调用的逻辑不可避免地耦合在一起,tool 的执行又和主流程耦合在一起
* 而 history 的维护和更新逻辑,又必须嵌入多轮 tool call 中
* @reasoning
* - 放弃框架提供的自动的多轮调用,每轮调用都由主流程发起
* - 理想情况下,llm 调用应该获得 structured output,然后由额外的模块触发 tool call,目前模型和框架都无法实现
* - 当前只能将 llm api 和 本地 tool call 耦合在一起,不关心其中的衔接方式
* @conclusion
* - @llm responsibility boundary:
* - call llm api with given messages and tools
* - invoke tool call and get the result of the tool
* - return the result to main loop
* - @main_loop responsibility boundary:
* - maintain all behaviors of an **agent**
* @conclusion
* - 这里的 llm 模块不是 agent,只负责一轮 llm 调用和工具调用,无状态
*/
/**
* @topic 结构化输出
* @facts
* - 几乎所有模型都支持 tool call schema
* - 几乎所有模型都支持返回 json
* - 只有 openAI/grok/gemini 支持 schema 并保证格式
* - 主流模型都支持 tool_choice: required
* - 除了 qwen 必须指定一个函数名 (9月上新后支持)
* @conclusion
* - 永远使用 tool call 来返回结构化数据,禁止模型直接返回(视为出错)
* - 不能假设 tool 参数合法,必须有修复机制,而且修复也应该使用 tool call 返回
*/
import { OpenAIClient } from './OpenAILenientClient'
import { OpenAIClient } from './OpenAIClient'
import {
DEFAULT_API_KEY,
DEFAULT_BASE_URL,
@@ -40,27 +7,9 @@ import {
LLM_MAX_RETRIES,
} from './constants'
import { InvokeError } from './errors'
import type {
AgentBrain,
InvokeResult,
LLMClient,
LLMConfig,
MacroToolInput,
MacroToolResult,
Message,
Tool,
} from './types'
import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
export type {
AgentBrain,
InvokeResult,
LLMClient,
LLMConfig,
MacroToolInput,
MacroToolResult,
Message,
Tool,
}
export type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool }
export function parseLLMConfig(config: LLMConfig): Required<LLMConfig> {
return {
@@ -93,11 +42,12 @@ export class LLM extends EventTarget {
async invoke(
messages: Message[],
tools: Record<string, Tool>,
abortSignal: AbortSignal
abortSignal: AbortSignal,
options?: InvokeOptions
): Promise<InvokeResult> {
return await withRetry(
async () => {
const result = await this.client.invoke(messages, tools, abortSignal)
const result = await this.client.invoke(messages, tools, abortSignal, options)
return result
},
+20 -34
View File
@@ -32,6 +32,24 @@ export interface Tool<TParams = any, TResult = any> {
execute: (args: TParams) => Promise<TResult>
}
/**
* Invoke options for LLM call
*/
export interface InvokeOptions {
/**
* Force LLM to call a specific tool by name.
* If provided: tool_choice = { type: 'function', function: { name: toolChoiceName } }
* If not provided: tool_choice = 'required' (must call some tool, but model chooses which)
*/
toolChoiceName?: string
/**
* Response normalization function.
* Called before parsing the response.
* Used to fix various response format errors from the model.
*/
normalizeResponse?: (response: any) => any
}
/**
* LLM Client interface
* Note: Does not use generics because each tool in the tools array has different types
@@ -40,7 +58,8 @@ export interface LLMClient {
invoke(
messages: Message[],
tools: Record<string, Tool>,
abortSignal?: AbortSignal
abortSignal?: AbortSignal,
options?: InvokeOptions
): Promise<InvokeResult>
}
@@ -82,36 +101,3 @@ export interface LLMConfig {
*/
customFetch?: typeof globalThis.fetch
}
/**
* Agent brain state - the reflection-before-action model
*
* Every tool call must first reflect on:
* - evaluation_previous_goal: How well did the previous action achieve its goal?
* - memory: Key information to remember for future steps
* - next_goal: What should be accomplished in the next action?
*/
export interface AgentBrain {
// thinking?: string
evaluation_previous_goal: string
memory: string
next_goal: string
}
/**
* MacroTool input structure
*
* This is the core abstraction that enforces the "reflection-before-action" mental model.
* Before executing any action, the LLM must output its reasoning state.
*/
export interface MacroToolInput extends AgentBrain {
action: Record<string, any>
}
/**
* MacroTool output structure
*/
export interface MacroToolResult {
input: MacroToolInput
output: string
}
+18 -175
View File
@@ -4,8 +4,7 @@
import chalk from 'chalk'
import { z } from 'zod'
import { InvokeError, InvokeErrorType } from './errors'
import type { MacroToolInput, Tool } from './types'
import type { Tool } from './types'
function debug(message: string) {
console.debug(chalk.gray('[LLM]'), message)
@@ -26,176 +25,6 @@ export function zodToOpenAITool(name: string, tool: Tool) {
}
}
/**
* Although some models cannot guarantee correct response. Common issues are fixable:
* - Instead of returning a proper tool call. Return the tool call parameters in the message content.
* - Returned tool calls or messages don't follow the nested MacroToolInput format.
*/
export function lenientParseMacroToolCall(
responseData: any,
inputSchema: z.ZodObject<MacroToolInput & Record<string, any>>
): MacroToolInput {
// check
const choice = responseData.choices?.[0]
if (!choice) {
throw new InvokeError(InvokeErrorType.UNKNOWN, 'No choices in response', responseData)
}
// check
switch (choice.finish_reason) {
case 'tool_calls':
case 'function_call': // gemini
case 'stop': // will try a robust parse
// ✅ Normal
break
case 'length':
// ⚠️ Token limit reached
throw new InvokeError(
InvokeErrorType.CONTEXT_LENGTH,
'Response truncated: max tokens reached'
)
case 'content_filter':
// ❌ Content filtered
throw new InvokeError(InvokeErrorType.CONTENT_FILTER, 'Content filtered by safety system')
default:
throw new InvokeError(
InvokeErrorType.UNKNOWN,
`Unexpected finish_reason: ${choice.finish_reason}`
)
}
// Extract action schema from MacroToolInput schema
const actionSchema = inputSchema.shape.action
if (!actionSchema) {
throw new Error('inputSchema must have an "action" field')
}
// patch stopReason mis-format
let arg: string | null = null
// try to use tool call
const toolCall = choice.message?.tool_calls?.[0]?.function
arg = toolCall?.arguments ?? null
if (arg && toolCall.name !== 'AgentOutput') {
// TODO: check if toolCall.name is a valid action name
// case: instead of AgentOutput, the model returned a action name as tool call
console.log(chalk.yellow('lenientParseMacroToolCall: #1 fixing incorrect tool call'))
let tmpArg
try {
tmpArg = JSON.parse(arg)
} catch (error) {
throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS,
'Failed to parse tool arguments as JSON',
error
)
}
arg = JSON.stringify({ action: { [toolCall.name]: tmpArg } })
}
if (!arg) {
// try to use message content as JSON
arg = choice.message?.content.trim() || null
}
if (!arg) {
throw new InvokeError(
InvokeErrorType.NO_TOOL_CALL,
'No tool call or content found in response',
responseData
)
}
// make sure is valid JSON
let parsedArgs: any
try {
parsedArgs = JSON.parse(arg)
} catch (error) {
throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS,
'Failed to parse tool arguments as JSON',
error
)
}
// patch incomplete formats
if (parsedArgs.action || parsedArgs.evaluation_previous_goal || parsedArgs.next_goal) {
// case: nested MacroToolInput format (correct format)
// some models may give a empty action (they may think reasoning and action should be separate)
if (!parsedArgs.action) {
console.log(chalk.yellow('lenientParseMacroToolCall: #2 fixing incorrect tool call'))
parsedArgs.action = {
wait: { seconds: 1 },
}
}
} else if (parsedArgs.type && parsedArgs.function) {
// case: upper level function call format provided. only keep its arguments
// TODO: check if function name is a valid action name
if (parsedArgs.function.name !== 'AgentOutput')
throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS,
`Expected function name "AgentOutput", got "${parsedArgs.function.name}"`,
null
)
console.log(chalk.yellow('lenientParseMacroToolCall: #3 fixing incorrect tool call'))
parsedArgs = parsedArgs.function.arguments
} else if (parsedArgs.name && parsedArgs.arguments) {
// case: upper level function call format provided. only keep its arguments
// TODO: check if function name is a valid action name
if (parsedArgs.name !== 'AgentOutput')
throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS,
`Expected function name "AgentOutput", got "${parsedArgs.name}"`,
null
)
console.log(chalk.yellow('lenientParseMacroToolCall: #4 fixing incorrect tool call'))
parsedArgs = parsedArgs.arguments
} else {
// case: only action parameters provided, wrap into MacroToolInput
// TODO: check if action name is valid
console.log(chalk.yellow('lenientParseMacroToolCall: #5 fixing incorrect tool call'))
parsedArgs = { action: parsedArgs } as MacroToolInput
}
// make sure it's not wrapped as string
if (typeof parsedArgs === 'string') {
console.log(chalk.yellow('lenientParseMacroToolCall: #6 fixing incorrect tool call'))
try {
parsedArgs = JSON.parse(parsedArgs)
} catch (error) {
throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS,
'Failed to parse nested tool arguments as JSON',
error
)
}
}
const validation = inputSchema.safeParse(parsedArgs)
if (validation.success) {
return validation.data as unknown as MacroToolInput
} else {
const action = parsedArgs.action ?? {}
const actionName = Object.keys(action)[0] || 'unknown'
const actionArgs = JSON.stringify(action[actionName] || 'unknown')
// TODO: check if action name is valid. give a readable error message
throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS,
`Tool arguments validation failed: action "${actionName}" with args ${actionArgs}`,
validation.error
)
}
}
/**
* Patch model specific parameters
*/
@@ -205,11 +34,25 @@ export function modelPatch(body: Record<string, any>) {
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)
}
if (modelName.startsWith('claude')) {
debug('Applying Claude patch: change tool_choice and disable thinking')
body.tool_choice = { type: 'tool', name: 'AgentOutput' }
debug('Applying Claude patch: disable thinking')
body.thinking = { type: 'disabled' }
// body.reasoning = { enabled: '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 }
}
}
if (modelName.startsWith('grok')) {
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "page-agent",
"private": false,
"version": "0.0.20",
"version": "0.0.21",
"type": "module",
"main": "./dist/esm/page-agent.js",
"module": "./dist/esm/page-agent.js",
@@ -46,8 +46,8 @@
"dependencies": {
"chalk": "^5.6.2",
"zod": "^4.3.5",
"@page-agent/llms": "0.0.20",
"@page-agent/page-controller": "0.0.20",
"@page-agent/ui": "0.0.20"
"@page-agent/llms": "0.0.21",
"@page-agent/page-controller": "0.0.21",
"@page-agent/ui": "0.0.21"
}
}
+41 -14
View File
@@ -2,13 +2,7 @@
* Copyright (C) 2025 Alibaba Group Holding Limited
* All rights reserved.
*/
import {
type AgentBrain,
LLM,
type MacroToolInput,
type MacroToolResult,
type Tool,
} from '@page-agent/llms'
import { LLM, type Tool } from '@page-agent/llms'
import { PageController } from '@page-agent/page-controller'
import { Panel, SimulatorMask } from '@page-agent/ui'
import chalk from 'chalk'
@@ -18,15 +12,46 @@ import type { PageAgentConfig } from './config'
import { MAX_STEPS } from './config/constants'
import SYSTEM_PROMPT from './prompts/system_prompt.md?raw'
import { tools } from './tools'
import { trimLines, uid, waitUntil } from './utils'
import { normalizeResponse, trimLines, uid, waitUntil } from './utils'
import { assert } from './utils/assert'
/**
* Agent brain state - the reflection-before-action model
*
* Every tool call must first reflect on:
* - evaluation_previous_goal: How well did the previous action achieve its goal?
* - memory: Key information to remember for future steps
* - next_goal: What should be accomplished in the next action?
*/
export interface AgentReflection {
evaluation_previous_goal: string
memory: string
next_goal: string
}
/**
* MacroTool input structure
*
* This is the core abstraction that enforces the "reflection-before-action" mental model.
* Before executing any action, the LLM must output its reasoning state.
*/
export interface MacroToolInput extends Partial<AgentReflection> {
action: Record<string, any>
}
/**
* MacroTool output structure
*/
export interface MacroToolResult {
input: MacroToolInput
output: string
}
export type { PageAgentConfig }
export { tool, type PageAgentTool } from './tools'
export type { AgentBrain, MacroToolInput, MacroToolResult }
export interface AgentHistory {
brain: AgentBrain
brain: Partial<AgentReflection>
action: {
name: string
input: any
@@ -124,9 +149,6 @@ export class PageAgent extends EventTarget {
window.addEventListener('beforeunload', this.#beforeUnloadListener)
}
/**
* @todo maybe return something?
*/
async execute(task: string): Promise<ExecutionResult> {
if (!task) throw new Error('Task is required')
this.task = task
@@ -183,7 +205,11 @@ export class PageAgent extends EventTarget {
},
],
{ AgentOutput: this.#packMacroTool() },
this.#abortController.signal
this.#abortController.signal,
{
toolChoiceName: 'AgentOutput',
normalizeResponse,
}
)
const macroResult = result.toolResult as MacroToolResult
@@ -279,6 +305,7 @@ export class PageAgent extends EventTarget {
})
return {
description: 'You MUST call this tool every step. Outputs your reflections and next action.',
inputSchema: macroToolSchema as zod.ZodType<MacroToolInput>,
execute: async (input: MacroToolInput): Promise<MacroToolResult> => {
// abort
+157
View File
@@ -0,0 +1,157 @@
import chalk from 'chalk'
/**
* Normalize LLM response and fix common format issues.
*
* Handles:
* - No tool_calls but JSON in message.content (fallback)
* - Model returns action name as tool call instead of AgentOutput
* - Arguments wrapped as double JSON string
* - Nested function call format
* - Missing action field (fallback to wait)
* - etc.
*/
export function normalizeResponse(response: any): any {
let resolvedArguments = null as any
const choice = (response as { choices?: Choice[] }).choices?.[0]
if (!choice) throw new Error('No choices in response')
const message = choice.message
if (!message) throw new Error('No message in choice')
const toolCall = message.tool_calls?.[0]
// fix level and location of arguments
if (toolCall?.function?.arguments) {
resolvedArguments = safeJsonParse(toolCall.function.arguments)
// case: sometimes the model only returns the action level
if (toolCall.function.name && toolCall.function.name !== 'AgentOutput') {
console.log(chalk.yellow(`[normalizeResponse] #1: fixing tool_call`))
resolvedArguments = { action: safeJsonParse(resolvedArguments) }
}
} else {
// case: sometimes the model returns json in content instead of tool_calls
if (message.content) {
const content = message.content.trim()
const jsonInContent = retrieveJsonFromString(content)
if (jsonInContent) {
resolvedArguments = safeJsonParse(jsonInContent)
// case: sometimes the content json includes upper level wrapper
if (resolvedArguments?.name === 'AgentOutput') {
console.log(chalk.yellow(`[normalizeResponse] #2: fixing tool_call`))
resolvedArguments = safeJsonParse(resolvedArguments.arguments)
}
// case: sometimes even 2-levels of wrapping
if (resolvedArguments?.type === 'function') {
console.log(chalk.yellow(`[normalizeResponse] #3: fixing tool_call`))
resolvedArguments = safeJsonParse(resolvedArguments.function.arguments)
}
// case: and sometimes action level only
// todo: needs better detection logic
if (
!resolvedArguments?.action &&
!resolvedArguments?.evaluation_previous_goal &&
!resolvedArguments?.memory &&
!resolvedArguments?.next_goal &&
!resolvedArguments?.thinking
) {
console.log(chalk.yellow(`[normalizeResponse] #4: fixing tool_call`))
resolvedArguments = { action: safeJsonParse(resolvedArguments) }
}
} else {
throw new Error('No tool_call and the message content does not contain valid JSON')
}
} else {
throw new Error('No tool_call nor message content is present')
}
}
// fix double stringified arguments
resolvedArguments = safeJsonParse(resolvedArguments)
if (resolvedArguments.action) {
resolvedArguments.action = safeJsonParse(resolvedArguments.action)
}
// fix incomplete formats
if (!resolvedArguments.action) {
console.log(chalk.yellow(`[normalizeResponse] #5: fixing tool_call`))
resolvedArguments.action = { name: 'wait', input: { seconds: 1 } }
}
// pack back to standard format
return {
...response,
choices: [
{
...choice,
message: {
...message,
tool_calls: [
{
...(toolCall || {}),
function: {
...(toolCall?.function || {}),
name: 'AgentOutput',
arguments: JSON.stringify(resolvedArguments),
},
},
],
},
},
],
}
}
/**
* Safely parse JSON, return original input if not json.
*/
function safeJsonParse(input: any): any {
if (typeof input === 'string') {
try {
return JSON.parse(input.trim())
} catch {
return input
}
}
return input
}
/**
* Extract and parse JSON from a string.
* - Treat content between the first `{` and the last `}` as JSON.
* - Try to parse that content as JSON and return the parsed value (object/array/primitive) if successful, otherwise return null.
*/
function retrieveJsonFromString(str: string): any {
try {
const json = /({[\s\S]*})/.exec(str) ?? []
if (json.length === 0) {
return null
}
return JSON.parse(json[0]!)
} catch {
return null
}
}
interface Choice {
message?: {
role?: 'assistant'
content?: string
tool_calls?: {
id?: string
type?: 'function'
function?: {
name?: string
arguments?: string
}
}[]
}
index?: 0
finish_reason?: 'tool_calls'
}
+2
View File
@@ -1,3 +1,5 @@
export { normalizeResponse } from './autoFixer'
/**
* Wait until condition becomes true
* @returns Returns when condition becomes true, throws otherwise
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@page-agent/page-controller",
"version": "0.0.20",
"version": "0.0.21",
"type": "module",
"main": "./dist/lib/page-controller.js",
"module": "./dist/lib/page-controller.js",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@page-agent/ui",
"version": "0.0.20",
"version": "0.0.21",
"type": "module",
"main": "./dist/lib/page-agent-ui.js",
"module": "./dist/lib/page-agent-ui.js",
@@ -36,6 +36,6 @@
"postpublish": "node -e \"['LICENSE'].forEach(f=>{try{require('fs').unlinkSync(f)}catch{}})\""
},
"dependencies": {
"ai-motion": "^0.4.7"
"ai-motion": "^0.4.8"
}
}
+6 -6
View File
@@ -1,7 +1,7 @@
{
"name": "@page-agent/website",
"private": true,
"version": "0.0.20",
"version": "0.0.21",
"type": "module",
"scripts": {
"dev": "vite",
@@ -11,14 +11,14 @@
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.18",
"@types/react": "^19.2.2",
"@types/react": "^19.2.8",
"@types/react-dom": "^19.2.1",
"@vitejs/plugin-react-swc": "^4.1.0",
"i18next": "^25.7.3",
"i18next": "^25.7.4",
"i18next-browser-languagedetector": "^8.2.0",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react-i18next": "^16.5.1",
"react-i18next": "^16.5.2",
"tailwindcss": "^4.1.14",
"tw-animate-css": "^1.4.0",
"wouter": "^3.9.0"
@@ -32,10 +32,10 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.562.0",
"motion": "^12.24.0",
"motion": "^12.26.1",
"next-themes": "^0.4.6",
"rough-notation": "^0.5.1",
"simple-icons": "^16.4.0",
"simple-icons": "^16.5.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.0"
}
+1 -1
View File
@@ -32,7 +32,7 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
{
title: t('nav.features'),
items: [
{ title: t('nav.models'), path: '/docs/features/model-integration' },
{ title: t('nav.models'), path: '/docs/features/models' },
{ title: t('nav.custom_tools'), path: '/docs/features/custom-tools' },
{ title: t('nav.knowledge_injection'), path: '/docs/features/custom-instructions' },
{ title: t('nav.data_masking'), path: '/docs/features/data-masking' },
@@ -4,13 +4,19 @@ import CodeEditor from '@/components/CodeEditor'
// Recommended models: lightweight with excellent tool call capabilities
const MODELS = {
recommended: ['gpt-4.1-mini', 'claude-haiku-4.5', 'gemini-3-flash', 'deepseek-3.2', 'gpt-5.2'],
verified: [
recommended: [
'gpt-4.1-mini',
'claude-haiku-4.5',
'gemini-3-flash',
'deepseek-3.2',
'gpt-5.2',
'qwen-3-max',
],
verified: [
'qwen-3-plus',
'gpt-4.1',
'gpt-5',
'gpt-5-mini',
'gpt-5.1',
'grok-4',
'grok-code-fast',
'claude-sonnet-3.5',
@@ -21,7 +27,7 @@ const MODELS = {
],
}
export default function ModelIntegration() {
export default function Models() {
const { t } = useTranslation('docs')
const allModels = [...MODELS.recommended, ...MODELS.verified]
+3 -3
View File
@@ -7,7 +7,7 @@ import DocsLayout from './pages/docs/Layout'
import Instructions from './pages/docs/features/custom-instructions/page'
import CustomTools from './pages/docs/features/custom-tools/page'
import DataMasking from './pages/docs/features/data-masking/page'
import ModelIntegration from './pages/docs/features/model-integration/page'
import Models from './pages/docs/features/models/page'
// Integration pages
import BestPractices from './pages/docs/integration/best-practices/page'
import CdnSetup from './pages/docs/integration/cdn-setup/page'
@@ -80,11 +80,11 @@ export default function Router() {
</div>
</Route>
<Route path="/docs/features/model-integration">
<Route path="/docs/features/models">
<div className="min-h-screen bg-white dark:bg-gray-900">
<Header />
<DocsLayout>
<ModelIntegration />
<Models />
</DocsLayout>
</div>
</Route>