page-agent 1.11.0 β†’ 1.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,9 +5,18 @@
5
5
  <img alt="Page Agent Banner" src="https://page-agent.github.io/assets/readme/banner-light.png">
6
6
  </picture>
7
7
 
8
- [![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)
8
+ [![CI](https://img.shields.io/github/actions/workflow/status/alibaba/page-agent/main-ci.yml?branch=main&style=flat-square&label=ci)](https://github.com/alibaba/page-agent/actions/workflows/main-ci.yml)
9
+ [![npm](https://img.shields.io/npm/v/page-agent?style=flat-square&label=npm)](https://www.npmjs.com/package/page-agent)
10
+ [![downloads](https://img.shields.io/npm/dt/page-agent?style=flat-square)](https://www.npmjs.com/package/page-agent)
11
+ [![size](https://img.shields.io/bundlephobia/minzip/page-agent?style=flat-square&label=size)](https://bundlephobia.com/package/page-agent)
12
+ [![license](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://opensource.org/licenses/MIT)
13
+ [![typescript](https://img.shields.io/badge/%3C%2F%3E-typescript-blue?style=flat-square)](http://www.typescriptlang.org/)
14
+ [![Chrome Web Store Rating](https://img.shields.io/chrome-web-store/rating/akldabonmimlicnjlflnapfeklbfemhj?style=flat-square&label=chrome%20rating)](https://chromewebstore.google.com/detail/page-agent-ext/akldabonmimlicnjlflnapfeklbfemhj)
15
+ [![GitHub stars](https://img.shields.io/github/stars/alibaba/page-agent.svg)](https://github.com/alibaba/page-agent)
9
16
 
10
- The GUI Agent Living in Your Webpage. Control web interfaces with natural language.
17
+ The GUI Agent Living in Your Webpage. One script gives any web page its own AI agent.
18
+
19
+ <a href="https://trendshift.io/repositories/22551?utm_source=repository-badge&amp;utm_medium=badge&amp;utm_campaign=badge-repository-22551" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/22551" alt="alibaba%2Fpage-agent | Trendshift" width="180"/></a>
11
20
 
12
21
  🌐 **English** | [δΈ­ζ–‡](./docs/README-zh.md)
13
22
 
@@ -15,7 +24,7 @@ The GUI Agent Living in Your Webpage. Control web interfaces with natural langua
15
24
 
16
25
  <!-- demo video -->
17
26
 
18
- https://github.com/user-attachments/assets/a1f2eae2-13fb-4aae-98cf-a3fc1620a6c2
27
+ [![Watch the demo](https://page-agent.github.io/assets/readme/poster.jpg)](https://github.com/user-attachments/assets/a1f2eae2-13fb-4aae-98cf-a3fc1620a6c2)
19
28
 
20
29
  ---
21
30
 
@@ -27,6 +36,7 @@ https://github.com/user-attachments/assets/a1f2eae2-13fb-4aae-98cf-a3fc1620a6c2
27
36
  - **πŸ“– Text-based DOM manipulation**
28
37
  - No screenshots. No multi-modal LLMs or special permissions needed.
29
38
  - **🧠 Bring your own LLMs**
39
+ - Works with most mainstream models, including locally deployed ones. See [supported models](https://alibaba.github.io/page-agent/docs/features/models).
30
40
  - **πŸ™ Optional [chrome extension](https://alibaba.github.io/page-agent/docs/features/chrome-extension) for multi-page tasks.**
31
41
  - And an [MCP Server (Beta)](https://alibaba.github.io/page-agent/docs/features/mcp-server) to control it from outside
32
42
 
@@ -35,7 +45,7 @@ https://github.com/user-attachments/assets/a1f2eae2-13fb-4aae-98cf-a3fc1620a6c2
35
45
  - **SaaS AI Copilot** β€” Ship an AI copilot in your product in lines of code. No backend rewrite.
36
46
  - **Smart Form Filling** β€” Turn 20-click workflows into one sentence. Perfect for ERP, CRM, and admin systems.
37
47
  - **Accessibility** β€” Make any web app accessible through natural language. Voice commands, screen readers, zero barrier.
38
- - **Multi-page Agent** β€” Extend your own web agent's reach across browser tabs [chrome extension](https://alibaba.github.io/page-agent/docs/features/chrome-extension).
48
+ - **Multi-page Agent** β€” Extend your own web agent's reach across browser tabs via the [Chrome extension](https://alibaba.github.io/page-agent/docs/features/chrome-extension).
39
49
  - **MCP** - Allow your agent clients to control your browser.
40
50
 
41
51
  ## πŸš€ Quick Start
@@ -45,17 +55,19 @@ https://github.com/user-attachments/assets/a1f2eae2-13fb-4aae-98cf-a3fc1620a6c2
45
55
  Fastest way to try PageAgent with our free Demo LLM:
46
56
 
47
57
  ```html
48
- <script src="{URL}" crossorigin="true"></script>
58
+ <script
59
+ src="https://cdn.jsdelivr.net/npm/page-agent@1.12.1/dist/iife/page-agent.demo.js"
60
+ crossorigin="anonymous"
61
+ ></script>
49
62
  ```
50
63
 
51
64
  > **⚠️ 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).
65
+ > Add `?autoInit=false` to load the script without creating the demo agent automatically. You can then instantiate it with `new window.PageAgent(...)`.
52
66
 
53
67
  | Mirrors | URL |
54
68
  | ------- | ----------------------------------------------------------------------------------- |
55
- | Global | https://cdn.jsdelivr.net/npm/page-agent@1.11.0/dist/iife/page-agent.demo.js |
56
- | China | https://registry.npmmirror.com/page-agent/1.11.0/files/dist/iife/page-agent.demo.js |
57
-
58
- Add `?autoInit=false` to load the script without creating the demo agent automatically. You can then instantiate it with `new window.PageAgent(...)`.
69
+ | Global | https://cdn.jsdelivr.net/npm/page-agent@1.12.1/dist/iife/page-agent.demo.js |
70
+ | China | https://registry.npmmirror.com/page-agent/1.12.1/files/dist/iife/page-agent.demo.js |
59
71
 
60
72
  ### NPM Installation
61
73
 
@@ -78,20 +90,12 @@ await agent.execute('Click the login button')
78
90
 
79
91
  For more programmatic usage, see [πŸ“– Documentations](https://alibaba.github.io/page-agent/docs/introduction/overview).
80
92
 
81
- ## 🌟 Awesome Page Agent
82
-
83
- Built something cool with PageAgent? Add it here! Open a PR to share your project.
84
-
85
- > These are community projects β€” not maintained or endorsed by us. Use at your own discretion.
86
-
87
- | Project | Description |
88
- | -------- | ----------------------------------------------------------- |
89
- | _Yours?_ | [Open a PR](https://github.com/alibaba/page-agent/pulls) πŸ™Œ |
90
-
91
93
  ## 🀝 Contributing
92
94
 
93
95
  We welcome contributions from the community! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines and [docs/developer-guide.md](docs/developer-guide.md) for local development workflows.
94
96
 
97
+ Built something cool with PageAgent? Share it in [Show and Tell](https://github.com/alibaba/page-agent/discussions/categories/show-and-tell). πŸ™Œ
98
+
95
99
  Please read the [maintainer's note](https://github.com/alibaba/page-agent/issues/349) on principles and current state.
96
100
 
97
101
  Contributions generated entirely by **bots or AI** without substantial human involvement will **not be accepted**.
@@ -118,6 +122,4 @@ excellent work on web automation and DOM interaction patterns that helped make
118
122
  this project possible.
119
123
  ```
120
124
 
121
- ---
122
-
123
125
  **⭐ Star this repo if you find PageAgent helpful!**
@@ -67,7 +67,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
67
67
  `:`
68
68
  `)+n,i=r+1,r=e.indexOf(`
69
69
  `,i)}while(r!==-1);return a+=e.slice(i),a}var{stdout:stdoutColor,stderr:stderrColor}=supportsColor,GENERATOR=Symbol(`GENERATOR`),STYLER=Symbol(`STYLER`),IS_EMPTY=Symbol(`IS_EMPTY`),levelMapping=[`ansi`,`ansi`,`ansi256`,`ansi16m`],styles=Object.create(null),applyOptions=(e,t={})=>{if(t.level&&!(Number.isInteger(t.level)&&t.level>=0&&t.level<=3))throw Error("The `level` option should be an integer from 0 to 3");let n=stdoutColor?stdoutColor.level:0;e.level=t.level===void 0?n:t.level},chalkFactory=e=>{let t=(...e)=>e.join(` `);return applyOptions(t,e),Object.setPrototypeOf(t,createChalk.prototype),t};function createChalk(e){return chalkFactory(e)}Object.setPrototypeOf(createChalk.prototype,Function.prototype);for(let[e,t]of Object.entries(ansiStyles))styles[e]={get(){let n=createBuilder(this,createStyler(t.open,t.close,this[STYLER]),this[IS_EMPTY]);return Object.defineProperty(this,e,{value:n}),n}};styles.visible={get(){let e=createBuilder(this,this[STYLER],!0);return Object.defineProperty(this,"visible",{value:e}),e}};var getModelAnsi=(e,t,n,...r)=>e===`rgb`?t===`ansi16m`?ansiStyles[n].ansi16m(...r):t===`ansi256`?ansiStyles[n].ansi256(ansiStyles.rgbToAnsi256(...r)):ansiStyles[n].ansi(ansiStyles.rgbToAnsi(...r)):e===`hex`?getModelAnsi(`rgb`,t,n,...ansiStyles.hexToRgb(...r)):ansiStyles[n][e](...r);for(let e of[`rgb`,`hex`,`ansi256`]){styles[e]={get(){let{level:t}=this;return function(...n){let r=createStyler(getModelAnsi(e,levelMapping[t],`color`,...n),ansiStyles.color.close,this[STYLER]);return createBuilder(this,r,this[IS_EMPTY])}}};let t=`bg`+e[0].toUpperCase()+e.slice(1);styles[t]={get(){let{level:t}=this;return function(...n){let r=createStyler(getModelAnsi(e,levelMapping[t],`bgColor`,...n),ansiStyles.bgColor.close,this[STYLER]);return createBuilder(this,r,this[IS_EMPTY])}}}}var proto=Object.defineProperties(()=>{},{...styles,level:{enumerable:!0,get(){return this[GENERATOR].level},set(e){this[GENERATOR].level=e}}}),createStyler=(e,t,n)=>{let r,i;return n===void 0?(r=e,i=t):(r=n.openAll+e,i=t+n.closeAll),{open:e,close:t,openAll:r,closeAll:i,parent:n}},createBuilder=(e,t,n)=>{let r=(...e)=>applyStyle(r,e.length===1?``+e[0]:e.join(` `));return Object.setPrototypeOf(r,proto),r[GENERATOR]=e,r[STYLER]=t,r[IS_EMPTY]=n,r},applyStyle=(e,t)=>{if(e.level<=0||!t)return e[IS_EMPTY]?``:t;let n=e[STYLER];if(n===void 0)return t;let{openAll:r,closeAll:i}=n;if(t.includes(`\x1B`))for(;n!==void 0;)t=stringReplaceAll(t,n.close,n.open),n=n.parent;let a=t.indexOf(`
70
- `);return a!==-1&&(t=stringEncaseCRLFWithFirstIndex(t,i,r,a)),r+t+i};Object.defineProperties(createChalk.prototype,styles);var chalk=createChalk(),chalkStderr=createChalk({level:stderrColor?stderrColor.level:0}),debug=console.debug.bind(console,chalk.gray(`[LLM]`));function zodToOpenAITool(e,t){return{type:`function`,function:{name:e,description:t.description,parameters:toJSONSchema(t.inputSchema,{target:`openapi-3.0`})}}}function modelPatch(e,t){let n=e.model||``;if(!n)return e;let r=getProvider(t),i=normalizeModelName(n);if(i.startsWith(`qwen`)&&(debug(`Patch Qwen: disable thinking`),e.enable_thinking=!1,e.temperature===void 0&&!/max|plus/.test(i)&&(debug(`Patch Qwen: raise temperature to 1.0`),e.temperature=1)),i.startsWith(`deepseek`)&&(debug(`Patch DeepSeek: disable thinking, remove tool_choice`),e.thinking={type:`disabled`},delete e.tool_choice),i.startsWith(`gpt`)&&(i.startsWith(`gpt-5`)&&(e.verbosity=`low`,e.reasoning_effort=/^gpt-5(-|$)/.test(i)?`minimal`:`none`,debug(`Patch GPT-5: verbosity=low, reasoning_effort=${e.reasoning_effort}`)),i.includes(`chat-latest`)&&(debug(`Omitting reasoning_effort and temperature for chat-latest`),delete e.reasoning_effort,delete e.temperature)),i.startsWith(`claude`)&&(/opus|sonnet|haiku/.test(i)?(debug(`Patch Claude: disable thinking`),e.thinking={type:`disabled`},r!==`openrouter`&&(e.tool_choice===`required`?(debug(`Applying Claude patch: convert tool_choice "required" to { type: "any" }`),e.tool_choice={type:`any`}):e.tool_choice?.function?.name&&(debug(`Applying Claude patch: convert tool_choice format`),e.tool_choice={type:`tool`,name:e.tool_choice.function.name}))):(debug(`Patch Claude: reasoning_effort=low`),e.reasoning_effort=`low`,delete e.tool_choice)),i.startsWith(`gemini`)&&(debug(`Patch Gemini: reasoning_effort=low`),e.reasoning_effort=`low`,/^gemini-25(?!.*pro)/.test(i)?(debug(`Patch Gemini 2.5 non-Pro: reasoning_effort=none`),e.reasoning_effort=`none`):(i.startsWith(`gemini-35-flash`)||i.startsWith(`gemini-31-flash-lite`)||i.startsWith(`gemini-3-flash`))&&(debug(`Patch Gemini 3.x Flash/Lite: reasoning_effort=minimal`),e.reasoning_effort=`minimal`)),i.startsWith(`glm`)&&(debug(`Patch GLM: disable thinking`),e.thinking={type:`disabled`}),i.startsWith(`grok`)&&(/^grok-4-?3/.test(i)?(debug(`Patch Grok 4.3: reasoning_effort=none`),e.reasoning_effort=`none`):(i.startsWith(`grok-3-mini`)||i.startsWith(`grok-code-fast`))&&(debug(`Patch Grok mini/code: reasoning_effort=low`),e.reasoning_effort=`low`)),i.startsWith(`kimi`)&&(i.includes(`code`)||(debug(`Patch Kimi: disable thinking`),e.thinking={type:`disabled`})),i.startsWith(`minimax`)&&(debug(`Patch MiniMax: remove parallel_tool_calls`),delete e.parallel_tool_calls,i.includes(`m3`)&&(debug(`Patch MiniMax: disable thinking`),e.thinking={type:`disabled`})),r===`openrouter`){let t=e.reasoning_effort;e.thinking?.type===`disabled`||e.enable_thinking===!1||t===`none`?e.reasoning={enabled:!1}:t&&(e.reasoning={enabled:!0,effort:t})}return e}function normalizeModelName(e){let t=e.toLowerCase();return t.includes(`/`)&&(t=t.split(`/`)[1]),t=t.replace(/_/g,``),t=t.replace(/\./g,``),t}function getProvider(e){if(e)try{return new URL(e).hostname===`openrouter.ai`?`openrouter`:void 0}catch{return}}var OpenAIClient=class{config;fetch;constructor(e){this.config=e,this.fetch=e.customFetch}async invoke(e,t,n,r){n?.throwIfAborted();let i=Object.entries(t).map(([e,t])=>zodToOpenAITool(e,t)),a=`required`;r?.toolChoiceName&&!this.config.disableNamedToolChoice&&(a={type:`function`,function:{name:r.toolChoiceName}});let o={model:this.config.model,messages:e,tools:i,parallel_tool_calls:!1,tool_choice:a};this.config.temperature!==void 0&&(o.temperature=this.config.temperature),modelPatch(o,this.config.baseURL);let s;try{s=this.config.transformRequestBody(o)}catch(e){throw new InvokeError(InvokeErrorTypes.CONFIG_ERROR,`transformRequestBody failed: ${e.message}`,e)}let c=s??o,l;try{l=await this.fetch(`${this.config.baseURL}/chat/completions`,{method:`POST`,headers:{"Content-Type":`application/json`,...this.config.apiKey&&{Authorization:`Bearer ${this.config.apiKey}`}},body:JSON.stringify(c),signal:n})}catch(e){throw e?.name===`AbortError`?e:(console.error(e),new InvokeError(InvokeErrorTypes.NETWORK_ERROR,`Network request failed`,e))}if(!l.ok){let e;try{e=await l.json()}catch(e){if(e?.name===`AbortError`)throw e}let t=e?.error?.message||l.statusText;throw l.status===401||l.status===403?new InvokeError(InvokeErrorTypes.AUTH_ERROR,`Authentication failed: ${t}`,e):l.status===429?new InvokeError(InvokeErrorTypes.RATE_LIMIT,`Rate limit exceeded: ${t}`,e):l.status>=500?new InvokeError(InvokeErrorTypes.SERVER_ERROR,`Server error: ${t}`,e):new InvokeError(InvokeErrorTypes.UNKNOWN,`HTTP ${l.status}: ${t}`,e)}let u;try{u=await l.json()}catch(e){throw e?.name===`AbortError`?e:new InvokeError(InvokeErrorTypes.INVALID_RESPONSE,`Response body is not valid JSON`,e)}let d=u.choices?.[0];if(!d)throw new InvokeError(InvokeErrorTypes.INVALID_SCHEMA,`No choices in response`,u);switch(d.finish_reason){case`tool_calls`:case`function_call`:case`stop`:break;case`length`:throw new InvokeError(InvokeErrorTypes.CONTEXT_LENGTH,`Response truncated: max tokens reached`,void 0,u);case`content_filter`:throw new InvokeError(InvokeErrorTypes.CONTENT_FILTER,`Content filtered by safety system`,void 0,u);default:throw new InvokeError(InvokeErrorTypes.INVALID_SCHEMA,`Unexpected finish_reason: ${d.finish_reason}`,void 0,u)}let f=(r?.normalizeResponse?r.normalizeResponse(u):u).choices?.[0],p=f?.message?.tool_calls?.[0]?.function?.name;if(!p)throw new InvokeError(InvokeErrorTypes.NO_TOOL_CALL,`No tool call found in response`,void 0,u);let m=t[p];if(!m)throw new InvokeError(InvokeErrorTypes.UNKNOWN,`Tool "${p}" not found in tools`,void 0,u);let h=f.message?.tool_calls?.[0]?.function?.arguments;if(!h)throw new InvokeError(InvokeErrorTypes.INVALID_TOOL_ARGS,`No tool call arguments found`,void 0,u);let g;try{g=JSON.parse(h)}catch(e){throw new InvokeError(InvokeErrorTypes.INVALID_TOOL_ARGS,`Failed to parse tool arguments as JSON`,e,u)}let _=m.inputSchema.safeParse(g);if(!_.success)throw console.error(prettifyError(_.error)),new InvokeError(InvokeErrorTypes.INVALID_TOOL_ARGS,`Tool arguments validation failed`,_.error,u);let v=_.data,y;try{y=await m.execute(v)}catch(e){throw e?.name===`AbortError`?e:new InvokeError(InvokeErrorTypes.TOOL_EXECUTION_ERROR,`Tool execution failed: ${e?.message}`,e,u)}return{toolCall:{name:p,args:v},toolResult:y,usage:{promptTokens:u.usage?.prompt_tokens??0,completionTokens:u.usage?.completion_tokens??0,totalTokens:u.usage?.total_tokens??0,cachedTokens:u.usage?.prompt_tokens_details?.cached_tokens,reasoningTokens:u.usage?.completion_tokens_details?.reasoning_tokens},rawResponse:u,rawRequest:c}}},LLM=class extends EventTarget{config;client;constructor(e){super(),this.config=parseLLMConfig(e),this.client=new OpenAIClient(this.config)}async invoke(e,t,n,r){return await withRetry(async()=>this.client.invoke(e,t,n,r),{maxRetries:this.config.maxRetries,onRetry:(e,t)=>{this.dispatchEvent(new CustomEvent(`retry`,{detail:{attempt:e,maxAttempts:this.config.maxRetries,lastError:t}}))}})}};async function withRetry(e,t){let n=0;for(;;)try{return await e()}catch(e){if(e?.name===`AbortError`||e instanceof InvokeError&&!e.retryable||(n++,n>t.maxRetries))throw e;console.debug(`[LLM] retryable failure, will retry:`,e),t.onRetry(n,e),await new Promise(e=>setTimeout(e,100))}}function parseLLMConfig(e){if(!e.baseURL||!e.model)throw Error(`[PageAgent] LLM configuration required. Please provide: baseURL, model. See: https://alibaba.github.io/page-agent/docs/features/models`);return e.temperature!==void 0&&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.`),{baseURL:e.baseURL,model:e.model,apiKey:e.apiKey||``,temperature:e.temperature,maxRetries:e.maxRetries??2,transformRequestBody:e.transformRequestBody??(e=>e),disableNamedToolChoice:e.disableNamedToolChoice??!1,customFetch:(e.customFetch??fetch).bind(globalThis)}}var system_prompt_default=`You are an AI agent designed to operate in an iterative loop to automate browser tasks. Your ultimate goal is accomplishing the task provided in <user_request>.
70
+ `);return a!==-1&&(t=stringEncaseCRLFWithFirstIndex(t,i,r,a)),r+t+i};Object.defineProperties(createChalk.prototype,styles);var chalk=createChalk(),chalkStderr=createChalk({level:stderrColor?stderrColor.level:0}),debug=console.debug.bind(console,chalk.gray(`[LLM]`));function zodToOpenAITool(e,t){return{type:`function`,function:{name:e,description:t.description,parameters:toJSONSchema(t.inputSchema,{target:`openapi-3.0`})}}}function modelPatch(e,t){let n=e.model||``;if(!n)return e;let r=getProvider(t),i=normalizeModelName(n);if(i.startsWith(`qwen`)&&(debug(`Patch Qwen: disable thinking`),e.enable_thinking=!1,e.temperature===void 0&&!/max|plus/.test(i)&&(debug(`Patch Qwen: raise temperature to 1.0`),e.temperature=1)),i.startsWith(`deepseek`)&&(debug(`Patch DeepSeek: disable thinking, remove tool_choice`),e.thinking={type:`disabled`},delete e.tool_choice),i.startsWith(`gpt`)&&(i.startsWith(`gpt-5`)&&(e.verbosity=`low`),i.includes(`chat-latest`)?(debug(`Patch chat-latest: omit reasoning_effort and temperature`),delete e.reasoning_effort,delete e.temperature):/^gpt-5[12](-|$)/.test(i)?(debug(`Patch GPT-5.1/5.2: reasoning_effort=none`),e.reasoning_effort=`none`):/^gpt-5(-|$)/.test(i)?(debug(`Patch GPT-5: reasoning_effort=minimal`),e.reasoning_effort=`minimal`):(debug(`Patch GPT: omit reasoning_effort`),delete e.reasoning_effort)),i.startsWith(`claude`)&&(/opus|sonnet|haiku/.test(i)?(debug(`Patch Claude: disable thinking`),e.thinking={type:`disabled`},r!==`openrouter`&&(e.tool_choice===`required`?(debug(`Applying Claude patch: convert tool_choice "required" to { type: "any" }`),e.tool_choice={type:`any`}):e.tool_choice?.function?.name&&(debug(`Applying Claude patch: convert tool_choice format`),e.tool_choice={type:`tool`,name:e.tool_choice.function.name}))):(debug(`Patch Claude: reasoning_effort=low`),e.reasoning_effort=`low`,delete e.tool_choice)),i.startsWith(`gemini`)&&(debug(`Patch Gemini: reasoning_effort=low`),e.reasoning_effort=`low`,/^gemini-25(?!.*pro)/.test(i)?(debug(`Patch Gemini 2.5 non-Pro: reasoning_effort=none`),e.reasoning_effort=`none`):(i.startsWith(`gemini-35-flash`)||i.startsWith(`gemini-31-flash-lite`)||i.startsWith(`gemini-3-flash`))&&(debug(`Patch Gemini 3.x Flash/Lite: reasoning_effort=minimal`),e.reasoning_effort=`minimal`)),i.startsWith(`glm`)&&(debug(`Patch GLM: disable thinking`),e.thinking={type:`disabled`}),i.startsWith(`hy`)&&(debug(`Patch Hunyuan: disable thinking, reasoning_effort=low`),e.thinking={type:`disabled`},e.reasoning_effort=`low`),i.startsWith(`grok`)&&(/^grok-4-?3/.test(i)?(debug(`Patch Grok 4.3: reasoning_effort=none`),e.reasoning_effort=`none`):(i.startsWith(`grok-3-mini`)||i.startsWith(`grok-code-fast`))&&(debug(`Patch Grok mini/code: reasoning_effort=low`),e.reasoning_effort=`low`)),i.startsWith(`kimi`)&&(i.includes(`code`)||(debug(`Patch Kimi: disable thinking`),e.thinking={type:`disabled`})),i.startsWith(`minimax`)&&(debug(`Patch MiniMax: remove parallel_tool_calls`),delete e.parallel_tool_calls,i.includes(`m3`)&&(debug(`Patch MiniMax: disable thinking`),e.thinking={type:`disabled`})),r===`openrouter`){let t=e.reasoning_effort;e.thinking?.type===`disabled`||e.enable_thinking===!1||t===`none`?e.reasoning={enabled:!1}:t&&(e.reasoning={enabled:!0,effort:t})}return e}function normalizeModelName(e){let t=e.toLowerCase();return t.includes(`/`)&&(t=t.split(`/`)[1]),t=t.replace(/_/g,``),t=t.replace(/\./g,``),t}function getProvider(e){if(e)try{return new URL(e).hostname===`openrouter.ai`?`openrouter`:void 0}catch{return}}var OpenAIClient=class{config;fetch;constructor(e){this.config=e,this.fetch=e.customFetch}async invoke(e,t,n,r){n?.throwIfAborted();let i=Object.entries(t).map(([e,t])=>zodToOpenAITool(e,t)),a=`required`;r?.toolChoiceName&&!this.config.disableNamedToolChoice&&(a={type:`function`,function:{name:r.toolChoiceName}});let o={model:this.config.model,messages:e,tools:i,parallel_tool_calls:!1,tool_choice:a};this.config.temperature!==void 0&&(o.temperature=this.config.temperature),modelPatch(o,this.config.baseURL);let s;try{s=this.config.transformRequestBody(o)}catch(e){throw new InvokeError(InvokeErrorTypes.CONFIG_ERROR,`transformRequestBody failed: ${e.message}`,e)}let c=s??o,l;try{l=await this.fetch(`${this.config.baseURL}/chat/completions`,{method:`POST`,headers:{"Content-Type":`application/json`,...this.config.apiKey&&{Authorization:`Bearer ${this.config.apiKey}`}},body:JSON.stringify(c),signal:n})}catch(e){throw e?.name===`AbortError`?e:(console.error(e),new InvokeError(InvokeErrorTypes.NETWORK_ERROR,`Network request failed`,e))}if(!l.ok){let e;try{e=await l.json()}catch(e){if(e?.name===`AbortError`)throw e}let t=e?.error?.message||l.statusText;throw l.status===401||l.status===403?new InvokeError(InvokeErrorTypes.AUTH_ERROR,`Authentication failed: ${t}`,e):l.status===429?new InvokeError(InvokeErrorTypes.RATE_LIMIT,`Rate limit exceeded: ${t}`,e):l.status>=500?new InvokeError(InvokeErrorTypes.SERVER_ERROR,`Server error: ${t}`,e):new InvokeError(InvokeErrorTypes.UNKNOWN,`HTTP ${l.status}: ${t}`,e)}let u;try{u=await l.json()}catch(e){throw e?.name===`AbortError`?e:new InvokeError(InvokeErrorTypes.INVALID_RESPONSE,`Response body is not valid JSON`,e)}let d=u.choices?.[0];if(!d)throw new InvokeError(InvokeErrorTypes.INVALID_SCHEMA,`No choices in response`,u);switch(d.finish_reason){case`tool_calls`:case`function_call`:case`stop`:break;case`length`:throw new InvokeError(InvokeErrorTypes.CONTEXT_LENGTH,`Response truncated: max tokens reached`,void 0,u);case`content_filter`:throw new InvokeError(InvokeErrorTypes.CONTENT_FILTER,`Content filtered by safety system`,void 0,u);default:throw new InvokeError(InvokeErrorTypes.INVALID_SCHEMA,`Unexpected finish_reason: ${d.finish_reason}`,void 0,u)}let f=(r?.normalizeResponse?r.normalizeResponse(u):u).choices?.[0],p=f?.message?.tool_calls?.[0]?.function?.name;if(!p)throw new InvokeError(InvokeErrorTypes.NO_TOOL_CALL,`No tool call found in response`,void 0,u);let m=t[p];if(!m)throw new InvokeError(InvokeErrorTypes.UNKNOWN,`Tool "${p}" not found in tools`,void 0,u);let h=f.message?.tool_calls?.[0]?.function?.arguments;if(!h)throw new InvokeError(InvokeErrorTypes.INVALID_TOOL_ARGS,`No tool call arguments found`,void 0,u);let g;try{g=JSON.parse(h)}catch(e){throw new InvokeError(InvokeErrorTypes.INVALID_TOOL_ARGS,`Failed to parse tool arguments as JSON`,e,u)}let _=m.inputSchema.safeParse(g);if(!_.success)throw console.error(prettifyError(_.error)),new InvokeError(InvokeErrorTypes.INVALID_TOOL_ARGS,`Tool arguments validation failed`,_.error,u);let v=_.data,y;try{y=await m.execute(v)}catch(e){throw e?.name===`AbortError`?e:new InvokeError(InvokeErrorTypes.TOOL_EXECUTION_ERROR,`Tool execution failed: ${e?.message}`,e,u)}return{toolCall:{name:p,args:v},toolResult:y,usage:{promptTokens:u.usage?.prompt_tokens??0,completionTokens:u.usage?.completion_tokens??0,totalTokens:u.usage?.total_tokens??0,cachedTokens:u.usage?.prompt_tokens_details?.cached_tokens,reasoningTokens:u.usage?.completion_tokens_details?.reasoning_tokens},rawResponse:u,rawRequest:c}}},LLM=class extends EventTarget{config;client;constructor(e){super(),this.config=parseLLMConfig(e),this.client=new OpenAIClient(this.config)}async invoke(e,t,n,r){return await withRetry(async()=>this.client.invoke(e,t,n,r),{maxRetries:this.config.maxRetries,onRetry:(e,t)=>{this.dispatchEvent(new CustomEvent(`retry`,{detail:{attempt:e,maxAttempts:this.config.maxRetries,lastError:t}}))}})}};async function withRetry(e,t){let n=0;for(;;)try{return await e()}catch(e){if(e?.name===`AbortError`||e instanceof InvokeError&&!e.retryable||(n++,n>t.maxRetries))throw e;console.debug(`[LLM] retryable failure, will retry:`,e),t.onRetry(n,e),await new Promise(e=>setTimeout(e,100))}}function parseLLMConfig(e){if(!e.baseURL||!e.model)throw Error(`[PageAgent] LLM configuration required. Please provide: baseURL, model. See: https://alibaba.github.io/page-agent/docs/features/models`);return e.temperature!==void 0&&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.`),{baseURL:e.baseURL,model:e.model,apiKey:e.apiKey||``,temperature:e.temperature,maxRetries:e.maxRetries??2,transformRequestBody:e.transformRequestBody??(e=>e),disableNamedToolChoice:e.disableNamedToolChoice??!1,customFetch:(e.customFetch??fetch).bind(globalThis)}}var system_prompt_default=`You are an AI agent designed to operate in an iterative loop to automate browser tasks. Your ultimate goal is accomplishing the task provided in <user_request>.
71
71
 
72
72
  <intro>
73
73
  You excel at following tasks:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "page-agent",
3
3
  "private": false,
4
- "version": "1.11.0",
4
+ "version": "1.12.1",
5
5
  "type": "module",
6
6
  "main": "./dist/esm/page-agent.js",
7
7
  "types": "./dist/esm/PageAgent.d.ts",
@@ -43,10 +43,10 @@
43
43
  "postpublish": "node ../../scripts/post-publish.js"
44
44
  },
45
45
  "dependencies": {
46
- "@page-agent/core": "1.11.0",
47
- "@page-agent/llms": "1.11.0",
48
- "@page-agent/page-controller": "1.11.0",
49
- "@page-agent/ui": "1.11.0",
46
+ "@page-agent/core": "1.12.1",
47
+ "@page-agent/llms": "1.12.1",
48
+ "@page-agent/page-controller": "1.12.1",
49
+ "@page-agent/ui": "1.12.1",
50
50
  "chalk": "^5.6.2"
51
51
  },
52
52
  "peerDependencies": {