@coolkiller007/my-page-agent 0.2.0 → 0.2.2
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/dist/index.d.ts +15 -2
- package/dist/index.js +107 -61
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["#createCursor","#moveCursorToTarget","#cursor","#disposed","#currentCursorX","#targetCursorX","#currentCursorY","#targetCursorY","#handleStatusChange","#handleHistoryChange","#handleActivity","#askUser","#wrapper","#agent","#config","#i18n","#askUserCallback","#createWrapper","#indicator","#statusText","#historySectionWrapper","#historySection","#expandButton","#actionButton","#inputSection","#taskInput","#attachButton","#fileInput","#attachmentsBar","#attachError","#onStatusChange","#onHistoryChange","#onActivity","#onAgentDispose","#setupEventListeners","#startHeaderUpdateLoop","#showInputArea","#updateStatusIndicator","#hideInputArea","#isExpanded","#expand","#shouldShowInputArea","#renderHistory","#pendingHeaderText","#getToolExecutingText","#disposed","#createAbortError","#rejectPendingQuestion","#isWaitingForUserAnswer","#userAnswerResolver","#userAnswerRejecter","#scrollToBottom","#syncHistoryHeight","#userAnswerSignal","#userAnswerAbortHandler","#removeTempCards","#clearQuestionState","#collapse","#stopHeaderUpdateLoop","#timers","#handleUserAnswer","#pendingAttachments","#renderAttachments","#setAttachError","#syncInputHeight","#setTimer","#handleFileSelection","#isSupportedAttachment","#readFileAsDataUrl","#toggle","#handleActionButton","#submitTask","#removeAttachment","#headerUpdateTimer","#checkAndUpdateHeader","#isAnimating","#animateTextChange","#createTaskCard","#createHistoryCards","#createActionCards","#llm","#emitActivity","#emitHistoryChange","#status","#lastResult","#currentTaskHistory","#taskHistoryStart","#emitStatusChange","#observations","#abortController","#running","#taskAttachments","#states","#setStatus","#handleObservations","#getSystemPrompt","#assembleUserPrompt","#packMacroTool","#getInstructions","#injectAttachments"],"sources":["../src/browser/utils/index.ts","../src/browser/actions.ts","../src/browser/dom/dom_tree/index.js","../src/browser/dom/index.ts","../src/browser/dom/getPageInfo.ts","../src/browser/patches/react.ts","../src/browser/visual/checkDarkMode.ts","../src/browser/visual/SimulatorMask.module.css","../src/browser/visual/cursor.module.css","../src/browser/visual/SimulatorMask.ts","../src/browser/BrowserController.ts","../src/ui/i18n/locales.ts","../src/ui/i18n/index.ts","../src/ui/utils.ts","../src/ui/styles/UI.module.css","../src/ui/cards.ts","../src/ui/UI.ts","../src/llm/errors.ts","../src/shared/terminal.ts","../src/llm/utils.ts","../src/llm/OpenAICompatibleClient.ts","../src/llm/LLM.ts","../src/agent/utils/autoFixer.ts","../src/agent/utils/index.ts","../src/agent/tools/index.ts","../src/agent/AgentRuntime.ts","../src/agent/MyPageAgent.ts","../src/index.ts"],"sourcesContent":["// ======= 类型守卫 =======\n// @note 在 iframe 内的元素上使用 instanceof 会失败\n\n/** 判断值是否为 HTMLElement(type guard)。 */\nexport function isHTMLElement(el: unknown): el is HTMLElement {\n\t// @todo 要么收窄为 HTMLElement,要么在这里放宽为 Element。\n\treturn !!el && (el as Node).nodeType === 1\n}\n\n/** 判断元素是否为 <input>(type guard)。 */\nexport function isInputElement(el: Element): el is HTMLInputElement {\n\treturn el?.nodeType === 1 && el.tagName === 'INPUT'\n}\n\n/** 判断元素是否为 <textarea>(type guard)。 */\nexport function isTextAreaElement(el: Element): el is HTMLTextAreaElement {\n\treturn el?.nodeType === 1 && el.tagName === 'TEXTAREA'\n}\n\n/** 判断元素是否为 <select>(type guard)。 */\nexport function isSelectElement(el: Element): el is HTMLSelectElement {\n\treturn el?.nodeType === 1 && el.tagName === 'SELECT'\n}\n\n/** 判断元素是否为 <a>(type guard)。 */\nexport function isAnchorElement(el: Element): el is HTMLAnchorElement {\n\treturn el?.nodeType === 1 && el.tagName === 'A'\n}\n\n// ======= iframe 辅助函数 =======\n\n/** 用于将元素坐标换算为顶层视口坐标的 iframe 偏移量。 */\nexport function getIframeOffset(element: HTMLElement): { x: number; y: number } {\n\tconst frame = element.ownerDocument.defaultView?.frameElement as HTMLElement | null\n\tif (!frame) return { x: 0, y: 0 } // 无 iframe 时,x/y 偏移均为 0\n\tconst rect = frame.getBoundingClientRect()\n\treturn { x: rect.left, y: rect.top } // 返回 iframe 左上角相对视口的坐标偏移 { x, y }\n}\n\n/**\n * 从元素自身的原型上获取原生 value setter(iframe 安全)。\n * @note 用于 React\n */\nexport function getNativeValueSetter(element: HTMLInputElement | HTMLTextAreaElement) {\n\treturn Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element) as object, 'value')!\n\t\t.set as (v: string) => void\n}\n\n// ======= 通用工具函数 =======\n\n/** 等待指定的秒数。 */\nexport async function waitFor(seconds: number): Promise<void> {\n\tawait new Promise((resolve) => setTimeout(resolve, seconds * 1000))\n}\n\n// ======= 遮罩事件 =======\n\n/**\n * 将视觉指针移动到元素内的某个位置。\n * @param x - 元素所属文档视口内的 x 坐标\n * @param y - 元素所属文档视口内的 y 坐标\n */\nexport async function movePointerToElement(element: HTMLElement, x: number, y: number) {\n\tconst offset = getIframeOffset(element)\n\n\twindow.dispatchEvent(\n\t\tnew CustomEvent('PageAgent::MovePointerTo', {\n\t\t\tdetail: { x: x + offset.x, y: y + offset.y }, // 目标点在顶层视口内的坐标 { x, y }\n\t\t})\n\t)\n\n\tawait waitFor(0.3)\n}\n\n/** 触发一次模拟光标点击动画。 */\nexport async function clickPointer() {\n\twindow.dispatchEvent(new CustomEvent('PageAgent::ClickPointer'))\n}\n\n/** 开启遮罩穿透,鼠标事件不再被遮罩拦截。 */\nexport async function enablePassThrough() {\n\twindow.dispatchEvent(new CustomEvent('PageAgent::EnablePassThrough'))\n}\n\n/** 关闭遮罩穿透,遮罩重新拦截鼠标事件。 */\nexport async function disablePassThrough() {\n\twindow.dispatchEvent(new CustomEvent('PageAgent::DisablePassThrough'))\n}\n","/**\n * 版权所有 (C) 2025 Alibaba Group Holding Limited\n * 保留所有权利。\n */\nimport type { InteractiveElementDomNode } from './dom/dom_tree/type'\nimport {\n\tclickPointer,\n\tdisablePassThrough,\n\tenablePassThrough,\n\tgetNativeValueSetter,\n\tisHTMLElement,\n\tisInputElement,\n\tisSelectElement,\n\tisTextAreaElement,\n\tmovePointerToElement,\n\twaitFor,\n} from './utils'\n\n/**\n * 根据索引从 selectorMap 中获取对应的 HTMLElement。\n * @private 内部方法,随时可能变更。\n */\nexport function getElementByIndex(\n\tselectorMap: Map<number, InteractiveElementDomNode>,\n\tindex: number\n): HTMLElement {\n\tconst interactiveNode = selectorMap.get(index)\n\tif (!interactiveNode) {\n\t\tthrow new Error(`No interactive element found at index ${index}`)\n\t}\n\n\tconst element = interactiveNode.ref\n\tif (!element) {\n\t\tthrow new Error(`Element at index ${index} does not have a reference`)\n\t}\n\n\tif (!isHTMLElement(element)) {\n\t\tthrow new Error(`Element at index ${index} is not an HTMLElement`)\n\t}\n\n\treturn element\n}\n\n// 记录最近一次被点击的元素,用于下次点击前触发失焦相关事件\nlet lastClickedElement: HTMLElement | null = null\n\n/**\n * 触发上次点击元素的失焦相关事件(pointerout/pointerleave/mouseout/mouseleave/blur)并清空记录。\n */\nfunction blurLastClickedElement() {\n\tif (lastClickedElement) {\n\t\tlastClickedElement.dispatchEvent(new PointerEvent('pointerout', { bubbles: true }))\n\t\tlastClickedElement.dispatchEvent(new PointerEvent('pointerleave', { bubbles: false }))\n\t\tlastClickedElement.dispatchEvent(new MouseEvent('mouseout', { bubbles: true }))\n\t\tlastClickedElement.dispatchEvent(new MouseEvent('mouseleave', { bubbles: false }))\n\t\tlastClickedElement.blur()\n\t\tlastClickedElement = null\n\t}\n}\n\n/**\n * 模拟一次完整的点击,遵循 W3C Pointer Events + UI Events 规范顺序:\n * pointerover/enter → mouseover/enter → pointerdown → mousedown → [focus] →\n * pointerup → mouseup → click\n *\n * @private 内部方法,随时可能变更。\n */\nexport async function clickElement(element: HTMLElement) {\n\tblurLastClickedElement()\n\n\tlastClickedElement = element\n\n\tawait scrollIntoViewIfNeeded(element)\n\tconst frame = element.ownerDocument.defaultView?.frameElement\n\tif (frame) await scrollIntoViewIfNeeded(frame)\n\n\tconst rect = element.getBoundingClientRect()\n\tconst x = rect.left + rect.width / 2\n\tconst y = rect.top + rect.height / 2\n\n\tawait movePointerToElement(element, x, y)\n\tawait clickPointer()\n\n\tawait waitFor(0.05)\n\n\t// 命中测试:找出点击坐标处最深的元素,与真实浏览器行为一致——事件会命中最内层的元素\n\t// @note 这可能会命中黑名单中的元素\n\t// TODO: 这是一个临时方案,本应在 DOM 提取阶段就处理。\n\tconst doc = element.ownerDocument\n\tawait enablePassThrough()\n\tconst hitTarget = doc.elementFromPoint(x, y)\n\tawait disablePassThrough()\n\tconst target =\n\t\thitTarget instanceof HTMLElement && element.contains(hitTarget) ? hitTarget : element\n\n\tconst pointerOpts = {\n\t\tbubbles: true, // 事件是否冒泡\n\t\tcancelable: true, // 事件是否可取消\n\t\tclientX: x, // 点击点的 X 坐标(视口相对)\n\t\tclientY: y, // 点击点的 Y 坐标(视口相对)\n\t\tpointerType: 'mouse', // 指针设备类型\n\t}\n\tconst mouseOpts = {\n\t\tbubbles: true, // 事件是否冒泡\n\t\tcancelable: true, // 事件是否可取消\n\t\tclientX: x, // 点击点的 X 坐标(视口相对)\n\t\tclientY: y, // 点击点的 Y 坐标(视口相对)\n\t\tbutton: 0, // 鼠标按钮(0 表示左键)\n\t}\n\n\t// 悬停 — 先派发指针事件,再派发鼠标事件(规范顺序)\n\ttarget.dispatchEvent(new PointerEvent('pointerover', pointerOpts))\n\ttarget.dispatchEvent(new PointerEvent('pointerenter', { ...pointerOpts, bubbles: false }))\n\ttarget.dispatchEvent(new MouseEvent('mouseover', mouseOpts))\n\ttarget.dispatchEvent(new MouseEvent('mouseenter', { ...mouseOpts, bubbles: false }))\n\n\t// 按下\n\ttarget.dispatchEvent(new PointerEvent('pointerdown', pointerOpts))\n\ttarget.dispatchEvent(new MouseEvent('mousedown', mouseOpts))\n\n\t// 焦点不属于标准的指针/鼠标事件序列,\n\t// 其行为“未定义且因浏览器而异”。\n\t// 这里聚焦的是原始元素(最近的可聚焦祖先),而非命中测试目标,以匹配浏览器行为。\n\telement.focus({ preventScroll: true })\n\n\t// 释放\n\ttarget.dispatchEvent(new PointerEvent('pointerup', pointerOpts))\n\ttarget.dispatchEvent(new MouseEvent('mouseup', mouseOpts))\n\n\t// 点击 — 激活行为(导航、表单提交等)通过从目标向上冒泡到可交互的祖先元素来触发\n\ttarget.click()\n\n\tawait waitFor(0.1)\n}\n\n/**\n * 向输入类元素(input / textarea / contenteditable)输入文本,并触发相应的输入事件。\n * @private 内部方法,随时可能变更。\n */\nexport async function inputTextElement(element: HTMLElement, text: string) {\n\tconst isContentEditable = element.isContentEditable\n\tif (!isInputElement(element) && !isTextAreaElement(element) && !isContentEditable) {\n\t\tthrow new Error('Element is not an input, textarea, or contenteditable')\n\t}\n\n\tawait clickElement(element)\n\n\tif (isContentEditable) {\n\t\t// 对 contenteditable 的支持(不完整)\n\t\t// 不支持:\n\t\t// - Monaco/CodeMirror:需要直接访问其 JS 实例,没有通用的获取方式。\n\t\t// - Draft.js:对合成事件 / execCommand / Range / DataTransfer 均无响应,且已不再维护。\n\t\t//\n\t\t// 策略:先尝试方案 A(合成事件),验证失败后回退到方案 B(execCommand)再插入文本。\n\t\t//\n\t\t// 方案 A:派发合成事件\n\t\t// 适用:React contenteditable、Quill。\n\t\t// 不适用:Slate.js 以及一些忽略合成事件的 contenteditable 编辑器。\n\t\t// 事件顺序:beforeinput -> mutation -> input -> change -> blur\n\n\t\t// 派发 beforeinput + mutation + input 以清空内容\n\t\tif (\n\t\t\telement.dispatchEvent(\n\t\t\t\tnew InputEvent('beforeinput', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tcancelable: true,\n\t\t\t\t\tinputType: 'deleteContent',\n\t\t\t\t})\n\t\t\t)\n\t\t) {\n\t\t\telement.innerText = ''\n\t\t\telement.dispatchEvent(\n\t\t\t\tnew InputEvent('input', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tinputType: 'deleteContent',\n\t\t\t\t})\n\t\t\t)\n\t\t}\n\n\t\t// 派发 beforeinput + mutation + input 以插入文本(对 React 应用很重要)\n\t\tif (\n\t\t\telement.dispatchEvent(\n\t\t\t\tnew InputEvent('beforeinput', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tcancelable: true,\n\t\t\t\t\tinputType: 'insertText',\n\t\t\t\t\tdata: text,\n\t\t\t\t})\n\t\t\t)\n\t\t) {\n\t\t\telement.innerText = text\n\t\t\telement.dispatchEvent(\n\t\t\t\tnew InputEvent('input', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tinputType: 'insertText',\n\t\t\t\t\tdata: text,\n\t\t\t\t})\n\t\t\t)\n\t\t}\n\n\t\t// 通过检查文本是否真正插入来验证方案 A 是否成功\n\t\tconst planASucceeded = element.innerText.trim() === text.trim()\n\n\t\tif (!planASucceeded) {\n\t\t\t// 方案 B:execCommand 回退(已弃用但广泛支持)\n\t\t\t// 适用:Quill、Slate.js、react contenteditable 组件。\n\t\t\t// 这种方式会接入浏览器的撤销栈,并被大多数富文本编辑器原生处理。\n\t\t\telement.focus()\n\n\t\t\t// 选中全部现有内容并删除\n\t\t\tconst doc = element.ownerDocument\n\t\t\tconst selection = (doc.defaultView || window).getSelection()\n\t\t\tconst range = doc.createRange()\n\t\t\trange.selectNodeContents(element)\n\t\t\tselection?.removeAllRanges()\n\t\t\tselection?.addRange(range)\n\n\t\t\tdoc.execCommand('delete', false)\n\t\t\tdoc.execCommand('insertText', false, text)\n\t\t}\n\n\t\t// 派发 change 事件(保险起见)\n\t\telement.dispatchEvent(new Event('change', { bubbles: true }))\n\n\t\t// 触发 blur 以用于校验\n\t\telement.blur()\n\t} else {\n\t\tgetNativeValueSetter(element as HTMLInputElement | HTMLTextAreaElement).call(element, text)\n\t}\n\n\t// 仅对非 contenteditable 元素派发共享的 input 事件(contenteditable 有自己的处理)\n\tif (!isContentEditable) {\n\t\telement.dispatchEvent(new Event('input', { bubbles: true }))\n\t}\n\n\tawait waitFor(0.05)\n\n\tblurLastClickedElement()\n}\n\n/**\n * 在下拉选择框(select)中按文本匹配并选中对应选项,然后触发 change 事件。\n * @todo browser-use 版本非常复杂且支持菜单标签,需要跟进\n * @private 内部方法,随时可能变更。\n */\nexport async function selectOptionElement(selectElement: HTMLSelectElement, optionText: string) {\n\tif (!isSelectElement(selectElement)) {\n\t\tthrow new Error('Element is not a select element')\n\t}\n\n\tconst options = Array.from(selectElement.options)\n\tconst option = options.find((opt) => opt.textContent?.trim() === optionText.trim())\n\n\tif (!option) {\n\t\tthrow new Error(`Option with text \"${optionText}\" not found in select element`)\n\t}\n\n\tselectElement.value = option.value\n\tselectElement.dispatchEvent(new Event('change', { bubbles: true }))\n\n\tawait waitFor(0.05) // 等待以确保 change 事件处理完成\n}\n\ninterface ScrollableElement extends Element {\n\tscrollIntoViewIfNeeded?: (centerIfNeeded?: boolean) => void // 可选的原生方法:必要时将元素滚动到可视区域\n}\n\n/**\n * 将元素滚动到可视区域内;优先使用原生 scrollIntoViewIfNeeded,不支持时回退为 scrollIntoView。\n * @private 内部方法,随时可能变更。\n */\nexport async function scrollIntoViewIfNeeded(element: Element) {\n\tconst el = element as ScrollableElement\n\tif (typeof el.scrollIntoViewIfNeeded === 'function') {\n\t\tel.scrollIntoViewIfNeeded()\n\t\t// await waitFor(0.5) // 动画播放\n\t} else {\n\t\t// @todo 可见性检查\n\t\telement.scrollIntoView({ behavior: 'auto', block: 'center', inline: 'nearest' })\n\t\t// await waitFor(0.5) // 动画播放\n\t}\n}\n\n/**\n * 垂直滚动页面或指定元素容器。\n * @param scroll_amount - 滚动像素数(正数向下,负数向上)\n * @param element - 可选;若传入,则优先滚动该元素及其祖先中最近的可纵向滚动容器,否则滚动页面\n * @returns 滚动结果的描述消息\n */\nexport async function scrollVertically(scroll_amount: number, element?: HTMLElement | null) {\n\t// 如果提供了元素,则针对该元素进行滚动\n\tif (element) {\n\t\tconst targetElement = element\n\t\tlet currentElement = targetElement as HTMLElement | null\n\t\tlet scrollSuccess = false\n\t\tlet scrolledElement: HTMLElement | null = null\n\t\tlet scrollDelta = 0\n\t\tlet attempts = 0\n\t\tconst dy = scroll_amount\n\n\t\twhile (currentElement && attempts < 10) {\n\t\t\tconst computedStyle = window.getComputedStyle(currentElement)\n\t\t\tconst hasScrollableY =\n\t\t\t\t/(auto|scroll|overlay)/.test(computedStyle.overflowY) ||\n\t\t\t\t(computedStyle.scrollbarWidth && computedStyle.scrollbarWidth !== 'auto') ||\n\t\t\t\t(computedStyle.scrollbarGutter && computedStyle.scrollbarGutter !== 'auto')\n\t\t\tconst canScrollVertically = currentElement.scrollHeight > currentElement.clientHeight\n\n\t\t\tif (hasScrollableY && canScrollVertically) {\n\t\t\t\tconst beforeScroll = currentElement.scrollTop\n\t\t\t\tconst maxScroll = currentElement.scrollHeight - currentElement.clientHeight\n\n\t\t\t\tlet scrollAmount = dy / 3\n\n\t\t\t\tif (scrollAmount > 0) {\n\t\t\t\t\tscrollAmount = Math.min(scrollAmount, maxScroll - beforeScroll)\n\t\t\t\t} else {\n\t\t\t\t\tscrollAmount = Math.max(scrollAmount, -beforeScroll)\n\t\t\t\t}\n\n\t\t\t\tcurrentElement.scrollTop = beforeScroll + scrollAmount\n\n\t\t\t\tconst afterScroll = currentElement.scrollTop\n\t\t\t\tconst actualScrollDelta = afterScroll - beforeScroll\n\n\t\t\t\tif (Math.abs(actualScrollDelta) > 0.5) {\n\t\t\t\t\tscrollSuccess = true\n\t\t\t\t\tscrolledElement = currentElement\n\t\t\t\t\tscrollDelta = actualScrollDelta\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentElement === document.body || currentElement === document.documentElement) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcurrentElement = currentElement.parentElement\n\t\t\tattempts++\n\t\t}\n\n\t\tif (scrollSuccess) {\n\t\t\treturn `Scrolled container (${scrolledElement?.tagName}) by ${scrollDelta}px`\n\t\t} else {\n\t\t\treturn `No scrollable container found for element (${targetElement.tagName})`\n\t\t}\n\t}\n\n\t// 页级滚动(默认或回退逻辑)\n\tconst dy = scroll_amount\n\t// 判断元素高度是否足够大(至少占视口一半),用于筛选页级滚动容器\n\tconst bigEnough = (el: HTMLElement) => el.clientHeight >= window.innerHeight * 0.5\n\t// 判断元素是否为可纵向滚动的容器\n\tconst canScroll = (el: HTMLElement | null): boolean =>\n\t\tBoolean(\n\t\t\tel &&\n\t\t\t/(auto|scroll|overlay)/.test(getComputedStyle(el).overflowY) &&\n\t\t\tel.scrollHeight > el.clientHeight &&\n\t\t\tbigEnough(el)\n\t\t)\n\n\t// @deprecated 启发式容器查找。\n\t// 在多区域布局中不可靠。应引导 LLM 使用带索引的滚动以保证一致性。\n\t// TODO: 移除该回退逻辑\n\n\t// 尝试查找最近的滚动容器\n\t// document.activeElement 通常是 body。\n\t// 在 element.focus() 成功之后,activeElement 变为最近的可聚焦父元素\n\n\tlet el: HTMLElement | null = document.activeElement as HTMLElement | null\n\twhile (el && !canScroll(el) && el !== document.body) el = el.parentElement\n\n\t// 如果回退到全局 '*' 搜索,说明有问题\n\t// TODO: 返回错误消息而不是全局 '*' 搜索\n\n\tel = canScroll(el)\n\t\t? el\n\t\t: Array.from(document.querySelectorAll<HTMLElement>('*')).find(canScroll) ||\n\t\t\t(document.scrollingElement as HTMLElement) ||\n\t\t\t(document.documentElement as HTMLElement)\n\n\tif (el === document.scrollingElement || el === document.documentElement || el === document.body) {\n\t\t// 页级滚动\n\t\tconst scrollBefore = window.scrollY\n\t\tconst scrollMax = document.documentElement.scrollHeight - window.innerHeight\n\n\t\twindow.scrollBy(0, dy)\n\n\t\tconst scrollAfter = window.scrollY\n\t\tconst scrolled = scrollAfter - scrollBefore\n\n\t\tif (Math.abs(scrolled) < 1) {\n\t\t\treturn dy > 0\n\t\t\t\t? `⚠️ Already at the bottom of the page, cannot scroll down further.`\n\t\t\t\t: `⚠️ Already at the top of the page, cannot scroll up further.`\n\t\t}\n\n\t\tconst reachedBottom = dy > 0 && scrollAfter >= scrollMax - 1\n\t\tconst reachedTop = dy < 0 && scrollAfter <= 1\n\n\t\tif (reachedBottom) return `✅ Scrolled page by ${scrolled}px. Reached the bottom of the page.`\n\t\tif (reachedTop) return `✅ Scrolled page by ${scrolled}px. Reached the top of the page.`\n\t\treturn `✅ Scrolled page by ${scrolled}px.`\n\t} else {\n\t\t// 容器滚动\n\n\t\tconst warningMsg = `The document is not scrollable. Falling back to container scroll.`\n\t\tconsole.log(`[BrowserController] ${warningMsg}`)\n\n\t\tconst scrollBefore = el!.scrollTop\n\t\tconst scrollMax = el!.scrollHeight - el!.clientHeight\n\n\t\tel!.scrollBy({ top: dy, behavior: 'smooth' })\n\t\tawait waitFor(0.1)\n\n\t\tconst scrollAfter = el!.scrollTop\n\t\tconst scrolled = scrollAfter - scrollBefore\n\n\t\tif (Math.abs(scrolled) < 1) {\n\t\t\treturn dy > 0\n\t\t\t\t? `⚠️ ${warningMsg} Already at the bottom of container (${el!.tagName}), cannot scroll down further.`\n\t\t\t\t: `⚠️ ${warningMsg} Already at the top of container (${el!.tagName}), cannot scroll up further.`\n\t\t}\n\n\t\tconst reachedBottom = dy > 0 && scrollAfter >= scrollMax - 1\n\t\tconst reachedTop = dy < 0 && scrollAfter <= 1\n\n\t\tif (reachedBottom)\n\t\t\treturn `✅ ${warningMsg} Scrolled container (${el!.tagName}) by ${scrolled}px. Reached the bottom.`\n\t\tif (reachedTop)\n\t\t\treturn `✅ ${warningMsg} Scrolled container (${el!.tagName}) by ${scrolled}px. Reached the top.`\n\t\treturn `✅ ${warningMsg} Scrolled container (${el!.tagName}) by ${scrolled}px.`\n\t}\n}\n\n/**\n * 水平滚动页面或指定元素容器。\n * @param scroll_amount - 滚动像素数(正数向右,负数向左)\n * @param element - 可选;若传入,则优先滚动该元素及其祖先中最近的可横向滚动容器,否则滚动页面\n * @returns 滚动结果的描述消息\n */\nexport async function scrollHorizontally(scroll_amount: number, element?: HTMLElement | null) {\n\t// 如果提供了元素,则针对该元素进行滚动\n\tif (element) {\n\t\tconst targetElement = element\n\t\tlet currentElement = targetElement as HTMLElement | null\n\t\tlet scrollSuccess = false\n\t\tlet scrolledElement: HTMLElement | null = null\n\t\tlet scrollDelta = 0\n\t\tlet attempts = 0\n\t\tconst dx = scroll_amount\n\n\t\twhile (currentElement && attempts < 10) {\n\t\t\tconst computedStyle = window.getComputedStyle(currentElement)\n\t\t\tconst hasScrollableX =\n\t\t\t\t/(auto|scroll|overlay)/.test(computedStyle.overflowX) ||\n\t\t\t\t(computedStyle.scrollbarWidth && computedStyle.scrollbarWidth !== 'auto') ||\n\t\t\t\t(computedStyle.scrollbarGutter && computedStyle.scrollbarGutter !== 'auto')\n\t\t\tconst canScrollHorizontally = currentElement.scrollWidth > currentElement.clientWidth\n\n\t\t\tif (hasScrollableX && canScrollHorizontally) {\n\t\t\t\tconst beforeScroll = currentElement.scrollLeft\n\t\t\t\tconst maxScroll = currentElement.scrollWidth - currentElement.clientWidth\n\n\t\t\t\tlet scrollAmount = dx / 3\n\n\t\t\t\tif (scrollAmount > 0) {\n\t\t\t\t\tscrollAmount = Math.min(scrollAmount, maxScroll - beforeScroll)\n\t\t\t\t} else {\n\t\t\t\t\tscrollAmount = Math.max(scrollAmount, -beforeScroll)\n\t\t\t\t}\n\n\t\t\t\tcurrentElement.scrollLeft = beforeScroll + scrollAmount\n\n\t\t\t\tconst afterScroll = currentElement.scrollLeft\n\t\t\t\tconst actualScrollDelta = afterScroll - beforeScroll\n\n\t\t\t\tif (Math.abs(actualScrollDelta) > 0.5) {\n\t\t\t\t\tscrollSuccess = true\n\t\t\t\t\tscrolledElement = currentElement\n\t\t\t\t\tscrollDelta = actualScrollDelta\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentElement === document.body || currentElement === document.documentElement) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcurrentElement = currentElement.parentElement\n\t\t\tattempts++\n\t\t}\n\n\t\tif (scrollSuccess) {\n\t\t\treturn `Scrolled container (${scrolledElement?.tagName}) horizontally by ${scrollDelta}px`\n\t\t} else {\n\t\t\treturn `No horizontally scrollable container found for element (${targetElement.tagName})`\n\t\t}\n\t}\n\n\t// 页级滚动(默认或回退逻辑)\n\n\tconst dx = scroll_amount\n\n\t// 判断元素宽度是否足够大(至少占视口一半),用于筛选页级滚动容器\n\tconst bigEnough = (el: HTMLElement) => el.clientWidth >= window.innerWidth * 0.5\n\t// 判断元素是否为可横向滚动的容器\n\tconst canScroll = (el: HTMLElement | null): boolean =>\n\t\tBoolean(\n\t\t\tel &&\n\t\t\t/(auto|scroll|overlay)/.test(getComputedStyle(el).overflowX) &&\n\t\t\tel.scrollWidth > el.clientWidth &&\n\t\t\tbigEnough(el)\n\t\t)\n\n\t// @deprecated 与 scrollVertically 相同的启发式容器查找。\n\t// TODO: 一旦 LLM 可靠地通过 data-scrollable 使用带索引的滚动,就移除此处逻辑。\n\n\tlet el: HTMLElement | null = document.activeElement as HTMLElement | null\n\twhile (el && !canScroll(el) && el !== document.body) el = el.parentElement\n\n\tel = canScroll(el)\n\t\t? el\n\t\t: Array.from(document.querySelectorAll<HTMLElement>('*')).find(canScroll) ||\n\t\t\t(document.scrollingElement as HTMLElement) ||\n\t\t\t(document.documentElement as HTMLElement)\n\n\tif (el === document.scrollingElement || el === document.documentElement || el === document.body) {\n\t\t// 页级滚动\n\t\tconst scrollBefore = window.scrollX\n\t\tconst scrollMax = document.documentElement.scrollWidth - window.innerWidth\n\n\t\twindow.scrollBy(dx, 0)\n\n\t\tconst scrollAfter = window.scrollX\n\t\tconst scrolled = scrollAfter - scrollBefore\n\n\t\tif (Math.abs(scrolled) < 1) {\n\t\t\treturn dx > 0\n\t\t\t\t? `⚠️ Already at the right edge of the page, cannot scroll right further.`\n\t\t\t\t: `⚠️ Already at the left edge of the page, cannot scroll left further.`\n\t\t}\n\n\t\tconst reachedRight = dx > 0 && scrollAfter >= scrollMax - 1\n\t\tconst reachedLeft = dx < 0 && scrollAfter <= 1\n\n\t\tif (reachedRight)\n\t\t\treturn `✅ Scrolled page by ${scrolled}px. Reached the right edge of the page.`\n\t\tif (reachedLeft) return `✅ Scrolled page by ${scrolled}px. Reached the left edge of the page.`\n\t\treturn `✅ Scrolled page horizontally by ${scrolled}px.`\n\t} else {\n\t\t// 容器滚动\n\t\tconst warningMsg = `The document is not scrollable. Falling back to container scroll.`\n\t\tconsole.log(`[BrowserController] ${warningMsg}`)\n\n\t\tconst scrollBefore = el!.scrollLeft\n\t\tconst scrollMax = el!.scrollWidth - el!.clientWidth\n\n\t\tel!.scrollBy({ left: dx, behavior: 'smooth' })\n\t\tawait waitFor(0.1)\n\n\t\tconst scrollAfter = el!.scrollLeft\n\t\tconst scrolled = scrollAfter - scrollBefore\n\n\t\tif (Math.abs(scrolled) < 1) {\n\t\t\treturn dx > 0\n\t\t\t\t? `⚠️ ${warningMsg} Already at the right edge of container (${el!.tagName}), cannot scroll right further.`\n\t\t\t\t: `⚠️ ${warningMsg} Already at the left edge of container (${el!.tagName}), cannot scroll left further.`\n\t\t}\n\n\t\tconst reachedRight = dx > 0 && scrollAfter >= scrollMax - 1\n\t\tconst reachedLeft = dx < 0 && scrollAfter <= 1\n\n\t\tif (reachedRight)\n\t\t\treturn `✅ ${warningMsg} Scrolled container (${el!.tagName}) by ${scrolled}px. Reached the right edge.`\n\t\tif (reachedLeft)\n\t\t\treturn `✅ ${warningMsg} Scrolled container (${el!.tagName}) by ${scrolled}px. Reached the left edge.`\n\t\treturn `✅ ${warningMsg} Scrolled container (${el!.tagName}) horizontally by ${scrolled}px.`\n\t}\n}\n","/**\n * @file port from browser-use\n * @see https://github.com/browser-use/browser-use/commits/main/browser_use/dom/dom_tree/index.js\n * @match 0.5.9 d51b6e73daff7165fdd3e44debd667e7f5f7fdc5\n *\n * search @edit for all the changed lines.\n *\n * @edit export\n * @edit add interactiveBlacklist interactiveWhitelist\n * @edit adjustable opacity\n * @edit direct dom ref\n * @edit @workaround input.checked\n * @edit smaller zIndex for highlight\n * @edit no need for xpath\n * @edit add `extra` field for extra data\n * @edit scrollable element detection\n * @edit add `data-browser-use-ignore` attribute\n * @edit improve `sampleRect`, filter out rects with 0 area\n * @edit exclude aria-hidden elements\n * @edit make sure attributes exist for interactive candidates.\n * @edit fix \"aria-*\" attributes check\n */\n\n/* eslint-disable @typescript-eslint/no-unused-vars, no-undef, no-useless-assignment */\n\nexport default (\n\targs = {\n\t\tdoHighlightElements: true,\n\t\tfocusHighlightIndex: -1,\n\t\tviewportExpansion: 0,\n\t\tdebugMode: false,\n\n\t\t/**\n\t\t * @edit\n\t\t */\n\t\t/** @type {Element[]} */\n\t\tinteractiveBlacklist: [],\n\t\t/** @type {Element[]} */\n\t\tinteractiveWhitelist: [],\n\t\thighlightOpacity: 0.1,\n\t\thighlightLabelOpacity: 0.5,\n\t}\n) => {\n\t/**\n\t * @edit\n\t */\n\tconst { interactiveBlacklist, interactiveWhitelist, highlightOpacity, highlightLabelOpacity } =\n\t\targs\n\n\tconst { doHighlightElements, focusHighlightIndex, viewportExpansion, debugMode } = args\n\tlet highlightIndex = 0 // Reset highlight index\n\n\t/**\n\t * @edit add `extra` field for extra data\n\t */\n\tconst extraData = new WeakMap()\n\tfunction addExtraData(element, data) {\n\t\tif (!element || element.nodeType !== Node.ELEMENT_NODE) return\n\t\textraData.set(element, { ...extraData.get(element), ...data })\n\t}\n\n\t// Add caching mechanisms at the top level\n\tconst DOM_CACHE = {\n\t\tboundingRects: new WeakMap(),\n\t\tclientRects: new WeakMap(),\n\t\tcomputedStyles: new WeakMap(),\n\t\tclearCache: () => {\n\t\t\tDOM_CACHE.boundingRects = new WeakMap()\n\t\t\tDOM_CACHE.clientRects = new WeakMap()\n\t\t\tDOM_CACHE.computedStyles = new WeakMap()\n\t\t},\n\t}\n\n\t/**\n\t * Gets the cached bounding rect for an element.\n\t *\n\t * @param {HTMLElement} element - The element to get the bounding rect for.\n\t * @returns {DOMRect | null} The cached bounding rect, or null if the element is not found.\n\t */\n\tfunction getCachedBoundingRect(element) {\n\t\tif (!element) return null\n\n\t\tif (DOM_CACHE.boundingRects.has(element)) {\n\t\t\treturn DOM_CACHE.boundingRects.get(element)\n\t\t}\n\n\t\tconst rect = element.getBoundingClientRect()\n\n\t\tif (rect) {\n\t\t\tDOM_CACHE.boundingRects.set(element, rect)\n\t\t}\n\t\treturn rect\n\t}\n\n\t/**\n\t * Gets the cached computed style for an element.\n\t *\n\t * @param {HTMLElement} element - The element to get the computed style for.\n\t * @returns {CSSStyleDeclaration | null} The cached computed style, or null if the element is not found.\n\t */\n\tfunction getCachedComputedStyle(element) {\n\t\tif (!element) return null\n\n\t\tif (DOM_CACHE.computedStyles.has(element)) {\n\t\t\treturn DOM_CACHE.computedStyles.get(element)\n\t\t}\n\n\t\tconst style = window.getComputedStyle(element)\n\n\t\tif (style) {\n\t\t\tDOM_CACHE.computedStyles.set(element, style)\n\t\t}\n\t\treturn style\n\t}\n\n\t/**\n\t * Gets the cached client rects for an element.\n\t *\n\t * @param {HTMLElement} element - The element to get the client rects for.\n\t * @returns {DOMRectList | null} The cached client rects, or null if the element is not found.\n\t */\n\tfunction getCachedClientRects(element) {\n\t\tif (!element) return null\n\n\t\tif (DOM_CACHE.clientRects.has(element)) {\n\t\t\treturn DOM_CACHE.clientRects.get(element)\n\t\t}\n\n\t\tconst rects = element.getClientRects()\n\n\t\tif (rects) {\n\t\t\tDOM_CACHE.clientRects.set(element, rects)\n\t\t}\n\t\treturn rects\n\t}\n\n\t/**\n\t * Hash map of DOM nodes indexed by their highlight index.\n\t *\n\t * @type {Object<string, any>}\n\t */\n\tconst DOM_HASH_MAP = {}\n\n\tconst ID = { current: 0 }\n\n\tconst HIGHLIGHT_CONTAINER_ID = 'playwright-highlight-container'\n\n\t// Add a WeakMap cache for XPath strings\n\tconst xpathCache = new WeakMap()\n\n\t// // Initialize once and reuse\n\t// const viewportObserver = new IntersectionObserver(\n\t// (entries) => {\n\t// entries.forEach(entry => {\n\t// elementVisibilityMap.set(entry.target, entry.isIntersecting);\n\t// });\n\t// },\n\t// { rootMargin: `${viewportExpansion}px` }\n\t// );\n\n\t/**\n\t * Highlights an element in the DOM and returns the index of the next element.\n\t *\n\t * @param {HTMLElement} element - The element to highlight.\n\t * @param {number} index - The index of the element.\n\t * @param {HTMLElement | null} parentIframe - The parent iframe node.\n\t * @returns {number} The index of the next element.\n\t */\n\tfunction highlightElement(element, index, parentIframe = null) {\n\t\tif (!element) return index\n\n\t\tconst overlays = []\n\t\t/**\n\t\t * @type {HTMLElement | null}\n\t\t */\n\t\tlet label = null\n\t\tlet labelWidth = 20\n\t\tlet labelHeight = 16\n\t\tlet cleanupFn = null\n\n\t\ttry {\n\t\t\t// Create or get highlight container\n\t\t\tlet container = document.getElementById(HIGHLIGHT_CONTAINER_ID)\n\t\t\tif (!container) {\n\t\t\t\tcontainer = document.createElement('div')\n\t\t\t\tcontainer.id = HIGHLIGHT_CONTAINER_ID\n\t\t\t\tcontainer.style.position = 'fixed'\n\t\t\t\tcontainer.style.pointerEvents = 'none'\n\t\t\t\tcontainer.style.top = '0'\n\t\t\t\tcontainer.style.left = '0'\n\t\t\t\tcontainer.style.width = '100%'\n\t\t\t\tcontainer.style.height = '100%'\n\n\t\t\t\t/**\n\t\t\t\t * @edit smaller zIndex for highlight\n\t\t\t\t */\n\t\t\t\t// Use the maximum valid value in zIndex to ensure the element is not blocked by overlapping elements.\n\t\t\t\t// container.style.zIndex = \"2147483647\";\n\t\t\t\tcontainer.style.zIndex = '2147483640'\n\n\t\t\t\tcontainer.style.backgroundColor = 'transparent'\n\t\t\t\tdocument.body.appendChild(container)\n\t\t\t}\n\n\t\t\t// Get element client rects\n\t\t\tconst rects = element.getClientRects() // Use getClientRects()\n\n\t\t\tif (!rects || rects.length === 0) return index // Exit if no rects\n\n\t\t\t// Generate a color based on the index\n\t\t\tconst colors = [\n\t\t\t\t'#FF0000',\n\t\t\t\t'#00FF00',\n\t\t\t\t'#0000FF',\n\t\t\t\t'#FFA500',\n\t\t\t\t'#800080',\n\t\t\t\t'#008080',\n\t\t\t\t'#FF69B4',\n\t\t\t\t'#4B0082',\n\t\t\t\t'#FF4500',\n\t\t\t\t'#2E8B57',\n\t\t\t\t'#DC143C',\n\t\t\t\t'#4682B4',\n\t\t\t]\n\t\t\tconst colorIndex = index % colors.length\n\t\t\tlet baseColor = colors[colorIndex]\n\n\t\t\t/**\n\t\t\t * @edit adjustable opacity\n\t\t\t */\n\t\t\t// const backgroundColor = baseColor + \"1A\"; // 10% opacity version of the color\n\t\t\tconst backgroundColor =\n\t\t\t\tbaseColor +\n\t\t\t\tMath.floor(highlightOpacity * 255)\n\t\t\t\t\t.toString(16)\n\t\t\t\t\t.padStart(2, '0')\n\t\t\tbaseColor =\n\t\t\t\tbaseColor +\n\t\t\t\tMath.floor(highlightLabelOpacity * 255)\n\t\t\t\t\t.toString(16)\n\t\t\t\t\t.padStart(2, '0')\n\n\t\t\t// Get iframe offset if necessary\n\t\t\tlet iframeOffset = { x: 0, y: 0 }\n\t\t\tif (parentIframe) {\n\t\t\t\tconst iframeRect = parentIframe.getBoundingClientRect() // Keep getBoundingClientRect for iframe offset\n\t\t\t\tiframeOffset.x = iframeRect.left\n\t\t\t\tiframeOffset.y = iframeRect.top\n\t\t\t}\n\n\t\t\t// Create fragment to hold overlay elements\n\t\t\tconst fragment = document.createDocumentFragment()\n\n\t\t\t// Create highlight overlays for each client rect\n\t\t\tfor (const rect of rects) {\n\t\t\t\tif (rect.width === 0 || rect.height === 0) continue // Skip empty rects\n\n\t\t\t\tconst overlay = document.createElement('div')\n\t\t\t\toverlay.style.position = 'fixed'\n\t\t\t\toverlay.style.border = `2px solid ${baseColor}`\n\t\t\t\toverlay.style.backgroundColor = backgroundColor\n\t\t\t\toverlay.style.pointerEvents = 'none'\n\t\t\t\toverlay.style.boxSizing = 'border-box'\n\n\t\t\t\tconst top = rect.top + iframeOffset.y\n\t\t\t\tconst left = rect.left + iframeOffset.x\n\n\t\t\t\toverlay.style.top = `${top}px`\n\t\t\t\toverlay.style.left = `${left}px`\n\t\t\t\toverlay.style.width = `${rect.width}px`\n\t\t\t\toverlay.style.height = `${rect.height}px`\n\n\t\t\t\tfragment.appendChild(overlay)\n\t\t\t\toverlays.push({ element: overlay, initialRect: rect }) // Store overlay and its rect\n\t\t\t}\n\n\t\t\t// Create and position a single label relative to the first rect\n\t\t\tconst firstRect = rects[0]\n\t\t\tlabel = document.createElement('div')\n\t\t\tlabel.className = 'playwright-highlight-label'\n\t\t\tlabel.style.position = 'fixed'\n\t\t\tlabel.style.background = baseColor\n\t\t\tlabel.style.color = 'white'\n\t\t\tlabel.style.padding = '1px 4px'\n\t\t\tlabel.style.borderRadius = '4px'\n\t\t\tlabel.style.fontSize = `${Math.min(12, Math.max(8, firstRect.height / 2))}px`\n\t\t\tlabel.textContent = index.toString()\n\n\t\t\tlabelWidth = label.offsetWidth > 0 ? label.offsetWidth : labelWidth // Update actual width if possible\n\t\t\tlabelHeight = label.offsetHeight > 0 ? label.offsetHeight : labelHeight // Update actual height if possible\n\n\t\t\tconst firstRectTop = firstRect.top + iframeOffset.y\n\t\t\tconst firstRectLeft = firstRect.left + iframeOffset.x\n\n\t\t\tlet labelTop = firstRectTop + 2\n\t\t\tlet labelLeft = firstRectLeft + firstRect.width - labelWidth - 2\n\n\t\t\t// Adjust label position if first rect is too small\n\t\t\tif (firstRect.width < labelWidth + 4 || firstRect.height < labelHeight + 4) {\n\t\t\t\tlabelTop = firstRectTop - labelHeight - 2\n\t\t\t\tlabelLeft = firstRectLeft + firstRect.width - labelWidth // Align with right edge\n\t\t\t\tif (labelLeft < iframeOffset.x) labelLeft = firstRectLeft // Prevent going off-left\n\t\t\t}\n\n\t\t\t// Ensure label stays within viewport bounds slightly better\n\t\t\tlabelTop = Math.max(0, Math.min(labelTop, window.innerHeight - labelHeight))\n\t\t\tlabelLeft = Math.max(0, Math.min(labelLeft, window.innerWidth - labelWidth))\n\n\t\t\tlabel.style.top = `${labelTop}px`\n\t\t\tlabel.style.left = `${labelLeft}px`\n\n\t\t\tfragment.appendChild(label)\n\n\t\t\t// Update positions on scroll/resize\n\t\t\tconst updatePositions = () => {\n\t\t\t\tconst newRects = element.getClientRects() // Get fresh rects\n\t\t\t\tlet newIframeOffset = { x: 0, y: 0 }\n\n\t\t\t\tif (parentIframe) {\n\t\t\t\t\tconst iframeRect = parentIframe.getBoundingClientRect() // Keep getBoundingClientRect for iframe\n\t\t\t\t\tnewIframeOffset.x = iframeRect.left\n\t\t\t\t\tnewIframeOffset.y = iframeRect.top\n\t\t\t\t}\n\n\t\t\t\t// Update each overlay\n\t\t\t\toverlays.forEach((overlayData, i) => {\n\t\t\t\t\tif (i < newRects.length) {\n\t\t\t\t\t\t// Check if rect still exists\n\t\t\t\t\t\tconst newRect = newRects[i]\n\t\t\t\t\t\tconst newTop = newRect.top + newIframeOffset.y\n\t\t\t\t\t\tconst newLeft = newRect.left + newIframeOffset.x\n\n\t\t\t\t\t\toverlayData.element.style.top = `${newTop}px`\n\t\t\t\t\t\toverlayData.element.style.left = `${newLeft}px`\n\t\t\t\t\t\toverlayData.element.style.width = `${newRect.width}px`\n\t\t\t\t\t\toverlayData.element.style.height = `${newRect.height}px`\n\t\t\t\t\t\toverlayData.element.style.display =\n\t\t\t\t\t\t\tnewRect.width === 0 || newRect.height === 0 ? 'none' : 'block'\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If fewer rects now, hide extra overlays\n\t\t\t\t\t\toverlayData.element.style.display = 'none'\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\t// If there are fewer new rects than overlays, hide the extras\n\t\t\t\tif (newRects.length < overlays.length) {\n\t\t\t\t\tfor (let i = newRects.length; i < overlays.length; i++) {\n\t\t\t\t\t\toverlays[i].element.style.display = 'none'\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Update label position based on the first new rect\n\t\t\t\tif (label && newRects.length > 0) {\n\t\t\t\t\tconst firstNewRect = newRects[0]\n\t\t\t\t\tconst firstNewRectTop = firstNewRect.top + newIframeOffset.y\n\t\t\t\t\tconst firstNewRectLeft = firstNewRect.left + newIframeOffset.x\n\n\t\t\t\t\tlet newLabelTop = firstNewRectTop + 2\n\t\t\t\t\tlet newLabelLeft = firstNewRectLeft + firstNewRect.width - labelWidth - 2\n\n\t\t\t\t\tif (firstNewRect.width < labelWidth + 4 || firstNewRect.height < labelHeight + 4) {\n\t\t\t\t\t\tnewLabelTop = firstNewRectTop - labelHeight - 2\n\t\t\t\t\t\tnewLabelLeft = firstNewRectLeft + firstNewRect.width - labelWidth\n\t\t\t\t\t\tif (newLabelLeft < newIframeOffset.x) newLabelLeft = firstNewRectLeft\n\t\t\t\t\t}\n\n\t\t\t\t\t// Ensure label stays within viewport bounds\n\t\t\t\t\tnewLabelTop = Math.max(0, Math.min(newLabelTop, window.innerHeight - labelHeight))\n\t\t\t\t\tnewLabelLeft = Math.max(0, Math.min(newLabelLeft, window.innerWidth - labelWidth))\n\n\t\t\t\t\tlabel.style.top = `${newLabelTop}px`\n\t\t\t\t\tlabel.style.left = `${newLabelLeft}px`\n\t\t\t\t\tlabel.style.display = 'block'\n\t\t\t\t} else if (label) {\n\t\t\t\t\t// Hide label if element has no rects anymore\n\t\t\t\t\tlabel.style.display = 'none'\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst throttleFunction = (func, delay) => {\n\t\t\t\tlet lastCall = 0\n\t\t\t\treturn (...args) => {\n\t\t\t\t\tconst now = performance.now()\n\t\t\t\t\tif (now - lastCall < delay) return\n\t\t\t\t\tlastCall = now\n\t\t\t\t\treturn func(...args)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst throttledUpdatePositions = throttleFunction(updatePositions, 16) // ~60fps\n\t\t\twindow.addEventListener('scroll', throttledUpdatePositions, true)\n\t\t\twindow.addEventListener('resize', throttledUpdatePositions)\n\n\t\t\t// Add cleanup function\n\t\t\tcleanupFn = () => {\n\t\t\t\twindow.removeEventListener('scroll', throttledUpdatePositions, true)\n\t\t\t\twindow.removeEventListener('resize', throttledUpdatePositions)\n\t\t\t\t// Remove overlay elements if needed\n\t\t\t\toverlays.forEach((overlay) => overlay.element.remove())\n\t\t\t\tif (label) label.remove()\n\t\t\t}\n\n\t\t\t// Then add fragment to container in one operation\n\t\t\tcontainer.appendChild(fragment)\n\n\t\t\treturn index + 1\n\t\t} finally {\n\t\t\t// Store cleanup function for later use\n\t\t\tif (cleanupFn) {\n\t\t\t\t// Keep a reference to cleanup functions in a global array\n\t\t\t\t;(window._highlightCleanupFunctions = window._highlightCleanupFunctions || []).push(\n\t\t\t\t\tcleanupFn\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t// // Add this function to perform cleanup when needed\n\t// function cleanupHighlights() {\n\t// if (window._highlightCleanupFunctions && window._highlightCleanupFunctions.length) {\n\t// window._highlightCleanupFunctions.forEach(fn => fn());\n\t// window._highlightCleanupFunctions = [];\n\t// }\n\n\t// // Also remove the container\n\t// const container = document.getElementById(HIGHLIGHT_CONTAINER_ID);\n\t// if (container) container.remove();\n\t// }\n\n\t/**\n\t * Gets the position of an element in its parent.\n\t *\n\t * @param {HTMLElement} currentElement - The element to get the position for.\n\t * @returns {number} The position of the element in its parent.\n\t */\n\tfunction getElementPosition(currentElement) {\n\t\tif (!currentElement.parentElement) {\n\t\t\treturn 0 // No parent means no siblings\n\t\t}\n\n\t\tconst tagName = currentElement.nodeName.toLowerCase()\n\n\t\tconst siblings = Array.from(currentElement.parentElement.children).filter(\n\t\t\t(sib) => sib.nodeName.toLowerCase() === tagName\n\t\t)\n\n\t\tif (siblings.length === 1) {\n\t\t\treturn 0 // Only element of its type\n\t\t}\n\n\t\tconst index = siblings.indexOf(currentElement) + 1 // 1-based index\n\t\treturn index\n\t}\n\n\tfunction getXPathTree(element, stopAtBoundary = true) {\n\t\tif (xpathCache.has(element)) return xpathCache.get(element)\n\n\t\tconst segments = []\n\t\tlet currentElement = element\n\n\t\twhile (currentElement && currentElement.nodeType === Node.ELEMENT_NODE) {\n\t\t\t// Stop if we hit a shadow root or iframe\n\t\t\tif (\n\t\t\t\tstopAtBoundary &&\n\t\t\t\t(currentElement.parentNode instanceof ShadowRoot ||\n\t\t\t\t\tcurrentElement.parentNode instanceof HTMLIFrameElement)\n\t\t\t) {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tconst position = getElementPosition(currentElement)\n\t\t\tconst tagName = currentElement.nodeName.toLowerCase()\n\t\t\tconst xpathIndex = position > 0 ? `[${position}]` : ''\n\t\t\tsegments.unshift(`${tagName}${xpathIndex}`)\n\n\t\t\tcurrentElement = currentElement.parentNode\n\t\t}\n\n\t\tconst result = segments.join('/')\n\t\txpathCache.set(element, result)\n\t\treturn result\n\t}\n\n\t/**\n\t * @edit scrollable element detection\n\t * Checks if an element is scrollable. if so, return the scrollable distance on each direction (left right top bottom). if not return null.\n\t * @note distance smaller than 4 will be considered as not scrollable.\n\t * @note only check block elements, not inline elements.\n\t */\n\tfunction isScrollableElement(element) {\n\t\tif (!element || element.nodeType !== Node.ELEMENT_NODE) {\n\t\t\treturn null // Not a valid element\n\t\t}\n\n\t\tconst style = getCachedComputedStyle(element)\n\t\tif (!style) return null\n\n\t\t// Check if the element is a block-level element\n\t\tconst display = style.display\n\t\tif (display === 'inline' || display === 'inline-block') {\n\t\t\treturn null // Not a block-level element\n\t\t}\n\n\t\t// Check overflow properties\n\t\tconst overflowX = style.overflowX\n\t\tconst overflowY = style.overflowY\n\n\t\t// scrollbar-width/scrollbar-gutter are only set on elements designed to scroll;\n\t\t// their presence signals scroll intent even when overflow is hidden (e.g. overflow: auto on :hover)\n\t\tconst hasScrollbarSignal =\n\t\t\t(style.scrollbarWidth && style.scrollbarWidth !== 'auto') ||\n\t\t\t(style.scrollbarGutter && style.scrollbarGutter !== 'auto')\n\n\t\tconst scrollableX = overflowX === 'auto' || overflowX === 'scroll'\n\t\tconst scrollableY = overflowY === 'auto' || overflowY === 'scroll'\n\n\t\tif (!scrollableX && !scrollableY && !hasScrollbarSignal) {\n\t\t\treturn null // Not scrollable in any direction\n\t\t}\n\n\t\tconst scrollWidth = element.scrollWidth - element.clientWidth\n\t\tconst scrollHeight = element.scrollHeight - element.clientHeight\n\n\t\t// Consider small distances as not scrollable\n\t\tconst threshold = 4\n\n\t\tif (scrollWidth < threshold && scrollHeight < threshold) {\n\t\t\treturn null // Not scrollable\n\t\t}\n\n\t\tif (!scrollableY && !hasScrollbarSignal && scrollWidth < threshold) {\n\t\t\treturn null // Not scrollable horizontally\n\t\t}\n\n\t\tif (!scrollableX && !hasScrollbarSignal && scrollHeight < threshold) {\n\t\t\treturn null // Not scrollable vertically\n\t\t}\n\n\t\tconst distanceToTop = element.scrollTop\n\t\tconst distanceToLeft = element.scrollLeft\n\t\tconst distanceToRight = element.scrollWidth - element.clientWidth - element.scrollLeft\n\t\tconst distanceToBottom = element.scrollHeight - element.clientHeight - element.scrollTop\n\n\t\tconst scrollData = {\n\t\t\ttop: distanceToTop,\n\t\t\tright: distanceToRight,\n\t\t\tbottom: distanceToBottom,\n\t\t\tleft: distanceToLeft,\n\t\t}\n\n\t\t// Store extra data for the element\n\t\taddExtraData(element, {\n\t\t\tscrollable: true,\n\t\t\tscrollData: scrollData,\n\t\t})\n\n\t\treturn scrollData\n\t}\n\n\t/**\n\t * Checks if a text node is visible.\n\t *\n\t * @param {Text} textNode - The text node to check.\n\t * @returns {boolean} Whether the text node is visible.\n\t */\n\tfunction isTextNodeVisible(textNode) {\n\t\ttry {\n\t\t\t// Special case: when viewportExpansion is -1, consider all text nodes as visible\n\t\t\tif (viewportExpansion === -1) {\n\t\t\t\t// Still check parent visibility for basic filtering\n\t\t\t\tconst parentElement = textNode.parentElement\n\t\t\t\tif (!parentElement) return false\n\n\t\t\t\ttry {\n\t\t\t\t\treturn parentElement.checkVisibility({\n\t\t\t\t\t\tcheckOpacity: true,\n\t\t\t\t\t\tcheckVisibilityCSS: true,\n\t\t\t\t\t})\n\t\t\t\t} catch (e) {\n\t\t\t\t\t// Fallback if checkVisibility is not supported\n\t\t\t\t\tconst style = window.getComputedStyle(parentElement)\n\t\t\t\t\treturn style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0'\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst range = document.createRange()\n\t\t\trange.selectNodeContents(textNode)\n\t\t\tconst rects = range.getClientRects() // Use getClientRects for Range\n\n\t\t\tif (!rects || rects.length === 0) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tlet isAnyRectVisible = false\n\t\t\tlet isAnyRectInViewport = false\n\n\t\t\tfor (const rect of rects) {\n\t\t\t\t// Check size\n\t\t\t\tif (rect.width > 0 && rect.height > 0) {\n\t\t\t\t\tisAnyRectVisible = true\n\n\t\t\t\t\t// Viewport check for this rect\n\t\t\t\t\tif (!(\n\t\t\t\t\t\trect.bottom < -viewportExpansion ||\n\t\t\t\t\t\trect.top > window.innerHeight + viewportExpansion ||\n\t\t\t\t\t\trect.right < -viewportExpansion ||\n\t\t\t\t\t\trect.left > window.innerWidth + viewportExpansion\n\t\t\t\t\t)) {\n\t\t\t\t\t\tisAnyRectInViewport = true\n\t\t\t\t\t\tbreak // Found a visible rect in viewport, no need to check others\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!isAnyRectVisible || !isAnyRectInViewport) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check parent visibility\n\t\t\tconst parentElement = textNode.parentElement\n\t\t\tif (!parentElement) return false\n\n\t\t\ttry {\n\t\t\t\treturn parentElement.checkVisibility({\n\t\t\t\t\tcheckOpacity: true,\n\t\t\t\t\tcheckVisibilityCSS: true,\n\t\t\t\t})\n\t\t\t} catch (e) {\n\t\t\t\t// Fallback if checkVisibility is not supported\n\t\t\t\tconst style = window.getComputedStyle(parentElement)\n\t\t\t\treturn style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0'\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.warn('Error checking text node visibility:', e)\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Checks if an element is accepted.\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t * @returns {boolean} Whether the element is accepted.\n\t */\n\tfunction isElementAccepted(element) {\n\t\tif (!element || !element.tagName) return false\n\n\t\t// Always accept body and common container elements\n\t\tconst alwaysAccept = new Set([\n\t\t\t'body',\n\t\t\t'div',\n\t\t\t'main',\n\t\t\t'article',\n\t\t\t'section',\n\t\t\t'nav',\n\t\t\t'header',\n\t\t\t'footer',\n\t\t])\n\t\tconst tagName = element.tagName.toLowerCase()\n\n\t\tif (alwaysAccept.has(tagName)) return true\n\n\t\tconst leafElementDenyList = new Set([\n\t\t\t'svg',\n\t\t\t'script',\n\t\t\t'style',\n\t\t\t'link',\n\t\t\t'meta',\n\t\t\t'noscript',\n\t\t\t'template',\n\t\t])\n\n\t\treturn !leafElementDenyList.has(tagName)\n\t}\n\n\t/**\n\t * Checks if an element is visible.\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t * @returns {boolean} Whether the element is visible.\n\t */\n\tfunction isElementVisible(element) {\n\t\tconst style = getCachedComputedStyle(element)\n\t\treturn (\n\t\t\telement.offsetWidth > 0 &&\n\t\t\telement.offsetHeight > 0 &&\n\t\t\tstyle?.visibility !== 'hidden' &&\n\t\t\tstyle?.display !== 'none'\n\t\t)\n\t}\n\n\t/**\n\t * Checks if an element is interactive.\n\t *\n\t * lots of comments, and uncommented code - to show the logic of what we already tried\n\t *\n\t * One of the things we tried at the beginning was also to use event listeners, and other fancy class, style stuff -> what actually worked best was just combining most things with computed cursor style :)\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t */\n\tfunction isInteractiveElement(element) {\n\t\tif (!element || element.nodeType !== Node.ELEMENT_NODE) {\n\t\t\treturn false\n\t\t}\n\n\t\t/**\n\t\t * @edit add interactiveBlacklist interactiveWhitelist\n\t\t */\n\t\tif (interactiveBlacklist.includes(element)) {\n\t\t\treturn false // Skip blacklisted elements\n\t\t}\n\t\tif (interactiveWhitelist.includes(element)) {\n\t\t\treturn true // Skip whitelisted elements\n\t\t}\n\n\t\t// Cache the tagName and style lookups\n\t\tconst tagName = element.tagName.toLowerCase()\n\t\tconst style = getCachedComputedStyle(element)\n\n\t\t// Define interactive cursors\n\t\tconst interactiveCursors = new Set([\n\t\t\t'pointer', // Link/clickable elements\n\t\t\t'move', // Movable elements\n\t\t\t'text', // Text selection\n\t\t\t'grab', // Grabbable elements\n\t\t\t'grabbing', // Currently grabbing\n\t\t\t'cell', // Table cell selection\n\t\t\t'copy', // Copy operation\n\t\t\t'alias', // Alias creation\n\t\t\t'all-scroll', // Scrollable content\n\t\t\t'col-resize', // Column resize\n\t\t\t'context-menu', // Context menu available\n\t\t\t'crosshair', // Precise selection\n\t\t\t'e-resize', // East resize\n\t\t\t'ew-resize', // East-west resize\n\t\t\t'help', // Help available\n\t\t\t'n-resize', // North resize\n\t\t\t'ne-resize', // Northeast resize\n\t\t\t'nesw-resize', // Northeast-southwest resize\n\t\t\t'ns-resize', // North-south resize\n\t\t\t'nw-resize', // Northwest resize\n\t\t\t'nwse-resize', // Northwest-southeast resize\n\t\t\t'row-resize', // Row resize\n\t\t\t's-resize', // South resize\n\t\t\t'se-resize', // Southeast resize\n\t\t\t'sw-resize', // Southwest resize\n\t\t\t'vertical-text', // Vertical text selection\n\t\t\t'w-resize', // West resize\n\t\t\t'zoom-in', // Zoom in\n\t\t\t'zoom-out', // Zoom out\n\t\t])\n\n\t\t// Define non-interactive cursors\n\t\tconst nonInteractiveCursors = new Set([\n\t\t\t'not-allowed', // Action not allowed\n\t\t\t'no-drop', // Drop not allowed\n\t\t\t'wait', // Processing\n\t\t\t'progress', // In progress\n\t\t\t'initial', // Initial value\n\t\t\t'inherit', // Inherited value\n\t\t\t//? Let's just include all potentially clickable elements that are not specifically blocked\n\t\t\t// 'none', // No cursor\n\t\t\t// 'default', // Default cursor\n\t\t\t// 'auto', // Browser default\n\t\t])\n\n\t\t/**\n\t\t * Checks if an element has an interactive pointer.\n\t\t *\n\t\t * @param {HTMLElement} element - The element to check.\n\t\t * @returns {boolean} Whether the element has an interactive pointer.\n\t\t */\n\t\tfunction doesElementHaveInteractivePointer(element) {\n\t\t\tif (element.tagName.toLowerCase() === 'html') return false\n\n\t\t\tif (style?.cursor && interactiveCursors.has(style.cursor)) return true\n\n\t\t\treturn false\n\t\t}\n\n\t\tlet isInteractiveCursor = doesElementHaveInteractivePointer(element)\n\n\t\t// Genius fix for almost all interactive elements\n\t\tif (isInteractiveCursor) {\n\t\t\treturn true\n\t\t}\n\n\t\tconst interactiveElements = new Set([\n\t\t\t'a', // Links\n\t\t\t'button', // Buttons\n\t\t\t'input', // All input types (text, checkbox, radio, etc.)\n\t\t\t'select', // Dropdown menus\n\t\t\t'textarea', // Text areas\n\t\t\t'details', // Expandable details\n\t\t\t'summary', // Summary element (clickable part of details)\n\t\t\t'label', // Form labels (often clickable)\n\t\t\t'option', // Select options\n\t\t\t'optgroup', // Option groups\n\t\t\t'fieldset', // Form fieldsets (can be interactive with legend)\n\t\t\t'legend', // Fieldset legends\n\t\t])\n\n\t\t// Define explicit disable attributes and properties\n\t\tconst explicitDisableTags = new Set([\n\t\t\t'disabled', // Standard disabled attribute\n\t\t\t// 'aria-disabled', // ARIA disabled state\n\t\t\t'readonly', // Read-only state\n\t\t\t// 'aria-readonly', // ARIA read-only state\n\t\t\t// 'aria-hidden', // Hidden from accessibility\n\t\t\t// 'hidden', // Hidden attribute\n\t\t\t// 'inert', // Inert attribute\n\t\t\t// 'aria-inert', // ARIA inert state\n\t\t\t// 'tabindex=\"-1\"', // Removed from tab order\n\t\t\t// 'aria-hidden=\"true\"' // Hidden from screen readers\n\t\t])\n\n\t\t// handle inputs, select, checkbox, radio, textarea, button and make sure they are not cursor style disabled/not-allowed\n\t\tif (interactiveElements.has(tagName)) {\n\t\t\t// Check for non-interactive cursor\n\t\t\tif (style?.cursor && nonInteractiveCursors.has(style.cursor)) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check for explicit disable attributes\n\t\t\tfor (const disableTag of explicitDisableTags) {\n\t\t\t\tif (\n\t\t\t\t\telement.hasAttribute(disableTag) ||\n\t\t\t\t\telement.getAttribute(disableTag) === 'true' ||\n\t\t\t\t\telement.getAttribute(disableTag) === ''\n\t\t\t\t) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check for disabled property on form elements\n\t\t\tif (element.disabled) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check for readonly property on form elements\n\t\t\tif (element.readOnly) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check for inert property\n\t\t\tif (element.inert) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\treturn true\n\t\t}\n\n\t\tconst role = element.getAttribute('role')\n\t\tconst ariaRole = element.getAttribute('aria-role')\n\n\t\t// Check for contenteditable attribute\n\t\tif (element.getAttribute('contenteditable') === 'true' || element.isContentEditable) {\n\t\t\treturn true\n\t\t}\n\n\t\t// Added enhancement to capture dropdown interactive elements\n\t\tif (\n\t\t\telement.classList &&\n\t\t\t(element.classList.contains('button') ||\n\t\t\t\telement.classList.contains('dropdown-toggle') ||\n\t\t\t\telement.getAttribute('data-index') ||\n\t\t\t\telement.getAttribute('data-toggle') === 'dropdown' ||\n\t\t\t\telement.getAttribute('aria-haspopup') === 'true')\n\t\t) {\n\t\t\treturn true\n\t\t}\n\n\t\tconst interactiveRoles = new Set([\n\t\t\t'button', // Directly clickable element\n\t\t\t// 'link', // Clickable link\n\t\t\t'menu', // Menu container (ARIA menus)\n\t\t\t'menubar', // Menu bar container\n\t\t\t'menuitem', // Clickable menu item\n\t\t\t'menuitemradio', // Radio-style menu item (selectable)\n\t\t\t'menuitemcheckbox', // Checkbox-style menu item (toggleable)\n\t\t\t'radio', // Radio button (selectable)\n\t\t\t'checkbox', // Checkbox (toggleable)\n\t\t\t'tab', // Tab (clickable to switch content)\n\t\t\t'switch', // Toggle switch (clickable to change state)\n\t\t\t'slider', // Slider control (draggable)\n\t\t\t'spinbutton', // Number input with up/down controls\n\t\t\t'combobox', // Dropdown with text input\n\t\t\t'searchbox', // Search input field\n\t\t\t'textbox', // Text input field\n\t\t\t'listbox', // Selectable list\n\t\t\t'option', // Selectable option in a list\n\t\t\t'scrollbar', // Scrollable control\n\t\t])\n\n\t\t// Basic role/attribute checks\n\t\tconst hasInteractiveRole =\n\t\t\tinteractiveElements.has(tagName) ||\n\t\t\t(role && interactiveRoles.has(role)) ||\n\t\t\t(ariaRole && interactiveRoles.has(ariaRole))\n\n\t\tif (hasInteractiveRole) return true\n\n\t\t// check whether element has event listeners by window.getEventListeners\n\t\ttry {\n\t\t\tif (typeof getEventListeners === 'function') {\n\t\t\t\tconst listeners = getEventListeners(element)\n\t\t\t\tconst mouseEvents = ['click', 'mousedown', 'mouseup', 'dblclick']\n\t\t\t\tfor (const eventType of mouseEvents) {\n\t\t\t\t\tif (listeners[eventType] && listeners[eventType].length > 0) {\n\t\t\t\t\t\treturn true // Found a mouse interaction listener\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst getEventListenersForNode =\n\t\t\t\telement?.ownerDocument?.defaultView?.getEventListenersForNode ||\n\t\t\t\twindow.getEventListenersForNode\n\t\t\tif (typeof getEventListenersForNode === 'function') {\n\t\t\t\tconst listeners = getEventListenersForNode(element)\n\t\t\t\tconst interactionEvents = [\n\t\t\t\t\t'click',\n\t\t\t\t\t'mousedown',\n\t\t\t\t\t'mouseup',\n\t\t\t\t\t'keydown',\n\t\t\t\t\t'keyup',\n\t\t\t\t\t'submit',\n\t\t\t\t\t'change',\n\t\t\t\t\t'input',\n\t\t\t\t\t'focus',\n\t\t\t\t\t'blur',\n\t\t\t\t]\n\t\t\t\tfor (const eventType of interactionEvents) {\n\t\t\t\t\tfor (const listener of listeners) {\n\t\t\t\t\t\tif (listener.type === eventType) {\n\t\t\t\t\t\t\treturn true // Found a common interaction listener\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Fallback: Check common event attributes if getEventListeners is not available (getEventListeners doesn't work in page.evaluate context)\n\t\t\tconst commonMouseAttrs = ['onclick', 'onmousedown', 'onmouseup', 'ondblclick']\n\t\t\tfor (const attr of commonMouseAttrs) {\n\t\t\t\tif (element.hasAttribute(attr) || typeof element[attr] === 'function') {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\t// console.warn(`Could not check event listeners for ${element.tagName}:`, e);\n\t\t\t// If checking listeners fails, rely on other checks\n\t\t}\n\n\t\t/**\n\t\t * @edit scrollable element detection\n\t\t */\n\t\tif (isScrollableElement(element)) {\n\t\t\treturn true\n\t\t}\n\n\t\treturn false\n\t}\n\n\t/**\n\t * Checks if an element is the topmost element at its position.\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t * @returns {boolean} Whether the element is the topmost element at its position.\n\t */\n\tfunction isTopElement(element) {\n\t\t// Special case: when viewportExpansion is -1, consider all elements as \"top\" elements\n\t\tif (viewportExpansion === -1) {\n\t\t\treturn true\n\t\t}\n\n\t\tconst rects = getCachedClientRects(element) // Replace element.getClientRects()\n\n\t\tif (!rects || rects.length === 0) {\n\t\t\treturn false // No geometry, cannot be top\n\t\t}\n\n\t\tlet isAnyRectInViewport = false\n\t\tfor (const rect of rects) {\n\t\t\t// Use the same logic as isInExpandedViewport check\n\t\t\tif (\n\t\t\t\trect.width > 0 &&\n\t\t\t\trect.height > 0 &&\n\t\t\t\t!(\n\t\t\t\t\t// Only check non-empty rects\n\t\t\t\t\trect.bottom < -viewportExpansion ||\n\t\t\t\t\trect.top > window.innerHeight + viewportExpansion ||\n\t\t\t\t\trect.right < -viewportExpansion ||\n\t\t\t\t\trect.left > window.innerWidth + viewportExpansion\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tisAnyRectInViewport = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif (!isAnyRectInViewport) {\n\t\t\treturn false // All rects are outside the viewport area\n\t\t}\n\n\t\t// Find the correct document context and root element\n\t\tlet doc = element.ownerDocument\n\n\t\t// If we're in an iframe, elements are considered top by default\n\t\tif (doc !== window.document) {\n\t\t\treturn true\n\t\t}\n\n\t\t/**\n\t\t * @edit improve `sampleRect`, filter out rects with 0 area\n\t\t */\n\t\t// find a rect that has width and height as sample\n\t\tlet rect = Array.from(rects).find((r) => r.width > 0 && r.height > 0)\n\t\tif (!rect) {\n\t\t\treturn false // No valid rect found\n\t\t}\n\n\t\t// For shadow DOM, we need to check within its own root context\n\t\tconst shadowRoot = element.getRootNode()\n\t\tif (shadowRoot instanceof ShadowRoot) {\n\t\t\tconst centerX = rect.left + rect.width / 2\n\t\t\tconst centerY = rect.top + rect.height / 2\n\n\t\t\ttry {\n\t\t\t\tconst topEl = shadowRoot.elementFromPoint(centerX, centerY)\n\t\t\t\tif (!topEl) return false\n\n\t\t\t\tlet current = topEl\n\t\t\t\twhile (current && current !== shadowRoot) {\n\t\t\t\t\tif (current === element) return true\n\t\t\t\t\tcurrent = current.parentElement\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t} catch (e) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\tconst margin = 5\n\n\t\t// For elements in viewport, check if they're topmost. Do the check in the\n\t\t// center of the element and at the corners to ensure we catch more cases.\n\t\tconst checkPoints = [\n\t\t\t// Initially only this was used, but it was not enough\n\t\t\t{ x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 },\n\t\t\t{ x: rect.left + margin, y: rect.top + margin }, // top left\n\t\t\t// { x: rect.right - margin, y: rect.top + margin }, // top right\n\t\t\t// { x: rect.left + margin, y: rect.bottom - margin }, // bottom left\n\t\t\t{ x: rect.right - margin, y: rect.bottom - margin }, // bottom right\n\t\t]\n\n\t\treturn checkPoints.some(({ x, y }) => {\n\t\t\ttry {\n\t\t\t\tconst topEl = document.elementFromPoint(x, y)\n\t\t\t\tif (!topEl) return false\n\n\t\t\t\tlet current = topEl\n\t\t\t\twhile (current && current !== document.documentElement) {\n\t\t\t\t\tif (current === element) return true\n\t\t\t\t\tcurrent = current.parentElement\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t} catch (e) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Checks if an element is within the expanded viewport.\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t * @param {number} viewportExpansion - The viewport expansion.\n\t * @returns {boolean} Whether the element is within the expanded viewport.\n\t */\n\tfunction isInExpandedViewport(element, viewportExpansion) {\n\t\tif (viewportExpansion === -1) {\n\t\t\treturn true\n\t\t}\n\n\t\tconst rects = element.getClientRects() // Use getClientRects\n\n\t\tif (!rects || rects.length === 0) {\n\t\t\t// Fallback to getBoundingClientRect if getClientRects is empty,\n\t\t\t// useful for elements like <svg> that might not have client rects but have a bounding box.\n\t\t\tconst boundingRect = getCachedBoundingRect(element)\n\t\t\tif (!boundingRect || boundingRect.width === 0 || boundingRect.height === 0) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn !(\n\t\t\t\tboundingRect.bottom < -viewportExpansion ||\n\t\t\t\tboundingRect.top > window.innerHeight + viewportExpansion ||\n\t\t\t\tboundingRect.right < -viewportExpansion ||\n\t\t\t\tboundingRect.left > window.innerWidth + viewportExpansion\n\t\t\t)\n\t\t}\n\n\t\t// Check if *any* client rect is within the viewport\n\t\tfor (const rect of rects) {\n\t\t\tif (rect.width === 0 || rect.height === 0) continue // Skip empty rects\n\n\t\t\tif (!(\n\t\t\t\trect.bottom < -viewportExpansion ||\n\t\t\t\trect.top > window.innerHeight + viewportExpansion ||\n\t\t\t\trect.right < -viewportExpansion ||\n\t\t\t\trect.left > window.innerWidth + viewportExpansion\n\t\t\t)) {\n\t\t\t\treturn true // Found at least one rect in the viewport\n\t\t\t}\n\t\t}\n\n\t\treturn false // No rects were found in the viewport\n\t}\n\n\t// /**\n\t// * Gets the effective scroll of an element.\n\t// *\n\t// * @param {HTMLElement} element - The element to get the effective scroll for.\n\t// * @returns {Object} The effective scroll of the element.\n\t// */\n\t// function getEffectiveScroll(element) {\n\t// let currentEl = element;\n\t// let scrollX = 0;\n\t// let scrollY = 0;\n\n\t// while (currentEl && currentEl !== document.documentElement) {\n\t// if (currentEl.scrollLeft || currentEl.scrollTop) {\n\t// scrollX += currentEl.scrollLeft;\n\t// scrollY += currentEl.scrollTop;\n\t// }\n\t// currentEl = currentEl.parentElement;\n\t// }\n\n\t// scrollX += window.scrollX;\n\t// scrollY += window.scrollY;\n\n\t// return { scrollX, scrollY };\n\t// }\n\n\t/**\n\t * Checks if an element is an interactive candidate.\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t * @returns {boolean} Whether the element is an interactive candidate.\n\t */\n\n\t// @edit fix \"aria-*\" attributes check\n\tconst INTERACTIVE_ARIA_ATTRS = [\n\t\t'aria-expanded',\n\t\t'aria-checked',\n\t\t'aria-selected',\n\t\t'aria-pressed',\n\t\t'aria-haspopup',\n\t\t'aria-controls',\n\t\t'aria-owns',\n\t\t'aria-activedescendant',\n\t\t'aria-valuenow',\n\t\t'aria-valuetext',\n\t\t'aria-valuemax',\n\t\t'aria-valuemin',\n\t\t'aria-autocomplete',\n\t]\n\n\tfunction hasInteractiveAria(el) {\n\t\tfor (let i = 0; i < INTERACTIVE_ARIA_ATTRS.length; i++) {\n\t\t\tif (el.hasAttribute(INTERACTIVE_ARIA_ATTRS[i])) return true\n\t\t}\n\t\treturn false\n\t}\n\n\tfunction isInteractiveCandidate(element) {\n\t\tif (!element || element.nodeType !== Node.ELEMENT_NODE) return false\n\n\t\tconst tagName = element.tagName.toLowerCase()\n\n\t\t// Fast-path for common interactive elements\n\t\tconst interactiveElements = new Set([\n\t\t\t'a',\n\t\t\t'button',\n\t\t\t'input',\n\t\t\t'select',\n\t\t\t'textarea',\n\t\t\t'details',\n\t\t\t'summary',\n\t\t\t'label',\n\t\t])\n\n\t\tif (interactiveElements.has(tagName)) return true\n\n\t\t// Quick attribute checks without getting full lists\n\t\tconst hasQuickInteractiveAttr =\n\t\t\telement.hasAttribute('onclick') ||\n\t\t\telement.hasAttribute('role') ||\n\t\t\telement.hasAttribute('tabindex') ||\n\t\t\thasInteractiveAria(element) ||\n\t\t\telement.hasAttribute('data-action') ||\n\t\t\telement.getAttribute('contenteditable') === 'true'\n\n\t\treturn hasQuickInteractiveAttr\n\t}\n\n\t// --- Define constants for distinct interaction check ---\n\tconst DISTINCT_INTERACTIVE_TAGS = new Set([\n\t\t'a',\n\t\t'button',\n\t\t'input',\n\t\t'select',\n\t\t'textarea',\n\t\t'summary',\n\t\t'details',\n\t\t'label',\n\t\t'option',\n\t\t'li',\n\t])\n\tconst DISTINCT_INTERACTIVE_ROLES = new Set([\n\t\t'button',\n\t\t'link',\n\t\t'menuitem',\n\t\t'menuitemradio',\n\t\t'menuitemcheckbox',\n\t\t'radio',\n\t\t'checkbox',\n\t\t'tab',\n\t\t'switch',\n\t\t'slider',\n\t\t'spinbutton',\n\t\t'combobox',\n\t\t'searchbox',\n\t\t'textbox',\n\t\t'listbox',\n\t\t'listitem',\n\t\t'treeitem',\n\t\t'row',\n\t\t'option',\n\t\t'scrollbar',\n\t])\n\n\t/**\n\t * Heuristically determines if an element should be considered as independently interactive,\n\t * even if it's nested inside another interactive container.\n\t *\n\t * This function helps detect deeply nested actionable elements (e.g., menu items within a button)\n\t * that may not be picked up by strict interactivity checks.\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t * @returns {boolean} Whether the element is heuristically interactive.\n\t */\n\tfunction isHeuristicallyInteractive(element) {\n\t\tif (!element || element.nodeType !== Node.ELEMENT_NODE) return false\n\n\t\t// Skip non-visible elements early for performance\n\t\tif (!isElementVisible(element)) return false\n\n\t\t// Check for common attributes that often indicate interactivity\n\t\tconst hasInteractiveAttributes =\n\t\t\telement.hasAttribute('role') ||\n\t\t\telement.hasAttribute('tabindex') ||\n\t\t\telement.hasAttribute('onclick') ||\n\t\t\ttypeof element.onclick === 'function'\n\n\t\t// Check for semantic class names suggesting interactivity\n\t\tconst hasInteractiveClass = /\\b(btn|clickable|menu|item|entry|link)\\b/i.test(\n\t\t\telement.className || ''\n\t\t)\n\n\t\t// Determine whether the element is inside a known interactive container\n\t\tconst isInKnownContainer = Boolean(\n\t\t\telement.closest('button,a,[role=\"button\"],.menu,.dropdown,.list,.toolbar')\n\t\t)\n\n\t\t// Ensure the element has at least one visible child (to avoid marking empty wrappers)\n\t\tconst hasVisibleChildren = [...element.children].some(isElementVisible)\n\n\t\t// Avoid highlighting elements whose parent is <body> (top-level wrappers)\n\t\tconst isParentBody = element.parentElement && element.parentElement.isSameNode(document.body)\n\n\t\treturn (\n\t\t\t(isInteractiveElement(element) || hasInteractiveAttributes || hasInteractiveClass) &&\n\t\t\thasVisibleChildren &&\n\t\t\tisInKnownContainer &&\n\t\t\t!isParentBody\n\t\t)\n\t}\n\n\t/**\n\t * Checks if an element likely represents a distinct interaction\n\t * separate from its parent (if the parent is also interactive).\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t * @returns {boolean} Whether the element is a distinct interaction.\n\t */\n\tfunction isElementDistinctInteraction(element) {\n\t\tif (!element || element.nodeType !== Node.ELEMENT_NODE) {\n\t\t\treturn false\n\t\t}\n\n\t\tconst tagName = element.tagName.toLowerCase()\n\t\tconst role = element.getAttribute('role')\n\n\t\t// Check if it's an iframe - always distinct boundary\n\t\tif (tagName === 'iframe') {\n\t\t\treturn true\n\t\t}\n\n\t\t// Check tag name\n\t\tif (DISTINCT_INTERACTIVE_TAGS.has(tagName)) {\n\t\t\treturn true\n\t\t}\n\t\t// Check interactive roles\n\t\tif (role && DISTINCT_INTERACTIVE_ROLES.has(role)) {\n\t\t\treturn true\n\t\t}\n\t\t// Check contenteditable\n\t\tif (element.isContentEditable || element.getAttribute('contenteditable') === 'true') {\n\t\t\treturn true\n\t\t}\n\t\t// Check for common testing/automation attributes\n\t\tif (\n\t\t\telement.hasAttribute('data-testid') ||\n\t\t\telement.hasAttribute('data-cy') ||\n\t\t\telement.hasAttribute('data-test')\n\t\t) {\n\t\t\treturn true\n\t\t}\n\t\t// Check for explicit onclick handler (attribute or property)\n\t\tif (element.hasAttribute('onclick') || typeof element.onclick === 'function') {\n\t\t\treturn true\n\t\t}\n\t\t// ARIA state attributes imply the element manages its own interaction state\n\t\tif (hasInteractiveAria(element)) {\n\t\t\treturn true\n\t\t}\n\n\t\t// return false\n\n\t\t// Check for other common interaction event listeners\n\t\ttry {\n\t\t\tconst getEventListenersForNode =\n\t\t\t\telement?.ownerDocument?.defaultView?.getEventListenersForNode ||\n\t\t\t\twindow.getEventListenersForNode\n\t\t\tif (typeof getEventListenersForNode === 'function') {\n\t\t\t\tconst listeners = getEventListenersForNode(element)\n\t\t\t\tconst interactionEvents = [\n\t\t\t\t\t'click',\n\t\t\t\t\t'mousedown',\n\t\t\t\t\t'mouseup',\n\t\t\t\t\t'keydown',\n\t\t\t\t\t'keyup',\n\t\t\t\t\t'submit',\n\t\t\t\t\t'change',\n\t\t\t\t\t'input',\n\t\t\t\t\t'focus',\n\t\t\t\t\t'blur',\n\t\t\t\t]\n\t\t\t\tfor (const eventType of interactionEvents) {\n\t\t\t\t\tfor (const listener of listeners) {\n\t\t\t\t\t\tif (listener.type === eventType) {\n\t\t\t\t\t\t\treturn true // Found a common interaction listener\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Fallback: Check common event attributes if getEventListeners is not available (getEventListenersForNode doesn't work in page.evaluate context)\n\t\t\tconst commonEventAttrs = [\n\t\t\t\t'onmousedown',\n\t\t\t\t'onmouseup',\n\t\t\t\t'onkeydown',\n\t\t\t\t'onkeyup',\n\t\t\t\t'onsubmit',\n\t\t\t\t'onchange',\n\t\t\t\t'oninput',\n\t\t\t\t'onfocus',\n\t\t\t\t'onblur',\n\t\t\t]\n\t\t\tif (commonEventAttrs.some((attr) => element.hasAttribute(attr))) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t} catch (e) {\n\t\t\t// console.warn(`Could not check event listeners for ${element.tagName}:`, e);\n\t\t\t// If checking listeners fails, rely on other checks\n\t\t}\n\n\t\t// if the element is not strictly interactive but appears clickable based on heuristic signals\n\t\tif (isHeuristicallyInteractive(element)) {\n\t\t\treturn true\n\t\t}\n\n\t\t// Scrollable containers are always distinct — the LLM needs their index for targeted scrolling.\n\t\t// Check extraData (already set by isScrollableElement in isInteractiveElement) to avoid redundant layout reads.\n\t\tif (extraData.get(element)?.scrollable) {\n\t\t\treturn true\n\t\t}\n\n\t\t// Default to false: if it's interactive but doesn't match above,\n\t\t// assume it triggers the same action as the parent.\n\t\treturn false\n\t}\n\t// --- End distinct interaction check ---\n\n\t/**\n * Handles the logic for deciding whether to highlight an element and performing the highlight.\n * @param {\n {\n tagName: string;\n attributes: Record<string, string>;\n xpath: any;\n children: never[];\n isVisible?: boolean;\n isTopElement?: boolean;\n isInteractive?: boolean;\n isInViewport?: boolean;\n highlightIndex?: number;\n shadowRoot?: boolean;\n }} nodeData - The node data object.\n * @param {HTMLElement} node - The node to highlight.\n * @param {HTMLElement | null} parentIframe - The parent iframe node.\n * @param {boolean} isParentHighlighted - Whether the parent node is highlighted.\n * @returns {boolean} Whether the element was highlighted.\n */\n\tfunction handleHighlighting(nodeData, node, parentIframe, isParentHighlighted) {\n\t\tif (!nodeData.isInteractive) return false // Not interactive, definitely don't highlight\n\n\t\tlet shouldHighlight = false\n\t\tif (!isParentHighlighted) {\n\t\t\t// Parent wasn't highlighted, this interactive node can be highlighted.\n\t\t\tshouldHighlight = true\n\t\t} else {\n\t\t\t// Parent *was* highlighted. Only highlight this node if it represents a distinct interaction.\n\t\t\tif (isElementDistinctInteraction(node)) {\n\t\t\t\tshouldHighlight = true\n\t\t\t} else {\n\t\t\t\t// console.log(`Skipping highlight for ${nodeData.tagName} (parent highlighted)`);\n\t\t\t\tshouldHighlight = false\n\t\t\t}\n\t\t}\n\n\t\tif (shouldHighlight) {\n\t\t\t// Check viewport status before assigning index and highlighting\n\t\t\tnodeData.isInViewport = isInExpandedViewport(node, viewportExpansion)\n\n\t\t\t// When viewportExpansion is -1, all interactive elements should get a highlight index\n\t\t\t// regardless of viewport status\n\t\t\tif (nodeData.isInViewport || viewportExpansion === -1) {\n\t\t\t\tnodeData.highlightIndex = highlightIndex++\n\n\t\t\t\tif (doHighlightElements) {\n\t\t\t\t\tif (focusHighlightIndex >= 0) {\n\t\t\t\t\t\tif (focusHighlightIndex === nodeData.highlightIndex) {\n\t\t\t\t\t\t\thighlightElement(node, nodeData.highlightIndex, parentIframe)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\thighlightElement(node, nodeData.highlightIndex, parentIframe)\n\t\t\t\t\t}\n\t\t\t\t\treturn true // Successfully highlighted\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// console.log(`Skipping highlight for ${nodeData.tagName} (outside viewport)`);\n\t\t\t}\n\t\t}\n\n\t\treturn false // Did not highlight\n\t}\n\n\t/**\n\t * Creates a node data object for a given node and its descendants.\n\t *\n\t * @param {HTMLElement} node - The node to process.\n\t * @param {HTMLElement | null} parentIframe - The parent iframe node.\n\t * @param {boolean} isParentHighlighted - Whether the parent node is highlighted.\n\t * @returns {string | null} The ID of the node data object, or null if the node is not processed.\n\t */\n\tfunction buildDomTree(node, parentIframe = null, isParentHighlighted = false) {\n\t\t// Fast rejection checks first\n\t\tif (\n\t\t\t!node ||\n\t\t\tnode.id === HIGHLIGHT_CONTAINER_ID ||\n\t\t\t(node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE)\n\t\t) {\n\t\t\treturn null\n\t\t}\n\n\t\tif (!node || node.id === HIGHLIGHT_CONTAINER_ID) {\n\t\t\treturn null\n\t\t}\n\n\t\t/**\n\t\t * @edit add `data-browser-use-ignore` attribute\n\t\t */\n\t\tif (node.dataset?.browserUseIgnore === 'true' || node.dataset?.pageAgentIgnore === 'true') {\n\t\t\treturn null // Skip this node and its children\n\t\t}\n\n\t\t/**\n\t\t * @edit exclude aria-hidden elements\n\t\t */\n\t\tif (node.getAttribute && node.getAttribute('aria-hidden') === 'true') {\n\t\t\treturn null // Skip this node and its children\n\t\t}\n\n\t\t// Special handling for root node (body)\n\t\tif (node === document.body) {\n\t\t\tconst nodeData = {\n\t\t\t\ttagName: 'body',\n\t\t\t\tattributes: {},\n\t\t\t\txpath: '/body',\n\t\t\t\tchildren: [],\n\t\t\t}\n\n\t\t\t// Process children of body\n\t\t\tfor (const child of node.childNodes) {\n\t\t\t\tconst domElement = buildDomTree(child, parentIframe, false) // Body's children have no highlighted parent initially\n\t\t\t\tif (domElement) nodeData.children.push(domElement)\n\t\t\t}\n\n\t\t\tconst id = `${ID.current++}`\n\t\t\tDOM_HASH_MAP[id] = nodeData\n\t\t\treturn id\n\t\t}\n\n\t\t// Early bailout for non-element nodes except text\n\t\tif (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE) {\n\t\t\treturn null\n\t\t}\n\n\t\t// Process text nodes\n\t\tif (node.nodeType === Node.TEXT_NODE) {\n\t\t\tconst textContent = node.textContent?.trim()\n\t\t\tif (!textContent) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\t// Only check visibility for text nodes that might be visible\n\t\t\tconst parentElement = node.parentElement\n\t\t\tif (!parentElement || parentElement.tagName.toLowerCase() === 'script') {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tconst id = `${ID.current++}`\n\t\t\tDOM_HASH_MAP[id] = {\n\t\t\t\ttype: 'TEXT_NODE',\n\t\t\t\ttext: textContent,\n\t\t\t\tisVisible: isTextNodeVisible(node),\n\t\t\t}\n\t\t\treturn id\n\t\t}\n\n\t\t// Quick checks for element nodes\n\t\tif (node.nodeType === Node.ELEMENT_NODE && !isElementAccepted(node)) {\n\t\t\treturn null\n\t\t}\n\n\t\t// Early viewport check - only filter out elements clearly outside viewport\n\t\t// The getBoundingClientRect() of the Shadow DOM host element may return width/height = 0\n\t\tif (viewportExpansion !== -1 && !node.shadowRoot) {\n\t\t\tconst rect = getCachedBoundingRect(node) // Keep for initial quick check\n\t\t\tconst style = getCachedComputedStyle(node)\n\n\t\t\t// Skip viewport check for fixed/sticky elements as they may appear anywhere\n\t\t\tconst isFixedOrSticky = style && (style.position === 'fixed' || style.position === 'sticky')\n\n\t\t\t// Check if element has actual dimensions using offsetWidth/Height (quick check)\n\t\t\tconst hasSize = node.offsetWidth > 0 || node.offsetHeight > 0\n\n\t\t\t// Use getBoundingClientRect for the quick OUTSIDE check.\n\t\t\t// isInExpandedViewport will do the more accurate check later if needed.\n\t\t\tif (\n\t\t\t\t!rect ||\n\t\t\t\t(!isFixedOrSticky &&\n\t\t\t\t\t!hasSize &&\n\t\t\t\t\t(rect.bottom < -viewportExpansion ||\n\t\t\t\t\t\trect.top > window.innerHeight + viewportExpansion ||\n\t\t\t\t\t\trect.right < -viewportExpansion ||\n\t\t\t\t\t\trect.left > window.innerWidth + viewportExpansion))\n\t\t\t) {\n\t\t\t\t// console.log(\"Skipping node outside viewport (quick check):\", node.tagName, rect);\n\t\t\t\treturn null\n\t\t\t}\n\t\t}\n\n\t\t/**\n * @type {\n {\n tagName: string;\n attributes: Record<string, string | null>;\n xpath: any;\n children: never[];\n isVisible?: boolean;\n isTopElement?: boolean;\n isInteractive?: boolean;\n isInViewport?: boolean;\n highlightIndex?: number;\n shadowRoot?: boolean;\n }\n } nodeData - The node data object.\n */\n\t\tconst nodeData = {\n\t\t\ttagName: node.tagName.toLowerCase(),\n\t\t\tattributes: {},\n\n\t\t\t/**\n\t\t\t * @edit no need for xpath\n\t\t\t */\n\t\t\t// xpath: getXPathTree(node, true),\n\n\t\t\tchildren: [],\n\t\t}\n\n\t\t// Get attributes for interactive elements or potential text containers\n\t\tif (\n\t\t\tisInteractiveCandidate(node) ||\n\t\t\tnode.tagName.toLowerCase() === 'iframe' ||\n\t\t\tnode.tagName.toLowerCase() === 'body'\n\t\t) {\n\t\t\tconst attributeNames = node.getAttributeNames?.() || []\n\t\t\tfor (const name of attributeNames) {\n\t\t\t\tconst value = node.getAttribute(name)\n\t\t\t\tnodeData.attributes[name] = value\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @edit @workaround input.checked\n\t\t\t */\n\t\t\tif (\n\t\t\t\tnode.tagName.toLowerCase() === 'input' &&\n\t\t\t\t(node.type === 'checkbox' || node.type === 'radio')\n\t\t\t) {\n\t\t\t\tnodeData.attributes.checked = node.checked ? 'true' : 'false' // Store as string for consistency\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @edit @workaround select.value\n\t\t\t * select 的选中状态是 DOM 属性(selectedIndex/value),不会反映到 HTML attribute 上,\n\t\t\t * 若不同步,选择后重新快照时 LLM 看不到变化,会误判操作未生效而反复执行 select 操作。\n\t\t\t */\n\t\t\tif (node.tagName.toLowerCase() === 'select') {\n\t\t\t\tconst selectedOption = node.options[node.selectedIndex]\n\t\t\t\tnodeData.attributes.value = selectedOption ? selectedOption.text : node.value\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @edit @workaround label-wrapped checkbox/radio checked\n\t\t\t * 常见组件库(如 antd)把 checkbox/radio 包在 <label> 里,视觉上的选中框覆盖在原生\n\t\t\t * input 上面,导致 input 不是 isTopElement,不会被收进可交互树、也就打印不出 checked\n\t\t\t * 状态。真正可点击并带 highlightIndex 的是这个 <label>,所以把关联 input 的 checked\n\t\t\t * 状态同步到 label 自己的 attributes 上,否则 LLM 看不到勾选反馈会反复点击。\n\t\t\t */\n\t\t\tif (node.tagName.toLowerCase() === 'label') {\n\t\t\t\tconst forId = node.getAttribute('for')\n\t\t\t\tconst input =\n\t\t\t\t\tnode.querySelector('input[type=\"checkbox\"], input[type=\"radio\"]') ||\n\t\t\t\t\t(forId && document.getElementById(forId))\n\t\t\t\tif (input && (input.type === 'checkbox' || input.type === 'radio')) {\n\t\t\t\t\tnodeData.attributes.checked = input.checked ? 'true' : 'false'\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet nodeWasHighlighted = false\n\t\t// Perform visibility, interactivity, and highlighting checks\n\t\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\t\tnodeData.isVisible = isElementVisible(node) // isElementVisible uses offsetWidth/Height, which is fine\n\t\t\tif (nodeData.isVisible) {\n\t\t\t\tnodeData.isTopElement = isTopElement(node)\n\n\t\t\t\t// Special handling for ARIA menu containers - check interactivity even if not top element\n\t\t\t\tconst role = node.getAttribute('role')\n\t\t\t\tconst isMenuContainer = role === 'menu' || role === 'menubar' || role === 'listbox'\n\n\t\t\t\tif (nodeData.isTopElement || isMenuContainer) {\n\t\t\t\t\tnodeData.isInteractive = isInteractiveElement(node)\n\t\t\t\t\t// Call the dedicated highlighting function\n\t\t\t\t\tnodeWasHighlighted = handleHighlighting(nodeData, node, parentIframe, isParentHighlighted)\n\n\t\t\t\t\t/**\n\t\t\t\t\t * @edit direct dom ref\n\t\t\t\t\t */\n\t\t\t\t\tnodeData.ref = node\n\n\t\t\t\t\t/**\n\t\t\t\t\t * @edit make sure attributes exist for interactive candidates.\n\t\t\t\t\t * @note if the element failed the isInteractiveCandidate, attributes would be empty.\n\t\t\t\t\t */\n\t\t\t\t\tif (nodeData.isInteractive && Object.keys(nodeData.attributes).length === 0) {\n\t\t\t\t\t\tconst attributeNames = node.getAttributeNames?.() || []\n\t\t\t\t\t\tfor (const name of attributeNames) {\n\t\t\t\t\t\t\tconst value = node.getAttribute(name)\n\t\t\t\t\t\t\tnodeData.attributes[name] = value\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Process children, with special handling for iframes and rich text editors\n\t\tif (node.tagName) {\n\t\t\tconst tagName = node.tagName.toLowerCase()\n\n\t\t\t// Handle iframes\n\t\t\tif (tagName === 'iframe') {\n\t\t\t\ttry {\n\t\t\t\t\tconst iframeDoc = node.contentDocument || node.contentWindow?.document\n\t\t\t\t\tif (iframeDoc) {\n\t\t\t\t\t\tfor (const child of iframeDoc.childNodes) {\n\t\t\t\t\t\t\tconst domElement = buildDomTree(child, node, false)\n\t\t\t\t\t\t\tif (domElement) nodeData.children.push(domElement)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconsole.warn('Unable to access iframe:', e)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Handle rich text editors and contenteditable elements\n\t\t\telse if (\n\t\t\t\tnode.isContentEditable ||\n\t\t\t\tnode.getAttribute('contenteditable') === 'true' ||\n\t\t\t\tnode.id === 'tinymce' ||\n\t\t\t\tnode.classList.contains('mce-content-body') ||\n\t\t\t\t(tagName === 'body' && node.getAttribute('data-id')?.startsWith('mce_'))\n\t\t\t) {\n\t\t\t\t// Process all child nodes to capture formatted text\n\t\t\t\tfor (const child of node.childNodes) {\n\t\t\t\t\tconst domElement = buildDomTree(child, parentIframe, nodeWasHighlighted)\n\t\t\t\t\tif (domElement) nodeData.children.push(domElement)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Handle shadow DOM\n\t\t\t\tif (node.shadowRoot) {\n\t\t\t\t\tnodeData.shadowRoot = true\n\t\t\t\t\tfor (const child of node.shadowRoot.childNodes) {\n\t\t\t\t\t\tconst domElement = buildDomTree(child, parentIframe, nodeWasHighlighted)\n\t\t\t\t\t\tif (domElement) nodeData.children.push(domElement)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Handle regular elements\n\t\t\t\tfor (const child of node.childNodes) {\n\t\t\t\t\t// Pass the highlighted status of the *current* node to its children\n\t\t\t\t\tconst passHighlightStatusToChild = nodeWasHighlighted || isParentHighlighted\n\t\t\t\t\tconst domElement = buildDomTree(child, parentIframe, passHighlightStatusToChild)\n\t\t\t\t\tif (domElement) nodeData.children.push(domElement)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Skip empty anchor tags only if they have no dimensions and no children\n\t\tif (nodeData.tagName === 'a' && nodeData.children.length === 0 && !nodeData.attributes.href) {\n\t\t\t// Check if the anchor has actual dimensions\n\t\t\tconst rect = getCachedBoundingRect(node)\n\t\t\tconst hasSize =\n\t\t\t\t(rect && rect.width > 0 && rect.height > 0) || node.offsetWidth > 0 || node.offsetHeight > 0\n\n\t\t\tif (!hasSize) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * @edit add `extra` field for extra data\n\t\t */\n\t\tnodeData.extra = extraData.get(node) || null\n\n\t\tconst id = `${ID.current++}`\n\t\tDOM_HASH_MAP[id] = nodeData\n\t\treturn id\n\t}\n\n\tconst rootId = buildDomTree(document.body)\n\n\t// Clear the cache before starting\n\tDOM_CACHE.clearCache()\n\n\treturn { rootId, map: DOM_HASH_MAP }\n}\n","import domTree from './dom_tree/index.js'\nimport {\n\tElementDomNode,\n\tFlatDomTree,\n\tInteractiveElementDomNode,\n\tTextDomNode,\n} from './dom_tree/type'\n\n/**\n * 供 LLM 使用的结构化浏览器状态\n */\nexport interface BrowserState {\n\turl: string\n\ttitle: string\n\t/** 页面信息 + 滚动位置提示(例如 \"Page info: 1920x1080px...\\n[Start of page]\") */\n\theader: string\n\t/** 可交互元素的简化 HTML */\n\tcontent: string\n\t/** 页面底部提示(例如 \"... 300 pixels below ...\" 或 \"[End of page]\") */\n\tfooter: string\n}\n\n/**\n * DOM 树提取时的视口扩展。\n * -1 表示整页(不受视口限制)\n * 0 表示仅视口\n * 正数表示在视口基础上额外扩展的像素数\n *\n * @note 由于 isTopElement 依赖 elementFromPoint,\n * 当元素在视口外时会返回 null,该特性实际用处不大,仅在 -1 和 0 之间有区别\n */\nconst DEFAULT_VIEWPORT_EXPANSION = -1\n\n// 解析视口扩展参数,未传入时使用默认值(-1 表示整页)\nexport function resolveViewportExpansion(viewportExpansion?: number): number {\n\treturn viewportExpansion ?? DEFAULT_VIEWPORT_EXPANSION\n}\n\nexport interface DomConfig {\n\tviewportExpansion?: number // 视口扩展像素数,-1 表示整页\n\tinteractiveBlacklist?: (Element | (() => Element))[] // 不视为可交互的元素黑名单\n\tinteractiveWhitelist?: (Element | (() => Element))[] // 强制视为可交互的元素白名单\n\tincludeAttributes?: string[] // 输出文本时需要包含的额外属性名\n\thighlightOpacity?: number // 高亮边框透明度\n\thighlightLabelOpacity?: number // 高亮序号标签透明度\n\n\t/**\n\t * 即使不可交互,也保留脱水输出中的语义化地标标签\n\t * @note 结合页面滚动时可能让 LLM 感到困惑,请谨慎使用\n\t **/\n\tkeepSemanticTags?: boolean\n}\n\n// TODO: 对应的角色\n// 语义化标签集合:即使不可交互,输出时也会保留这些标签以提供上下文\nconst SEMANTIC_TAGS = new Set([\n\t'nav',\n\t'menu',\n\t// 'main',\n\t'header',\n\t'footer',\n\t'aside',\n\t// 'article',\n\t// 'form',\n\t'dialog',\n])\n\n/**\n * 用于检测可交互元素是否是新出现的。\n */\nconst newElementsCache = new WeakMap<HTMLElement, string>()\n\n// 生成扁平化 DOM 树(FlatDomTree),包含可交互元素标记、高亮编号及\"新出现\"标记\nexport function getFlatTree(config: DomConfig): FlatDomTree {\n\t// 解析视口扩展参数\n\tconst viewportExpansion = resolveViewportExpansion(config.viewportExpansion)\n\n\t// 解析黑名单:函数形式会在调用时取回实际元素\n\tconst interactiveBlacklist = [] as Element[]\n\tfor (const item of config.interactiveBlacklist || []) {\n\t\tif (typeof item === 'function') {\n\t\t\tinteractiveBlacklist.push(item())\n\t\t} else {\n\t\t\tinteractiveBlacklist.push(item)\n\t\t}\n\t}\n\n\t// 解析白名单:函数形式会在调用时取回实际元素\n\tconst interactiveWhitelist = [] as Element[]\n\tfor (const item of config.interactiveWhitelist || []) {\n\t\tif (typeof item === 'function') {\n\t\t\tinteractiveWhitelist.push(item())\n\t\t} else {\n\t\t\tinteractiveWhitelist.push(item)\n\t\t}\n\t}\n\n\tconst elements = domTree({\n\t\tdoHighlightElements: true, // 是否绘制高亮边框\n\t\tdebugMode: true, // 是否开启调试模式\n\t\tfocusHighlightIndex: -1, // 聚焦的高亮序号,-1 表示不聚焦\n\t\tviewportExpansion, // 视口扩展像素数\n\t\tinteractiveBlacklist, // 可交互元素黑名单\n\t\tinteractiveWhitelist, // 可交互元素白名单\n\t\thighlightOpacity: config.highlightOpacity ?? 0.0, // 高亮边框透明度\n\t\thighlightLabelOpacity: config.highlightLabelOpacity ?? 0.1, // 高亮序号标签透明度\n\t}) as FlatDomTree\n\n\t// 当前页面 URL,用于标记新出现元素\n\tconst currentUrl = window.location.href\n\n\t/**\n\t * 标记新出现的元素\n\t * @todo browser-use 使用 hash(位置,属性等信息) 来判断是否同一个元素,\n\t * 能够解决 1. 元素被删除后重新添加 2. 页面卸载 等问题。\n\t * 这里先简单做.\n\t */\n\tfor (const nodeId in elements.map) {\n\t\tconst node = elements.map[nodeId]\n\t\tif (node.isInteractive && node.ref) {\n\t\t\tconst ref = node.ref as HTMLElement\n\t\t\t// @note 这样太严格,元素是可以跨页面存在的\n\t\t\t// if (newElementsCache.get(ref) !== currentUrl) {\n\t\t\tif (!newElementsCache.has(ref)) {\n\t\t\t\tnewElementsCache.set(ref, currentUrl)\n\t\t\t\tnode.isNew = true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn elements\n}\n\n// glob 模式(含 * 通配符)到正则表达式的缓存,避免重复编译\nconst globRegexCache = new Map<string, RegExp>()\n\n// 将 glob 模式(* 通配符)转换为正则表达式,并缓存结果\nfunction globToRegex(pattern: string): RegExp {\n\tlet regex = globRegexCache.get(pattern)\n\tif (!regex) {\n\t\tconst escaped = pattern.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n\t\tregex = new RegExp(`^${escaped.replace(/\\*/g, '.*')}$`)\n\t\tglobRegexCache.set(pattern, regex)\n\t}\n\treturn regex\n}\n\n// 根据属性名模式列表,从元素属性中筛选出需要保留的属性(支持 * 通配符,并去掉空值)\nfunction matchAttributes(\n\tattrs: Record<string, string>,\n\tpatterns: string[]\n): Record<string, string> {\n\tconst result: Record<string, string> = {}\n\n\tfor (const pattern of patterns) {\n\t\tif (pattern.includes('*')) {\n\t\t\t// 含通配符:对每个属性名做正则匹配\n\t\t\tconst regex = globToRegex(pattern)\n\t\t\tfor (const key of Object.keys(attrs)) {\n\t\t\t\tif (regex.test(key) && attrs[key].trim()) {\n\t\t\t\t\tresult[key] = attrs[key].trim()\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// 精确属性名匹配\n\t\t\tconst value = attrs[pattern]\n\t\t\tif (value && value.trim()) {\n\t\t\t\tresult[pattern] = value.trim()\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result\n}\n\n/**\n * elementsToString 内部使用的类型\n */\ninterface TreeNode {\n\ttype: 'text' | 'element' // 节点类型:文本节点或元素节点\n\tparent: TreeNode | null // 父节点\n\tchildren: TreeNode[] // 子节点列表\n\tisVisible: boolean // 是否可见\n\t// 文本节点属性\n\ttext?: string // 文本节点内容\n\t// 元素节点属性\n\ttagName?: string // 元素标签名\n\tattributes?: Record<string, string> // 元素属性集合\n\tisInteractive?: boolean // 是否可交互\n\tisTopElement?: boolean // 是否为最上层元素\n\tisNew?: boolean // 是否为本次新出现的元素\n\thighlightIndex?: number // 高亮序号(可交互元素)\n\textra?: Record<string, any> // 附加信息(如滚动数据)\n}\n\n/**\n * 对应 python 中的 views::clickable_elements_to_string,\n * 将 dom 信息处理成适合 llm 阅读的文本格式\n * @形如\n * ``` text\n * [0]<a aria-label=page-agent.js 首页 />\n * [1]<div >P />\n * [2]<div >page-agent.js\n * UI Agent in your webpage />\n * [3]<a >文档 />\n * [4]<a aria-label=查看源码(在新窗口打开)>源码 />\n * UI Agent in your webpage\n * 用户输入需求,AI 理解页面并自动操作。\n * [5]<a role=button>快速开始 />\n * [6]<a role=button>查看文档 />\n * 无需后端\n * ```\n * 其中可交互元素用序号标出,提示llm可以用序号操作。\n * 缩进代表父子关系。\n * 普通文本则直接列出来。\n *\n * @todo 数据脱敏过滤器\n */\nexport function flatTreeToString(\n\tflatTree: FlatDomTree,\n\tincludeAttributes: string[] = [],\n\tkeepSemanticTags = false\n): string {\n\t// 默认需要在输出中保留的属性名列表\n\tconst DEFAULT_INCLUDE_ATTRIBUTES = [\n\t\t'title',\n\t\t'type',\n\t\t'checked',\n\t\t'name',\n\t\t'role',\n\t\t'value',\n\t\t'placeholder',\n\t\t'data-date-format',\n\t\t'alt',\n\t\t'aria-label',\n\t\t'aria-expanded',\n\t\t'data-state',\n\t\t'aria-checked',\n\n\t\t// @edit 为更好地处理表单而新增\n\t\t'id',\n\t\t'for',\n\n\t\t// 用于跳转检查\n\t\t'target',\n\n\t\t// 绝对定位的下拉菜单\n\t\t'aria-haspopup',\n\t\t'aria-controls',\n\t\t'aria-owns',\n\n\t\t// 内容可编辑\n\t\t'contenteditable',\n\t]\n\n\t// 合并用户传入的属性与默认属性列表\n\tconst includeAttrs = [...includeAttributes, ...DEFAULT_INCLUDE_ATTRIBUTES]\n\n\t// 辅助函数:截断文本长度\n\tconst capTextLength = (text: string, maxLength: number): string => {\n\t\tif (text.length > maxLength) {\n\t\t\treturn text.substring(0, maxLength) + '...'\n\t\t}\n\t\treturn text\n\t}\n\n\t// 根据扁平 map 构建树结构\n\tconst buildTreeNode = (nodeId: string): TreeNode | null => {\n\t\tconst node = flatTree.map[nodeId]\n\t\tif (!node) return null\n\n\t\tif (node.type === 'TEXT_NODE') {\n\t\t\tconst textNode = node as TextDomNode\n\t\t\treturn {\n\t\t\t\ttype: 'text',\n\t\t\t\ttext: textNode.text,\n\t\t\t\tisVisible: textNode.isVisible,\n\t\t\t\tparent: null,\n\t\t\t\tchildren: [],\n\t\t\t}\n\t\t} else {\n\t\t\tconst elementNode = node as ElementDomNode\n\t\t\tconst children: TreeNode[] = []\n\n\t\t\tif (elementNode.children) {\n\t\t\t\tfor (const childId of elementNode.children) {\n\t\t\t\t\tconst child = buildTreeNode(childId)\n\t\t\t\t\tif (child) {\n\t\t\t\t\t\tchild.parent = null // 稍后设置\n\t\t\t\t\t\tchildren.push(child)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttype: 'element',\n\t\t\t\ttagName: elementNode.tagName,\n\t\t\t\tattributes: elementNode.attributes ?? {},\n\t\t\t\tisVisible: elementNode.isVisible ?? false,\n\t\t\t\tisInteractive: elementNode.isInteractive ?? false,\n\t\t\t\tisTopElement: elementNode.isTopElement ?? false,\n\t\t\t\tisNew: elementNode.isNew ?? false,\n\t\t\t\thighlightIndex: elementNode.highlightIndex,\n\t\t\t\tparent: null,\n\t\t\t\tchildren,\n\t\t\t\textra: elementNode.extra ?? {},\n\t\t\t}\n\t\t}\n\t}\n\n\t// 设置父节点引用\n\tconst setParentReferences = (node: TreeNode, parent: TreeNode | null = null) => {\n\t\tnode.parent = parent\n\t\tfor (const child of node.children) {\n\t\t\tsetParentReferences(child, node)\n\t\t}\n\t}\n\n\t// 构建根节点\n\tconst rootNode = buildTreeNode(flatTree.rootId)\n\tif (!rootNode) return ''\n\n\tsetParentReferences(rootNode)\n\n\t// 辅助函数:检查文本节点的父节点是否带高亮序号\n\tconst hasParentWithHighlightIndex = (node: TreeNode): boolean => {\n\t\tlet current = node.parent\n\t\twhile (current) {\n\t\t\tif (current.type === 'element' && current.highlightIndex !== undefined) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tcurrent = current.parent\n\t\t}\n\t\treturn false\n\t}\n\n\t// 辅助函数:检查父节点是否为最上层元素\n\t// const isParentTopElement = (node: TreeNode): boolean => {\n\t// \treturn node.parent?.type === 'element' && node.parent.isTopElement === true\n\t// }\n\n\t// 主处理函数\n\tconst processNode = (node: TreeNode, depth: number, result: string[]): void => {\n\t\tlet nextDepth = depth\n\t\tconst depthStr = '\\t'.repeat(depth)\n\n\t\tif (node.type === 'element') {\n\t\t\tconst isSemantic = keepSemanticTags && node.tagName && SEMANTIC_TAGS.has(node.tagName)\n\n\t\t\t// 带高亮序号的元素\n\t\t\tif (node.highlightIndex !== undefined) {\n\t\t\t\tnextDepth += 1\n\n\t\t\t\tconst text = getAllTextTillNextClickableElement(node)\n\t\t\t\tlet attributesHtmlStr = ''\n\n\t\t\t\tif (includeAttrs.length > 0 && node.attributes) {\n\t\t\t\t\tconst attributesToInclude = matchAttributes(node.attributes, includeAttrs)\n\n\t\t\t\t\t// 去除重复值(针对长度超过 5 个字符的属性)\n\t\t\t\t\tconst keys = Object.keys(attributesToInclude)\n\t\t\t\t\tif (keys.length > 1) {\n\t\t\t\t\t\tconst keysToRemove = new Set<string>()\n\t\t\t\t\t\tconst seenValues: Record<string, string> = {}\n\n\t\t\t\t\t\tfor (const key of keys) {\n\t\t\t\t\t\t\tconst value = attributesToInclude[key]\n\t\t\t\t\t\t\tif (value.length > 5) {\n\t\t\t\t\t\t\t\tif (value in seenValues) {\n\t\t\t\t\t\t\t\t\tkeysToRemove.add(key)\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tseenValues[value] = key\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (const key of keysToRemove) {\n\t\t\t\t\t\t\tdelete attributesToInclude[key]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// 如果 role 与标签名一致则移除\n\t\t\t\t\tif (attributesToInclude.role === node.tagName) {\n\t\t\t\t\t\tdelete attributesToInclude.role\n\t\t\t\t\t}\n\n\t\t\t\t\t// 移除与文本内容重复的属性\n\t\t\t\t\tconst attrsToRemoveIfTextMatches = ['aria-label', 'placeholder', 'title']\n\t\t\t\t\tfor (const attr of attrsToRemoveIfTextMatches) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tattributesToInclude[attr] &&\n\t\t\t\t\t\t\tattributesToInclude[attr].toLowerCase().trim() === text.toLowerCase().trim()\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tdelete attributesToInclude[attr]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Object.keys(attributesToInclude).length > 0) {\n\t\t\t\t\t\tattributesHtmlStr = Object.entries(attributesToInclude)\n\t\t\t\t\t\t\t.map(([key, value]) => `${key}=${capTextLength(value, 20)}`)\n\t\t\t\t\t\t\t.join(' ')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// 构建该行\n\t\t\t\tconst highlightIndicator = node.isNew\n\t\t\t\t\t? `*[${node.highlightIndex}]`\n\t\t\t\t\t: `[${node.highlightIndex}]`\n\t\t\t\tlet line = `${depthStr}${highlightIndicator}<${node.tagName ?? ''}`\n\n\t\t\t\tif (attributesHtmlStr) {\n\t\t\t\t\tline += ` ${attributesHtmlStr}`\n\t\t\t\t}\n\n\t\t\t\t/**\n\t\t\t\t * @edit scrollable 数据\n\t\t\t\t */\n\t\t\t\tif (node.extra) {\n\t\t\t\t\tif (node.extra.scrollable) {\n\t\t\t\t\t\tlet scrollDataText = ''\n\t\t\t\t\t\tif (node.extra.scrollData?.left)\n\t\t\t\t\t\t\tscrollDataText += `left=${node.extra.scrollData.left}, `\n\t\t\t\t\t\tif (node.extra.scrollData?.top) scrollDataText += `top=${node.extra.scrollData.top}, `\n\t\t\t\t\t\tif (node.extra.scrollData?.right)\n\t\t\t\t\t\t\tscrollDataText += `right=${node.extra.scrollData.right}, `\n\t\t\t\t\t\tif (node.extra.scrollData?.bottom)\n\t\t\t\t\t\t\tscrollDataText += `bottom=${node.extra.scrollData.bottom}`\n\n\t\t\t\t\t\tline += ` data-scrollable=\"${scrollDataText}\"`\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (text) {\n\t\t\t\t\tconst trimmedText = text.trim()\n\t\t\t\t\tif (!attributesHtmlStr) {\n\t\t\t\t\t\tline += ' '\n\t\t\t\t\t}\n\t\t\t\t\tline += `>${trimmedText}`\n\t\t\t\t} else if (!attributesHtmlStr) {\n\t\t\t\t\tline += ' '\n\t\t\t\t}\n\n\t\t\t\tline += ' />'\n\t\t\t\tresult.push(line)\n\t\t\t}\n\n\t\t\t// 语义化标签的特殊处理:\n\t\t\t// 即使不可交互,也可以保留它们以提供清晰的上下文\n\n\t\t\tconst emitSemantic = isSemantic && node.highlightIndex === undefined\n\t\t\t// 用于检查该标签是否为空\n\t\t\tconst mark = emitSemantic ? result.length : -1\n\n\t\t\tif (emitSemantic) {\n\t\t\t\tresult.push(`${depthStr}<${node.tagName}>`)\n\t\t\t\tnextDepth += 1\n\t\t\t}\n\n\t\t\tfor (const child of node.children) {\n\t\t\t\tprocessNode(child, nextDepth, result)\n\t\t\t}\n\n\t\t\tif (emitSemantic) {\n\t\t\t\t// 空标签应被移除\n\t\t\t\tif (result.length === mark + 1) {\n\t\t\t\t\tresult.pop()\n\t\t\t\t} else {\n\t\t\t\t\tresult.push(`${depthStr}</${node.tagName}>`)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (node.type === 'text') {\n\t\t\t// 仅当文本没有带高亮序号的父节点时才输出\n\t\t\tif (hasParentWithHighlightIndex(node)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tnode.parent &&\n\t\t\t\tnode.parent.type === 'element' &&\n\t\t\t\tnode.parent.isVisible &&\n\t\t\t\tnode.parent.isTopElement\n\t\t\t) {\n\t\t\t\tresult.push(`${depthStr}${node.text ?? ''}`)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst result: string[] = []\n\tprocessNode(rootNode, 0, result)\n\treturn result.join('\\n')\n}\n\n// 获取到下一个可点击元素为止的所有文本\nexport const getAllTextTillNextClickableElement = (node: TreeNode, maxDepth = -1): string => {\n\tconst textParts: string[] = []\n\n\t// 递归收集当前节点及其子树的文本内容,遇到其他高亮元素则跳过该分支\n\tconst collectText = (currentNode: TreeNode, currentDepth: number) => {\n\t\tif (maxDepth !== -1 && currentDepth > maxDepth) {\n\t\t\treturn\n\t\t}\n\n\t\t// 遇到其他高亮元素则跳过该分支(当前节点除外)\n\t\tif (\n\t\t\tcurrentNode.type === 'element' &&\n\t\t\tcurrentNode !== node &&\n\t\t\tcurrentNode.highlightIndex !== undefined\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\tif (currentNode.type === 'text' && currentNode.text) {\n\t\t\ttextParts.push(currentNode.text)\n\t\t} else if (currentNode.type === 'element') {\n\t\t\tfor (const child of currentNode.children) {\n\t\t\t\tcollectText(child, currentDepth + 1)\n\t\t\t}\n\t\t}\n\t}\n\n\tcollectText(node, 0)\n\treturn textParts.join('\\n').trim()\n}\n\n// 构建 高亮序号 -> 可交互元素节点 的映射表,供按序号定位/操作页面元素\nexport function getSelectorMap(flatTree: FlatDomTree): Map<number, InteractiveElementDomNode> {\n\tconst selectorMap = new Map<number, InteractiveElementDomNode>()\n\n\tconst keys = Object.keys(flatTree.map)\n\tfor (const key of keys) {\n\t\tconst node = flatTree.map[key]\n\t\tif (node.isInteractive && typeof node.highlightIndex === 'number') {\n\t\t\tselectorMap.set(node.highlightIndex, node as InteractiveElementDomNode)\n\t\t}\n\t}\n\n\treturn selectorMap\n}\n\n// 解析简化 HTML 文本,提取每行的序号与原文,构建 序号 -> 文本行 的映射\nexport function getElementTextMap(simplifiedHTML: string) {\n\tconst lines = simplifiedHTML\n\t\t.split('\\n')\n\t\t.map((line) => line.trim())\n\t\t.filter((line) => line.length > 0)\n\tconst elementTextMap = new Map<number, string>()\n\tfor (const line of lines) {\n\t\tconst regex = /^\\[(\\d+)\\]<[^>]+>([^<]*)/\n\t\tconst match = regex.exec(line)\n\t\tif (match) {\n\t\t\tconst index = parseInt(match[1], 10)\n\t\t\telementTextMap.set(index, line)\n\t\t}\n\t}\n\n\treturn elementTextMap\n}\n\n// 依次执行并清空全局注册的高亮清理函数\nexport function cleanUpHighlights() {\n\tconst cleanupFunctions = (window as any)._highlightCleanupFunctions || []\n\tfor (const cleanup of cleanupFunctions) {\n\t\tif (typeof cleanup === 'function') {\n\t\t\tcleanup()\n\t\t}\n\t}\n\n\t;(window as any)._highlightCleanupFunctions = []\n}\n\n// 监听 URL 的任何变化,立刻清空 highLights\nwindow.addEventListener('popstate', () => {\n\t// console.log('URL changed (popstate), highlights cleaned up.')\n\tcleanUpHighlights()\n})\nwindow.addEventListener('hashchange', () => {\n\t// console.log('URL changed (hashchange), highlights cleaned up.')\n\tcleanUpHighlights()\n})\nwindow.addEventListener('beforeunload', () => {\n\t// console.log('Page is unloading, highlights cleaned up.')\n\tcleanUpHighlights()\n})\n\n// 使用 Navigation API(如果浏览器支持)监听页面导航,导航时清理高亮\nconst navigation = (window as any).navigation\nif (navigation && typeof navigation.addEventListener === 'function') {\n\tnavigation.addEventListener('navigate', () => {\n\t\t// console.log('Navigation event detected, highlights cleaned up.')\n\t\tcleanUpHighlights()\n\t})\n} else {\n\t// 定时器\n\tlet currentUrl = window.location.href\n\tsetInterval(() => {\n\t\tif (window.location.href !== currentUrl) {\n\t\t\tcurrentUrl = window.location.href\n\t\t\t// console.log('URL changed (interval), highlights cleaned up.')\n\t\t\tcleanUpHighlights()\n\t\t}\n\t}, 500)\n}\n","// 获取当前页面的视口尺寸、页面总尺寸、滚动位置及分页信息,供 LLM 了解页面空间结构\nexport function getPageInfo() {\n\tconst viewport_width = window.innerWidth\n\tconst viewport_height = window.innerHeight\n\n\tconst page_width = Math.max(document.documentElement.scrollWidth, document.body.scrollWidth || 0)\n\tconst page_height = Math.max(\n\t\tdocument.documentElement.scrollHeight,\n\t\tdocument.body.scrollHeight || 0\n\t)\n\n\tconst scroll_x = window.scrollX || window.pageXOffset || document.documentElement.scrollLeft || 0\n\tconst scroll_y = window.scrollY || window.pageYOffset || document.documentElement.scrollTop || 0\n\n\tconst pixels_below = Math.max(0, page_height - (window.innerHeight + scroll_y))\n\tconst pixels_right = Math.max(0, page_width - (window.innerWidth + scroll_x))\n\n\treturn {\n\t\t// 当前视口尺寸\n\t\tviewport_width, // 视口宽度(px)\n\t\tviewport_height, // 视口高度(px)\n\n\t\t// 页面总尺寸\n\t\tpage_width, // 页面总宽度(px)\n\t\tpage_height, // 页面总高度(px)\n\n\t\t// 当前滚动位置\n\t\tscroll_x, // 水平滚动距离(px)\n\t\tscroll_y, // 垂直滚动距离(px)\n\n\t\tpixels_above: scroll_y, // 视口上方已滚过的像素数\n\t\tpixels_below, // 视口下方剩余像素数\n\n\t\tpages_above: viewport_height > 0 ? scroll_y / viewport_height : 0, // 已滚过的页数(以视口高度为单位)\n\t\tpages_below: viewport_height > 0 ? pixels_below / viewport_height : 0, // 下方剩余页数\n\t\ttotal_pages: viewport_height > 0 ? page_height / viewport_height : 0, // 页面总页数\n\n\t\tcurrent_page_position: scroll_y / Math.max(1, page_height - viewport_height), // 当前在页面中的位置(0~1)\n\n\t\tpixels_left: scroll_x, // 视口左侧已滚过的像素数\n\t\tpixels_right, // 视口右侧剩余像素数\n\t}\n}\n","import type { BrowserController } from '../BrowserController'\n\n// 查找常见的 React 根节点,并为其添加 data-page-agent-not-interactive 属性(标记为不可交互)\nexport function patchReact(_browserController: BrowserController) {\n\tconst reactRootElements = document.querySelectorAll(\n\t\t'[data-reactroot], [data-reactid], [data-react-checksum], #root, #app, [id^=\"root-\"], [id^=\"app-\"], #adex-wrapper, #adex-root'\n\t)\n\n\tfor (const element of reactRootElements) {\n\t\telement.setAttribute('data-page-agent-not-interactive', 'true')\n\t}\n}\n\n/**\n * @todo (较重,可能有漏判)交互检测:如果元素宽高等于 body 的 offsetWidth/Height,则视为根元素并标记为不可交互(React 经常把大量事件绑定在根元素上,导致误判为可交互)\n */\n","/**\n * 判断页面当前是否处于深色主题的综合函数。\n * 启发式检测,只覆盖常见模式,默认返回 false。\n */\nexport function isPageDark() {\n\ttry {\n\t\tif (hasDarkModeClass()) return true\n\t\tif (hasDarkModeDataAttribute()) return true\n\t\tif (isColorSchemeDark()) return true\n\t\tif (isBackgroundDark()) return true\n\t\tif (isMainContentBackgroundDark()) return true\n\t\tif (isTextColorLight()) return true\n\n\t\treturn false\n\t} catch (error) {\n\t\tconsole.warn('Error determining if page is dark:', error)\n\t\treturn false\n\t}\n}\n\n/**\n * 检查 html 或 body 元素上常见的深色模式 CSS 类名。\n */\nfunction hasDarkModeClass() {\n\tconst DEFAULT_DARK_MODE_CLASSES = ['dark', 'dark-mode', 'theme-dark', 'night', 'night-mode']\n\n\tconst htmlElement = document.documentElement\n\tconst bodyElement = document.body || document.documentElement // 某些情况下 body 可能为 null\n\n\t// 检查 <html> 和 <body> 上的类名\n\tfor (const className of DEFAULT_DARK_MODE_CLASSES) {\n\t\tif (htmlElement.classList.contains(className) || bodyElement?.classList.contains(className)) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n/**\n * 某些 UI 框架使用 data 属性来表示主题\n */\nfunction hasDarkModeDataAttribute() {\n\tconst htmlElement = document.documentElement\n\tconst bodyElement = document.body || document.documentElement // 某些情况下 body 可能为 null\n\n\tconst dataAttrs = ['data-theme', 'data-color-mode', 'data-bs-theme', 'data-mui-color-scheme']\n\tfor (const attr of dataAttrs) {\n\t\tconst bodyValue = bodyElement?.getAttribute(attr)\n\t\tconst htmlValue = htmlElement.getAttribute(attr)\n\n\t\tif (bodyValue?.toLowerCase() === 'dark' || htmlValue?.toLowerCase() === 'dark') {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n/**\n * 检查 CSS 的 `color-scheme` 属性和 `<meta name=\"color-scheme\">` 标签。\n * 只有 \"dark\"/\"only dark\" 才视为深色;\"light dark\" 存在歧义,忽略。\n */\nfunction isColorSchemeDark() {\n\t// 检查 <meta name=\"color-scheme\" content=\"dark\">\n\tconst meta = document.querySelector<HTMLMetaElement>('meta[name=\"color-scheme\"]')\n\tconst metaContent = meta?.content.toLowerCase()\n\tif (metaContent === 'dark' || metaContent === 'only dark') return true\n\n\t// 检查 :root 上计算出的 color-scheme CSS 属性\n\tconst rootStyle = window.getComputedStyle(document.documentElement)\n\tconst colorScheme = rootStyle.getPropertyValue('color-scheme').trim().toLowerCase()\n\treturn colorScheme === 'dark' || colorScheme === 'only dark'\n}\n\n/**\n * 检查 body 元素的背景色以判断页面是否为深色。\n */\nfunction isBackgroundDark() {\n\t// 同时检查 <html> 和 <body>,因为有些页面把颜色设置在 <html> 上\n\tconst htmlStyle = window.getComputedStyle(document.documentElement)\n\tconst bodyStyle = window.getComputedStyle(document.body || document.documentElement)\n\n\t// 获取背景颜色\n\tconst htmlBgColor = htmlStyle.backgroundColor\n\tconst bodyBgColor = bodyStyle.backgroundColor\n\n\t// body 的背景可能是透明的,此时应回退到 html 元素的背景。\n\tif (isColorDark(bodyBgColor)) {\n\t\treturn true\n\t} else if (bodyBgColor === 'transparent' || bodyBgColor.startsWith('rgba(0, 0, 0, 0)')) {\n\t\treturn isColorDark(htmlBgColor)\n\t}\n\n\treturn false\n}\n\n/**\n * 检查 body 上的文字颜色是否为浅色,浅色文字通常意味着深色背景。\n */\nfunction isTextColorLight() {\n\t/** 亮度(0-255)超过该值时认为 body 文字为浅色 */\n\tconst LIGHT_TEXT_LUMINANCE = 200\n\n\tconst bodyStyle = window.getComputedStyle(document.body || document.documentElement)\n\tconst luminance = getLuminance(bodyStyle.color)\n\n\t// 浅色文字亮度很高(例如深色背景上的白色文字)\n\treturn luminance !== null && luminance > LIGHT_TEXT_LUMINANCE\n}\n\n/**\n * 检查主要布局元素(#app、#root 等)的背景色。\n * 很多 SPA 会把内容渲染到某个容器中,该容器可能有自己的深色背景,\n * 而 <body> 仍然是透明的。\n */\nfunction isMainContentBackgroundDark() {\n\tconst { innerWidth: vw, innerHeight: vh } = window\n\tconst minArea = vw * vh * 0.5\n\n\tconst selectors = ['#app', '#root', '#__next']\n\tfor (const selector of selectors) {\n\t\tconst el = document.querySelector(selector)\n\t\tif (!el) continue\n\n\t\tconst rect = el.getBoundingClientRect()\n\t\tif (rect.width * rect.height < minArea) continue\n\n\t\tif (isColorDark(window.getComputedStyle(el).backgroundColor)) return true\n\t}\n\treturn false\n}\n\n// --- 工具函数 ---\n\n/**\n * 解析 RGB 或 RGBA 颜色字符串,返回包含 r、g、b 属性的对象。\n * @param {string} colorString - 例如 \"rgb(34, 34, 34)\" 或 \"rgba(0, 0, 0, 0.5)\"\n * @returns {{r: number, g: number, b: number}|null}\n */\nfunction parseRgbColor(colorString: string) {\n\tconst rgbMatch = /rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/.exec(colorString)\n\tif (!rgbMatch) {\n\t\treturn null // 不是合法的 rgb/rgba 字符串\n\t}\n\treturn {\n\t\tr: parseInt(rgbMatch[1]), // 红色分量 (0-255)\n\t\tg: parseInt(rgbMatch[2]), // 绿色分量 (0-255)\n\t\tb: parseInt(rgbMatch[3]), // 蓝色分量 (0-255)\n\t}\n}\n\n/**\n * 计算 CSS 颜色字符串的感知亮度(0-255)。\n * @param {string} colorString - 例如 \"rgb(50, 50, 50)\" 或 \"rgba(0, 0, 0, 0.5)\"\n * @returns {number|null} - 亮度值,若颜色为透明或无法解析则返回 null。\n */\nfunction getLuminance(colorString: string): number | null {\n\tif (!colorString || colorString === 'transparent' || colorString.startsWith('rgba(0, 0, 0, 0)')) {\n\t\treturn null // 透明色没有有意义的亮度\n\t}\n\n\tconst rgb = parseRgbColor(colorString)\n\tif (!rgb) {\n\t\treturn null // 无法解析该颜色\n\t}\n\n\t// 标准感知亮度公式\n\treturn 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b\n}\n\n/**\n * 根据计算出的亮度判断颜色是否为“深色”。\n * @param {string} colorString - CSS 颜色字符串(例如 \"rgb(50, 50, 50)\")。\n * @param {number} threshold - 0 到 255 之间的值。亮度低于该值的颜色视为深色。默认为 128。\n */\nfunction isColorDark(colorString: string, threshold = 128) {\n\tconst luminance = getLuminance(colorString)\n\treturn luminance !== null && luminance < threshold\n}\n",".wrapper {\n\tposition: fixed;\n\tinset: 0;\n\tz-index: 2147483641; /* 确保在所有元素之上,除了 Agent UI */\n\tcursor: wait;\n\toverflow: hidden;\n\n\tdisplay: none;\n}\n\n.wrapper.visible {\n\tdisplay: block;\n}\n","/* AI 光标样式 */\n.cursor {\n\tposition: absolute;\n\twidth: var(--cursor-size, 48px);\n\theight: var(--cursor-size, 48px);\n\tpointer-events: none;\n\tz-index: 10000;\n}\n\n.cursorBorder {\n\tposition: absolute;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: linear-gradient(45deg, rgb(57, 182, 255), rgb(189, 69, 251));\n\tmask-image: url(./cursor-border.svg);\n\tmask-size: 100% 100%;\n\tmask-repeat: no-repeat;\n\tmargin-left: -16px;\n\tmargin-top: -7px;\n}\n\n.cursorFilling {\n\tposition: absolute;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: url(./cursor-fill.svg);\n\tbackground-size: 100% 100%;\n\tbackground-repeat: no-repeat;\n\tmargin-left: -16px;\n\tmargin-top: -7px;\n}\n\n.cursorRipple {\n\tposition: absolute;\n\twidth: 100%;\n\theight: 100%;\n\tpointer-events: none;\n\tmargin-left: -50%;\n\tmargin-top: -50%;\n\n\t&::after {\n\t\tcontent: '';\n\t\topacity: 0;\n\t\tposition: absolute;\n\t\tinset: 0;\n\t\tborder: 4px solid rgba(57, 182, 255, 1);\n\t\tborder-radius: 50%;\n\t}\n}\n\n.cursor.clicking .cursorRipple::after {\n\tanimation: cursor-ripple 300ms ease-out forwards;\n}\n\n@keyframes cursor-ripple {\n\t0% {\n\t\ttransform: scale(0);\n\t\topacity: 1;\n\t}\n\t100% {\n\t\ttransform: scale(2);\n\t\topacity: 0;\n\t}\n}\n","import { Motion } from 'ai-motion'\n\nimport { isPageDark } from './checkDarkMode'\n\nimport styles from './SimulatorMask.module.css'\nimport cursorStyles from './cursor.module.css'\n\nexport class SimulatorMask extends EventTarget {\n\tshown: boolean = false // 是否已显示遮罩\n\twrapper = document.createElement('div') // 遮罩的根容器元素\n\tmotion: Motion | null = null // ai-motion 的动效实例,可能为 null(创建失败时)\n\n\t#disposed = false // 是否已销毁(dispose 后为 true)\n\n\t#cursor = document.createElement('div') // 模拟光标元素\n\n\t#currentCursorX = 0 // 光标当前 X 坐标\n\t#currentCursorY = 0 // 光标当前 Y 坐标\n\n\t#targetCursorX = 0 // 光标目标 X 坐标\n\t#targetCursorY = 0 // 光标目标 Y 坐标\n\n\t/**\n\t * 构造函数:初始化遮罩 DOM、创建 ai-motion 动效、拦截底层事件,并注册全局事件监听。\n\t */\n\tconstructor() {\n\t\tsuper()\n\n\t\tthis.wrapper.id = 'page-agent-runtime_simulator-mask'\n\t\tthis.wrapper.className = styles.wrapper\n\t\tthis.wrapper.setAttribute('data-browser-use-ignore', 'true')\n\t\tthis.wrapper.setAttribute('data-page-agent-ignore', 'true')\n\n\t\ttry {\n\t\t\tconst motion = new Motion({\n\t\t\t\tmode: isPageDark() ? 'dark' : 'light',\n\t\t\t\tborderWidth: 2,\n\t\t\t\tglowWidth: 50,\n\t\t\t\tstyles: { position: 'absolute', inset: '0' },\n\t\t\t\tskipGreeting: true, // 关闭 ai-motion 库控制台打印\n\t\t\t})\n\t\t\tthis.motion = motion\n\t\t\tthis.wrapper.appendChild(motion.element)\n\t\t\tmotion.autoResize(this.wrapper)\n\t\t} catch (e) {\n\t\t\tconsole.warn('[SimulatorMask] Motion overlay unavailable:', e)\n\t\t}\n\n\t\t// 拦截所有鼠标、键盘和滚轮事件,防止它们穿透到下层页面\n\t\t// 拦截所有鼠标、键盘和滚轮事件,防止它们穿透到下层页面\n\t\tthis.wrapper.addEventListener('click', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('mousedown', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('mouseup', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('mousemove', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('wheel', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('keydown', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('keyup', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\n\t\t// 创建 AI 光标\n\t\tthis.#createCursor()\n\t\t// this.show()\n\n\t\tdocument.body.appendChild(this.wrapper)\n\n\t\tthis.#moveCursorToTarget()\n\n\t\t// 全局事件\n\t\t// @note 遮罩应与代码的其他部分隔离。\n\t\t// 全局事件更容易管理和清理。\n\n\t\tconst movePointerToListener = (event: Event) => {\n\t\t\tconst { x, y } = (event as CustomEvent).detail // 目标坐标 { x, y }\n\t\t\tthis.setCursorPosition(x, y)\n\t\t}\n\t\tconst clickPointerListener = () => {\n\t\t\tthis.triggerClickAnimation()\n\t\t}\n\t\tconst enablePassThroughListener = () => {\n\t\t\tthis.wrapper.style.pointerEvents = 'none'\n\t\t}\n\t\tconst disablePassThroughListener = () => {\n\t\t\tthis.wrapper.style.pointerEvents = 'auto'\n\t\t}\n\n\t\twindow.addEventListener('PageAgent::MovePointerTo', movePointerToListener)\n\t\twindow.addEventListener('PageAgent::ClickPointer', clickPointerListener)\n\t\twindow.addEventListener('PageAgent::EnablePassThrough', enablePassThroughListener)\n\t\twindow.addEventListener('PageAgent::DisablePassThrough', disablePassThroughListener)\n\n\t\tthis.addEventListener('dispose', () => {\n\t\t\twindow.removeEventListener('PageAgent::MovePointerTo', movePointerToListener)\n\t\t\twindow.removeEventListener('PageAgent::ClickPointer', clickPointerListener)\n\t\t\twindow.removeEventListener('PageAgent::EnablePassThrough', enablePassThroughListener)\n\t\t\twindow.removeEventListener('PageAgent::DisablePassThrough', disablePassThroughListener)\n\t\t})\n\t}\n\n\t/** 创建模拟光标的 DOM 结构(包含波纹、填充层、边框层)。 */\n\t#createCursor() {\n\t\tthis.#cursor.className = cursorStyles.cursor\n\n\t\t// 创建波纹效果容器\n\t\t// 波纹效果容器\n\t\tconst rippleContainer = document.createElement('div')\n\t\trippleContainer.className = cursorStyles.cursorRipple\n\t\tthis.#cursor.appendChild(rippleContainer)\n\n\t\t// 创建填充层\n\t\t// 填充层\n\t\tconst fillingLayer = document.createElement('div')\n\t\tfillingLayer.className = cursorStyles.cursorFilling\n\t\tthis.#cursor.appendChild(fillingLayer)\n\n\t\t// 创建边框层\n\t\t// 边框层\n\t\tconst borderLayer = document.createElement('div')\n\t\tborderLayer.className = cursorStyles.cursorBorder\n\t\tthis.#cursor.appendChild(borderLayer)\n\n\t\tthis.wrapper.appendChild(this.#cursor)\n\t}\n\n\t/** 每帧将光标向目标位置移动 20% 距离,实现平滑跟随动画。 */\n\t#moveCursorToTarget() {\n\t\tif (this.#disposed) return\n\n\t\tconst newX = this.#currentCursorX + (this.#targetCursorX - this.#currentCursorX) * 0.2\n\t\tconst newY = this.#currentCursorY + (this.#targetCursorY - this.#currentCursorY) * 0.2\n\n\t\tconst xDistance = Math.abs(newX - this.#targetCursorX)\n\t\tif (xDistance > 0) {\n\t\t\tif (xDistance < 2) {\n\t\t\t\tthis.#currentCursorX = this.#targetCursorX\n\t\t\t} else {\n\t\t\t\tthis.#currentCursorX = newX\n\t\t\t}\n\t\t\tthis.#cursor.style.left = `${this.#currentCursorX}px`\n\t\t}\n\n\t\tconst yDistance = Math.abs(newY - this.#targetCursorY)\n\t\tif (yDistance > 0) {\n\t\t\tif (yDistance < 2) {\n\t\t\t\tthis.#currentCursorY = this.#targetCursorY\n\t\t\t} else {\n\t\t\t\tthis.#currentCursorY = newY\n\t\t\t}\n\t\t\tthis.#cursor.style.top = `${this.#currentCursorY}px`\n\t\t}\n\n\t\trequestAnimationFrame(() => this.#moveCursorToTarget())\n\t}\n\n\t/**\n\t * 设置光标的目标坐标(由外部通过事件触发调用)。\n\t * @param x - 目标 X 坐标\n\t * @param y - 目标 Y 坐标\n\t */\n\tsetCursorPosition(x: number, y: number) {\n\t\tif (this.#disposed) return\n\n\t\tthis.#targetCursorX = x\n\t\tthis.#targetCursorY = y\n\t}\n\n\t/** 触发一次光标点击动画(通过重放 clicking class 实现)。 */\n\ttriggerClickAnimation() {\n\t\tif (this.#disposed) return\n\n\t\tthis.#cursor.classList.remove(cursorStyles.clicking)\n\t\t// 强制触发重排以重新开始动画\n\t\tvoid this.#cursor.offsetHeight\n\t\tthis.#cursor.classList.add(cursorStyles.clicking)\n\t}\n\n\t/** 显示遮罩及模拟光标。 */\n\tshow() {\n\t\tif (this.shown || this.#disposed) return\n\n\t\tthis.shown = true\n\t\tthis.motion?.start()\n\t\tthis.motion?.fadeIn()\n\n\t\tthis.wrapper.classList.add(styles.visible)\n\n\t\t// 初始化光标位置\n\t\tthis.#currentCursorX = window.innerWidth / 2\n\t\tthis.#currentCursorY = window.innerHeight / 2\n\t\tthis.#targetCursorX = this.#currentCursorX\n\t\tthis.#targetCursorY = this.#currentCursorY\n\t\tthis.#cursor.style.left = `${this.#currentCursorX}px`\n\t\tthis.#cursor.style.top = `${this.#currentCursorY}px`\n\t}\n\n\t/** 隐藏遮罩及模拟光标。 */\n\thide() {\n\t\tif (!this.shown || this.#disposed) return\n\n\t\tthis.shown = false\n\t\tthis.motion?.fadeOut()\n\t\tthis.motion?.pause()\n\n\t\tthis.#cursor.classList.remove(cursorStyles.clicking)\n\n\t\tsetTimeout(() => {\n\t\t\tthis.wrapper.classList.remove(styles.visible)\n\t\t}, 800) // 匹配动画的持续时间\n\t}\n\n\t/** 销毁遮罩:释放动效、移除 DOM 并触发 dispose 事件。 */\n\tdispose() {\n\t\tthis.#disposed = true\n\t\tthis.motion?.dispose()\n\t\tthis.wrapper.remove()\n\t\tthis.dispatchEvent(new Event('dispose'))\n\t}\n}\n","/**\n * 版权所有 (C) 2025 Alibaba Group Holding Limited\n * 保留所有权利。\n *\n * BrowserController - 管理 DOM 操作和元素交互。\n * 设计上独立于 LLM,可在单元测试中直接测试。\n * 所有公开方法均为异步,以支持潜在的远程调用。\n */\nimport {\n\tclickElement,\n\tgetElementByIndex,\n\tinputTextElement,\n\tscrollHorizontally,\n\tscrollVertically,\n\tselectOptionElement,\n} from './actions'\nimport * as dom from './dom'\nimport type { BrowserState } from './dom'\nimport type { FlatDomTree, InteractiveElementDomNode } from './dom/dom_tree/type'\nimport { getPageInfo } from './dom/getPageInfo'\nimport { patchReact } from './patches/react'\nimport { isAnchorElement } from './utils'\n\nexport type { BrowserState } from './dom'\n\n/**\n * BrowserController 的配置\n */\nexport interface BrowserControllerConfig extends dom.DomConfig {\n\t/** 操作期间启用视觉遮罩层(默认:false) */\n\tenableMask?: boolean\n}\n\ninterface ActionResult {\n\tsuccess: boolean // 操作是否成功\n\tmessage: string // 操作结果描述消息\n}\n\n/**\n * BrowserController 管理 DOM 状态和元素交互。\n * 它为所有 DOM 操作提供异步方法,并保持状态隔离。\n *\n * @lifecycle\n * - beforeUpdate: 在 DOM 树更新之前触发。\n * - afterUpdate: 在 DOM 树更新之后触发。\n */\nexport class BrowserController extends EventTarget {\n\tprivate config: BrowserControllerConfig // 控制器配置(含 DOM 相关配置及遮罩开关)\n\n\t/** 对应 browser-use 中的 eval_page */\n\tprivate flatTree: FlatDomTree | null = null\n\n\t/**\n\t * 所有已索引的高亮交互元素\n\t * 对应 browser-use 中的 DOMState.selector_map\n\t */\n\tprivate selectorMap = new Map<number, InteractiveElementDomNode>()\n\n\t/** 索引 -> 元素文本描述映射 */\n\tprivate elementTextMap = new Map<number, string>()\n\n\t/**\n\t * 供 LLM 消费的简化 HTML。\n\t * 对应 browser-use 中的 clickable_elements_to_string\n\t */\n\tprivate simplifiedHTML = '<EMPTY>'\n\n\t/** 树最后一次更新的时间 */\n\tprivate lastTimeUpdate = 0\n\n\t/** 树是否至少被索引过一次 */\n\tprivate isIndexed = false\n\n\t/** 用于在自动化期间阻止用户交互的视觉遮罩层 */\n\tprivate mask: InstanceType<typeof import('./visual/SimulatorMask').SimulatorMask> | null = null\n\t/** mask 异步初始化完成的 Promise,用于在调用前等待初始化完成 */\n\tprivate maskReady: Promise<void> | null = null\n\t/** 是否已释放资源 */\n\tprivate disposed = false\n\n\tconstructor(config: BrowserControllerConfig = {}) {\n\t\tsuper()\n\n\t\t// 保存控制器配置\n\t\tthis.config = config\n\n\t\tpatchReact(this)\n\n\t\t// 若配置开启遮罩,则初始化遮罩层\n\t\tif (config.enableMask) this.initMask()\n\t}\n\n\t/**\n\t * 异步初始化遮罩层(动态导入以避免在 Node 中加载 CSS)\n\t */\n\tinitMask() {\n\t\tif (this.disposed || this.maskReady !== null) return\n\t\tthis.maskReady = (async () => {\n\t\t\tconst { SimulatorMask } = await import('./visual/SimulatorMask')\n\t\t\tif (this.disposed) return\n\n\t\t\tconst mask = new SimulatorMask()\n\t\t\tif (this.disposed) {\n\t\t\t\tmask.dispose()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.mask = mask\n\t\t})()\n\t}\n\t// ======= 状态查询 =======\n\n\t/**\n\t * 获取当前页面 URL\n\t */\n\tasync getCurrentUrl(): Promise<string> {\n\t\treturn window.location.href\n\t}\n\n\t/**\n\t * 获取树最后一次更新的时间戳\n\t */\n\tasync getLastUpdateTime(): Promise<number> {\n\t\treturn this.lastTimeUpdate\n\t}\n\n\t/**\n\t * 获取供 LLM 消费的结构化浏览器状态。\n\t * 自动调用 updateTree() 刷新 DOM 状态。\n\t */\n\tasync getBrowserState(): Promise<BrowserState> {\n\t\tconst url = window.location.href // 当前页面 URL\n\t\tconst title = document.title // 当前页面标题\n\t\tconst pi = getPageInfo() // 页面尺寸与滚动位置等信息\n\t\tconst viewportExpansion = dom.resolveViewportExpansion(this.config.viewportExpansion) // 解析视口扩展配置\n\n\t\tawait this.updateTree()\n\n\t\tconst content = this.simplifiedHTML // 供 LLM 使用的简化 HTML\n\n\t\t// 构建页头:页面信息 + 滚动位置提示\n\t\tconst titleLine = `Current Page: [${title}](${url})`\n\n\t\tconst pageInfoLine = `Page info: ${pi.viewport_width}x${pi.viewport_height}px viewport, ${pi.page_width}x${pi.page_height}px total page size, ${pi.pages_above.toFixed(1)} pages above, ${pi.pages_below.toFixed(1)} pages below, ${pi.total_pages.toFixed(1)} total pages, at ${(pi.current_page_position * 100).toFixed(0)}% of page`\n\n\t\tconst elementsLabel =\n\t\t\tviewportExpansion === -1\n\t\t\t\t? 'Interactive elements from top layer of the current page (full page):'\n\t\t\t\t: 'Interactive elements from top layer of the current page inside the viewport:'\n\n\t\tconst hasContentAbove = pi.pixels_above > 4\n\t\tconst scrollHintAbove =\n\t\t\thasContentAbove && viewportExpansion !== -1\n\t\t\t\t? `... ${pi.pixels_above} pixels above (${pi.pages_above.toFixed(1)} pages) - scroll to see more ...`\n\t\t\t\t: '[Start of page]'\n\n\t\tconst header = `${titleLine}\\n${pageInfoLine}\\n\\n${elementsLabel}\\n\\n${scrollHintAbove}`\n\n\t\t// 构建页脚:滚动位置提示\n\t\tconst hasContentBelow = pi.pixels_below > 4\n\t\tconst footer =\n\t\t\thasContentBelow && viewportExpansion !== -1\n\t\t\t\t? `... ${pi.pixels_below} pixels below (${pi.pages_below.toFixed(1)} pages) - scroll to see more ...`\n\t\t\t\t: '[End of page]'\n\n\t\treturn {\n\t\t\turl, // 当前页面 URL\n\t\t\ttitle, // 当前页面标题\n\t\t\theader, // 页头信息(页面信息 + 上方滚动提示)\n\t\t\tcontent, // 供 LLM 使用的简化 HTML\n\t\t\tfooter, // 页脚信息(下方滚动提示)\n\t\t}\n\t}\n\n\t// ======= DOM 树操作 =======\n\n\t/**\n\t * 更新 DOM 树,返回供 LLM 使用的简化 HTML。\n\t * 这是刷新页面状态的主要方法。\n\t * 若已启用遮罩,DOM 提取期间会自动绕过遮罩。\n\t */\n\tasync updateTree(): Promise<string> {\n\t\t// 更新前派发事件,供外部监听\n\t\tthis.dispatchEvent(new Event('beforeUpdate'))\n\n\t\t// 记录本次更新树的时间戳\n\t\tthis.lastTimeUpdate = Date.now()\n\n\t\t// 临时绕过遮罩以允许进行 DOM 提取\n\t\tif (this.mask) {\n\t\t\tthis.mask.wrapper.style.pointerEvents = 'none'\n\t\t}\n\n\t\tdom.cleanUpHighlights()\n\n\t\tconst blacklist = [\n\t\t\t...(this.config.interactiveBlacklist || []), // 配置中指定的交互黑名单\n\t\t\t...Array.from(document.querySelectorAll('[data-page-agent-not-interactive]')), // 页面中标记为不可交互的元素\n\t\t]\n\n\t\t// 生成扁平 DOM 树\n\t\tthis.flatTree = dom.getFlatTree({\n\t\t\t...this.config,\n\t\t\tinteractiveBlacklist: blacklist,\n\t\t})\n\n\t\t// 将扁平 DOM 树转换为供 LLM 使用的简化 HTML\n\t\tthis.simplifiedHTML = dom.flatTreeToString(\n\t\t\tthis.flatTree,\n\t\t\tthis.config.includeAttributes,\n\t\t\tthis.config.keepSemanticTags\n\t\t)\n\n\t\tthis.selectorMap.clear()\n\t\tthis.selectorMap = dom.getSelectorMap(this.flatTree) // 生成索引 -> 交互元素节点的映射\n\n\t\tthis.elementTextMap.clear()\n\t\tthis.elementTextMap = dom.getElementTextMap(this.simplifiedHTML) // 生成索引 -> 元素文本描述的映射\n\n\t\t// 标记为已索引 — 现在允许对元素执行操作\n\t\tthis.isIndexed = true\n\n\t\t// 恢复遮罩拦截\n\t\tif (this.mask) {\n\t\t\tthis.mask.wrapper.style.pointerEvents = 'auto'\n\t\t}\n\n\t\t// 更新完成后派发事件,供外部监听\n\t\tthis.dispatchEvent(new Event('afterUpdate'))\n\n\t\treturn this.simplifiedHTML\n\t}\n\n\t/**\n\t * 清理所有元素高亮\n\t */\n\tasync cleanUpHighlights(): Promise<void> {\n\t\tconsole.log('[BrowserController] cleanUpHighlights')\n\t\tdom.cleanUpHighlights()\n\t}\n\n\t// ======= 元素操作 =======\n\n\t/**\n\t * 在任何基于索引的操作之前确保树已被索引。\n\t * 若尚未调用 updateTree() 则抛出异常。\n\t */\n\tprivate assertIndexed(): void {\n\t\tif (!this.isIndexed) {\n\t\t\tthrow new Error('DOM tree not indexed yet. Can not perform actions on elements.')\n\t\t}\n\t}\n\n\t/**\n\t * 根据索引点击元素\n\t */\n\tasync clickElement(index: number): Promise<ActionResult> {\n\t\ttry {\n\t\t\tthis.assertIndexed()\n\t\t\tconst element = getElementByIndex(this.selectorMap, index) // 根据索引获取目标元素\n\t\t\tconst elemText = this.elementTextMap.get(index) // 获取元素文本描述(用于消息展示)\n\n\t\t\tawait clickElement(element)\n\n\t\t\t// 处理在新标签页打开的链接\n\t\t\t// 处理 _blank 新标签页链接\n\t\t\tif (isAnchorElement(element) && element.target === '_blank') {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true, // 操作成功\n\t\t\t\t\tmessage: `✅ Clicked element (${elemText ?? index}). ⚠️ Link opened in a new tab.`, // 成功消息\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsuccess: true, // 操作成功\n\t\t\t\tmessage: `✅ Clicked element (${elemText ?? index}).`, // 成功消息\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false, // 操作失败\n\t\t\t\tmessage: `❌ Failed to click element: ${error}`, // 失败消息\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 根据索引向元素输入文本\n\t */\n\tasync inputText(index: number, text: string): Promise<ActionResult> {\n\t\ttry {\n\t\t\tthis.assertIndexed()\n\t\t\tconst element = getElementByIndex(this.selectorMap, index) // 根据索引获取目标元素\n\t\t\tconst elemText = this.elementTextMap.get(index) // 获取元素文本描述(用于消息展示)\n\t\t\tawait inputTextElement(element, text)\n\n\t\t\treturn {\n\t\t\t\tsuccess: true, // 操作成功\n\t\t\t\tmessage: `✅ Input text (${text}) into element (${elemText ?? index}).`, // 成功消息\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false, // 操作失败\n\t\t\t\tmessage: `❌ Failed to input text: ${error}`, // 失败消息\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 根据索引和选项文本在下拉框中选中选项\n\t */\n\tasync selectOption(index: number, optionText: string): Promise<ActionResult> {\n\t\ttry {\n\t\t\tthis.assertIndexed()\n\t\t\tconst element = getElementByIndex(this.selectorMap, index) // 根据索引获取目标元素\n\t\t\tconst elemText = this.elementTextMap.get(index) // 获取元素文本描述(用于消息展示)\n\t\t\tawait selectOptionElement(element as HTMLSelectElement, optionText)\n\n\t\t\treturn {\n\t\t\t\tsuccess: true, // 操作成功\n\t\t\t\tmessage: `✅ Selected option (${optionText}) in element (${elemText ?? index}).`, // 成功消息\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false, // 操作失败\n\t\t\t\tmessage: `❌ Failed to select option: ${error}`, // 失败消息\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 垂直滚动\n\t */\n\tasync scroll(options: {\n\t\tdown: boolean // 是否向下滚动\n\t\tnumPages: number // 滚动的页数(在未指定 pixels 时作为滚动量基准)\n\t\tpixels?: number // 可选的滚动像素数,优先于 numPages\n\t\tindex?: number // 可选的元素索引;指定时滚动该元素容器而非整页\n\t}): Promise<ActionResult> {\n\t\ttry {\n\t\t\tconst { down, numPages, pixels, index } = options\n\n\t\t\tthis.assertIndexed()\n\n\t\t\tconst scrollAmount = (pixels ?? numPages * window.innerHeight) * (down ? 1 : -1)\n\n\t\t\tconst element = index !== undefined ? getElementByIndex(this.selectorMap, index) : null\n\n\t\t\tconst message = await scrollVertically(scrollAmount, element)\n\n\t\t\treturn {\n\t\t\t\tsuccess: true, // 操作成功\n\t\t\t\tmessage, // 滚动结果描述消息\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false, // 操作失败\n\t\t\t\tmessage: `❌ Failed to scroll: ${error}`, // 失败消息\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 水平滚动\n\t */\n\tasync scrollHorizontally(options: {\n\t\tright: boolean // 是否向右滚动\n\t\tpixels: number // 滚动的像素数\n\t\tindex?: number // 可选的元素索引;指定时滚动该元素容器而非整页\n\t}): Promise<ActionResult> {\n\t\ttry {\n\t\t\tconst { right, pixels, index } = options\n\n\t\t\tthis.assertIndexed()\n\n\t\t\tconst scrollAmount = pixels * (right ? 1 : -1)\n\n\t\t\tconst element = index !== undefined ? getElementByIndex(this.selectorMap, index) : null\n\n\t\t\tconst message = await scrollHorizontally(scrollAmount, element)\n\n\t\t\treturn {\n\t\t\t\tsuccess: true, // 操作成功\n\t\t\t\tmessage, // 滚动结果描述消息\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false, // 操作失败\n\t\t\t\tmessage: `❌ Failed to scroll horizontally: ${error}`, // 失败消息\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 在页面上执行任意 JavaScript。\n\t * 可选的 `signal` 会暴露到脚本作用域中,使协作代码能在任务被中止时及时退出。\n\t */\n\tasync executeJavascript(script: string, signal?: AbortSignal): Promise<ActionResult> {\n\t\ttry {\n\t\t\t// 将脚本包进 async 函数以支持 await,并把 signal 暴露到脚本作用域\n\t\t\tconst asyncFunction = (0, eval)(`(async (signal) => { ${script} })`)\n\t\t\tconst result = await asyncFunction(signal)\n\t\t\treturn {\n\t\t\t\tsuccess: true, // 操作成功\n\t\t\t\tmessage: `✅ Executed JavaScript. Result: ${result}`, // 成功消息\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false, // 操作失败\n\t\t\t\tmessage: `❌ Error executing JavaScript: ${error}`, // 失败消息\n\t\t\t}\n\t\t}\n\t}\n\n\t// ======= 遮罩操作 =======\n\n\t/**\n\t * 显示视觉遮罩层。\n\t * 仅在遮罩初始化完成后可用。\n\t */\n\tasync showMask(): Promise<void> {\n\t\tif (this.disposed) return\n\t\tawait this.maskReady\n\t\tif (this.disposed) return\n\t\tthis.mask?.show()\n\t}\n\n\t/**\n\t * 隐藏视觉遮罩层。\n\t * 仅在遮罩初始化完成后可用。\n\t */\n\tasync hideMask(): Promise<void> {\n\t\tif (this.disposed) return\n\t\tawait this.maskReady\n\t\tif (this.disposed) return\n\t\tthis.mask?.hide()\n\t}\n\n\t/**\n\t * 释放资源并清理\n\t */\n\tdispose(): void {\n\t\tif (this.disposed) return\n\t\tthis.disposed = true\n\n\t\tconst errors: unknown[] = [] // 收集清理过程中出现的错误\n\t\tconst cleanUp = (callback: () => void) => {\n\t\t\ttry {\n\t\t\t\tcallback()\n\t\t\t} catch (error: unknown) {\n\t\t\t\terrors.push(error)\n\t\t\t}\n\t\t}\n\n\t\tcleanUp(() => dom.cleanUpHighlights()) // 清理元素高亮\n\t\tcleanUp(() => {\n\t\t\tthis.flatTree = null\n\t\t\tthis.selectorMap.clear()\n\t\t\tthis.elementTextMap.clear()\n\t\t\tthis.simplifiedHTML = '<EMPTY>'\n\t\t\tthis.isIndexed = false\n\t\t})\n\t\tcleanUp(() => this.mask?.dispose()) // 释放遮罩层\n\t\tthis.mask = null\n\n\t\tif (errors.length === 1) throw errors[0]\n\t\tif (errors.length > 1) throw new AggregateError(errors, 'BrowserController disposal failed')\n\t}\n}\n\nexport * from './actions'\n","// 英文翻译(基准/参考语言)\nconst enUS = {\n\tui: {\n\t\tready: 'Ready', // 就绪状态提示\n\t\tthinking: 'Thinking...', // 思考中提示\n\t\ttaskInput: 'Enter new task, describe steps in detail, press Enter to submit', // 任务输入框占位提示\n\t\tuserAnswerPrompt: 'Please answer the question above, press Enter to submit', // 用户回答问题时的输入框提示\n\t\ttaskTerminated: 'Task terminated', // 任务已终止\n\t\ttaskCompleted: 'Task completed', // 任务已完成\n\t\tuserAnswer: 'User answer: {{input}}', // 用户回答展示模板\n\t\tquestion: 'Question: {{question}}', // 问题展示模板\n\t\twaitingPlaceholder: 'Waiting for task to start...', // 等待任务开始的占位文本\n\t\tstop: 'Stop', // 停止按钮文本\n\t\tclose: 'Close', // 关闭按钮文本\n\t\texpand: 'Expand history', // 展开历史提示\n\t\tcollapse: 'Collapse history', // 收起历史提示\n\t\tstep: 'Step {{number}}', // 步骤编号模板\n\t\tattachButtonTitle: 'Attach files', // 附件按钮的 title 提示\n\t\tattachTooMany: 'You can attach up to {{max}} files', // 附件数量超限提示\n\t\tattachTooLarge: '\"{{name}}\" exceeds the {{maxMB}}MB size limit', // 附件体积超限提示\n\t\tattachUnsupportedType: '\"{{name}}\" is not a supported file type', // 附件类型不支持提示\n\t\tattachmentsLabel: '📎 Attachments: {{names}}', // 任务卡片中展示的附件文件名列表\n\t\ttools: {\n\t\t\tclicking: 'Clicking element [{{index}}]...', // 正在点击元素\n\t\t\tinputting: 'Inputting text to element [{{index}}]...', // 正在向元素输入文本\n\t\t\tselecting: 'Selecting option \"{{text}}\"...', // 正在选择选项\n\t\t\tscrolling: 'Scrolling page...', // 正在滚动页面\n\t\t\twaiting: 'Waiting {{seconds}} seconds...', // 正在等待\n\t\t\taskingUser: 'Asking user...', // 正在询问用户\n\t\t\tdone: 'Task done', // 任务结束\n\t\t\tclicked: '🖱️ Clicked element [{{index}}]', // 已点击元素\n\t\t\tinputted: '⌨️ Inputted text \"{{text}}\"', // 已输入文本\n\t\t\tselected: '☑️ Selected option \"{{text}}\"', // 已选择选项\n\t\t\tscrolled: '🛞 Page scrolled', // 页面已滚动\n\t\t\twaited: '⌛️ Wait completed', // 等待完成\n\t\t\texecuting: 'Executing {{toolName}}...', // 正在执行工具\n\t\t\tresultSuccess: 'success', // 工具执行结果:成功\n\t\t\tresultFailure: 'failed', // 工具执行结果:失败\n\t\t\tresultError: 'error', // 工具执行结果:错误\n\t\t},\n\t\terrors: {\n\t\t\telementNotFound: 'No interactive element found at index {{index}}', // 未找到交互元素\n\t\t\ttaskRequired: 'Task description is required', // 任务描述不能为空\n\t\t\texecutionFailed: 'Task execution failed', // 任务执行失败\n\t\t\tnotInputElement: 'Element is not an input or textarea', // 元素不是输入框或文本域\n\t\t\tnotSelectElement: 'Element is not a select element', // 元素不是选择框\n\t\t\toptionNotFound: 'Option \"{{text}}\" not found', // 未找到选项\n\t\t},\n\t},\n} as const\n\n// 中文翻译(必须与 enUS 结构一致)\nconst zhCN = {\n\tui: {\n\t\tready: '准备就绪', // 就绪状态提示\n\t\tthinking: '正在思考...', // 思考中提示\n\t\ttaskInput: '输入新任务,详细描述步骤,回车提交', // 任务输入框占位提示\n\t\tuserAnswerPrompt: '请回答上面问题,回车提交', // 用户回答问题时的输入框提示\n\t\ttaskTerminated: '任务已终止', // 任务已终止\n\t\ttaskCompleted: '任务结束', // 任务已完成\n\t\tuserAnswer: '用户回答: {{input}}', // 用户回答展示模板\n\t\tquestion: '询问: {{question}}', // 问题展示模板\n\t\twaitingPlaceholder: '等待任务开始...', // 等待任务开始的占位文本\n\t\tstop: '终止', // 停止按钮文本\n\t\tclose: '关闭', // 关闭按钮文本\n\t\texpand: '展开历史', // 展开历史提示\n\t\tcollapse: '收起历史', // 收起历史提示\n\t\tstep: '步骤 {{number}}', // 步骤编号模板\n\t\tattachButtonTitle: '添加附件', // 附件按钮的 title 提示\n\t\tattachTooMany: '最多可添加 {{max}} 个文件', // 附件数量超限提示\n\t\tattachTooLarge: '\"{{name}}\" 超过 {{maxMB}}MB 大小限制', // 附件体积超限提示\n\t\tattachUnsupportedType: '\"{{name}}\" 不是支持的文件类型', // 附件类型不支持提示\n\t\tattachmentsLabel: '📎 附件: {{names}}', // 任务卡片中展示的附件文件名列表\n\t\ttools: {\n\t\t\tclicking: '正在点击元素 [{{index}}]...', // 正在点击元素\n\t\t\tinputting: '正在输入文本到元素 [{{index}}]...', // 正在向元素输入文本\n\t\t\tselecting: '正在选择选项 \"{{text}}\"...', // 正在选择选项\n\t\t\tscrolling: '正在滚动页面...', // 正在滚动页面\n\t\t\twaiting: '等待 {{seconds}} 秒...', // 正在等待\n\t\t\taskingUser: '正在询问用户...', // 正在询问用户\n\t\t\tdone: '结束任务', // 任务结束\n\t\t\tclicked: '🖱️ 已点击元素 [{{index}}]', // 已点击元素\n\t\t\tinputted: '⌨️ 已输入文本 \"{{text}}\"', // 已输入文本\n\t\t\tselected: '☑️ 已选择选项 \"{{text}}\"', // 已选择选项\n\t\t\tscrolled: '🛞 页面滚动完成', // 页面已滚动\n\t\t\twaited: '⌛️ 等待完成', // 等待完成\n\t\t\texecuting: '正在执行 {{toolName}}...', // 正在执行工具\n\t\t\tresultSuccess: '成功', // 工具执行结果:成功\n\t\t\tresultFailure: '失败', // 工具执行结果:失败\n\t\t\tresultError: '错误', // 工具执行结果:错误\n\t\t},\n\t\terrors: {\n\t\t\telementNotFound: '未找到索引为 {{index}} 的交互元素', // 未找到交互元素\n\t\t\ttaskRequired: '任务描述不能为空', // 任务描述不能为空\n\t\t\texecutionFailed: '任务执行失败', // 任务执行失败\n\t\t\tnotInputElement: '元素不是输入框或文本域', // 元素不是输入框或文本域\n\t\t\tnotSelectElement: '元素不是选择框', // 元素不是选择框\n\t\t\toptionNotFound: '未找到选项 \"{{text}}\"', // 未找到选项\n\t\t},\n\t},\n} as const\n\n// 类型定义:根据英文基础结构生成(但叶子值为 string 类型)\ntype DeepStringify<T> = {\n\t[K in keyof T]: T[K] extends string ? string : T[K] extends object ? DeepStringify<T[K]> : T[K] // 递归将叶子值展开为 string\n}\n\n/** 翻译数据结构:由英文基础结构推导出的全字符串类型 */\nexport type TranslationSchema = DeepStringify<typeof enUS>\n\n// 工具类型:提取翻译对象的所有嵌套路径\ntype NestedKeyOf<ObjectType extends object> = {\n\t[Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object\n\t\t? `${Key}` | `${Key}.${NestedKeyOf<ObjectType[Key]>}`\n\t\t: `${Key}`\n}[keyof ObjectType & (string | number)]\n\n/** 所有可能的翻译键路径(如 \"ui.tools.done\") */\nexport type TranslationKey = NestedKeyOf<TranslationSchema>\n\n/** 翻译占位符参数类型:键为参数名,值为字符串或数字 */\nexport type TranslationParams = Record<string, string | number>\n\n/** 语言包集合:键为语言代码,值为对应翻译表 */\nexport const locales = {\n\t'en-US': enUS, // 英文翻译\n\t'zh-CN': zhCN, // 中文翻译\n} as const\n\n/** 受支持的语言代码类型 */\nexport type SupportedLanguage = keyof typeof locales\n","import {\n\ttype SupportedLanguage,\n\ttype TranslationKey,\n\ttype TranslationParams,\n\ttype TranslationSchema,\n\tlocales,\n} from './locales'\n\nexport class I18n {\n\tprivate language: SupportedLanguage\n\tprivate translations: TranslationSchema\n\n\tconstructor(language: SupportedLanguage = 'en-US') { // language 语言代码,默认 'en-US'\n\t\tthis.language = language in locales ? language : 'en-US' // 兜底:不支持的语种回退到 en-US\n\t\tthis.translations = locales[this.language] // 当前语言的翻译表\n\t}\n\n\t// 类型安全的翻译方法\n\tt(key: TranslationKey, params?: TranslationParams): string {\n\t\tconst value = this.getNestedValue(this.translations, key) // 根据点分路径取出翻译文本\n\t\tif (!value) {\n\t\t\tconsole.warn(`Translation key \"${key}\" not found for language \"${this.language}\"`)\n\t\t\treturn key\n\t\t}\n\n\t\tif (params) {\n\t\t\treturn this.interpolate(value, params)\n\t\t}\n\t\treturn value\n\t}\n\n\t/** 根据点分路径从翻译对象中取值 */\n\tprivate getNestedValue(obj: any, path: string): string | undefined {\n\t\treturn path.split('.').reduce((current, key) => current?.[key], obj)\n\t}\n\n\t/** 将模板中的 {{key}} 占位符替换为参数值 */\n\tprivate interpolate(template: string, params: TranslationParams): string {\n\t\treturn template.replace(/\\{\\{(\\w+)\\}\\}/g, (match, key) => {\n\t\t\t// 使用 != null 同时判断 null 和 undefined,允许空字符串通过\n\t\t\treturn params[key] != null ? params[key].toString() : match\n\t\t})\n\t}\n\n\t/** 获取当前语言代码 */\n\tgetLanguage(): SupportedLanguage {\n\t\treturn this.language\n\t}\n}\n\n// 导出类型和实例创建函数\nexport type { TranslationKey, SupportedLanguage, TranslationParams }\nexport { locales }\n","/** 截断字符串,超过最大长度时以省略号结尾 */\nexport function truncate(text: string, maxLength: number): string {\n\tif (text.length > maxLength) {\n\t\treturn text.substring(0, maxLength) + '...'\n\t}\n\treturn text\n}\n\n/**\n * 转义 HTML 特殊字符,防止 XSS 及渲染问题\n */\nexport function escapeHtml(text: string): string {\n\treturn text\n\t\t.replace(/&/g, '&')\n\t\t.replace(/</g, '<')\n\t\t.replace(/>/g, '>')\n\t\t.replace(/\"/g, '"')\n\t\t.replace(/'/g, ''')\n}\n",".wrapper {\n\tposition: fixed;\n\tbottom: 100px;\n\tleft: 50%;\n\ttransform: translateX(-50%) translateY(20px);\n\topacity: 0;\n\tz-index: 2147483642; /* 比 SimulatorMask 高一层 */\n\tbox-sizing: border-box;\n\n\t/* 普通流布局:历史弹窗、面板依次纵向排列,靠子元素自身尺寸撑开,\n\t * 不再用 position:absolute + 手算偏移量拼位置(这套算法之前连续出过两次 bug:\n\t * 弹窗被状态条盖住、状态条运行态时下方角没跟着圆)。 */\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\n\t* {\n\t\tbox-sizing: border-box;\n\t}\n\n\t/* 宽度取窗口 40%,用 clamp 防止超窄/超宽窗口下过小或过大 */\n\t--width: clamp(280px, 40vw, 640px);\n\t--height: 40px; /* 状态条高度 */\n\t--input-height: 80px; /* 输入区高度 */\n\t--border-radius: 16px;\n\n\t--side-space: 12px; /* 控制栏两侧的间距 */\n\t--history-width: calc(var(--width) - var(--side-space) * 2);\n\n\t--color-1: rgb(57, 182, 255);\n\t--color-2: rgb(189, 69, 251);\n\t--color-3: rgb(255, 87, 51);\n\t--color-4: rgb(255, 214, 0);\n\n\twidth: var(--width);\n\n\ttransition:\n\t\topacity 0.3s ease-in-out,\n\t\ttransform 0.3s ease-in-out;\n\n\t/* 响应式设计 */\n\t@media (max-width: 480px) {\n\t\twidth: calc(100vw - 40px);\n\t\t--width: calc(100vw - 40px);\n\t}\n}\n\n@keyframes mask-running {\n\tfrom {\n\t\ttransform: translateX(-100%);\n\t}\n\tto {\n\t\ttransform: translateX(100%);\n\t}\n}\n\n/* 状态条 + 输入区的外层容器;.background 光效挂在这一层,.shellInner 负责裁出圆角 */\n.shell {\n\tposition: relative;\n\twidth: 100%;\n}\n\n.background {\n\tposition: absolute;\n\tinset: -2px;\n\tborder-radius: calc(var(--border-radius) + 4px);\n\tfilter: blur(4px);\n\toverflow: hidden;\n\tpointer-events: none;\n\n\t&::before {\n\t\tcontent: '';\n\t\tpointer-events: none;\n\t\tposition: absolute;\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\tleft: 0;\n\t\ttop: 0;\n\n\t\tbackground-image: linear-gradient(\n\t\t\tto bottom left,\n\t\t\tvar(--color-1),\n\t\t\tvar(--color-2),\n\t\t\tvar(--color-1)\n\t\t);\n\t\tanimation: mask-running 2s linear infinite;\n\t}\n\t&::after {\n\t\tcontent: '';\n\t\tpointer-events: none;\n\t\tposition: absolute;\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\tleft: 0;\n\t\ttop: 0;\n\n\t\tbackground-image: linear-gradient(\n\t\t\tto bottom left,\n\t\t\tvar(--color-2),\n\t\t\tvar(--color-1),\n\t\t\tvar(--color-2)\n\t\t);\n\t\tanimation: mask-running 2s linear infinite;\n\t\tanimation-delay: 1s;\n\t}\n}\n\n/* 可视区域:靠 overflow:hidden + border-radius 统一裁角,\n * header 单独存在(运行中输入区隐藏)或 header+输入区一起存在时,四角都自动是圆的,\n * 不用再像以前那样给 header 写一条\"输入区隐藏时补圆角\"的特判规则。 */\n.shellInner {\n\tposition: relative;\n\tdisplay: flex;\n\tflex-direction: column;\n\toverflow: hidden;\n\tborder-radius: var(--border-radius);\n\tborder: 2px solid rgba(255, 255, 255, 0.4);\n\tbox-shadow:\n\t\t0 0 5px 1px rgba(255, 255, 255, 0.3),\n\t\t0 4px 20px rgba(0, 0, 0, 0.5);\n}\n\n/* 控制栏 */\n.header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tpadding: 8px 12px;\n\tuser-select: none;\n\n\theight: var(--height);\n\tflex-shrink: 0; /* 防止 header 被压缩 */\n\n\tcursor: pointer;\n\n\tbackground: rgba(0, 0, 0, 0.5);\n\tbackdrop-filter: blur(10px);\n\tbackground-clip: padding-box;\n\n\t.statusSection {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 8px;\n\t\tflex: 1;\n\t\tmin-height: 24px; /* 确保垂直居中 */\n\n\t\t.indicator {\n\t\t\twidth: 6px;\n\t\t\theight: 6px;\n\t\t\tborder-radius: 50%;\n\t\t\tbackground: rgba(255, 255, 255, 0.5);\n\t\t\tflex-shrink: 0;\n\t\t\tanimation: none; /* 默认无动画 */\n\n\t\t\t/* 运行状态 - 有动画 */\n\t\t\t&.thinking {\n\t\t\t\tbackground: rgb(57, 182, 255);\n\t\t\t\tanimation: pulse 0.8s ease-in-out infinite;\n\t\t\t}\n\n\t\t\t&.tool_executing {\n\t\t\t\tbackground: rgb(189, 69, 251);\n\t\t\t\tanimation: pulse 0.6s ease-in-out infinite;\n\t\t\t}\n\n\t\t\t&.retry {\n\t\t\t\tbackground: rgb(255, 214, 0);\n\t\t\t\tanimation: retryPulse 1s ease-in-out infinite;\n\t\t\t}\n\n\t\t\t/* 静止状态 - 无动画 */\n\t\t\t&.completed,\n\t\t\t&.input,\n\t\t\t&.output {\n\t\t\t\tbackground: rgb(34, 197, 94);\n\t\t\t\tanimation: none;\n\t\t\t}\n\n\t\t\t&.error {\n\t\t\t\tbackground: rgb(239, 68, 68);\n\t\t\t\tanimation: none;\n\t\t\t}\n\t\t}\n\n\t\t.statusText {\n\t\t\tcolor: white;\n\t\t\tfont-size: 12px;\n\t\t\tline-height: 1;\n\t\t\tfont-weight: 500;\n\t\t\ttransition: all 0.3s ease-in-out;\n\t\t\tposition: relative;\n\t\t\toverflow: hidden;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tmin-height: 24px; /* 确保垂直居中 */\n\n\t\t\t&.fadeOut {\n\t\t\t\tanimation: statusTextFadeOut 0.3s ease forwards;\n\t\t\t}\n\n\t\t\t&.fadeIn {\n\t\t\t\tanimation: statusTextFadeIn 0.3s ease forwards;\n\t\t\t}\n\t\t}\n\t}\n\n\t.controls {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 4px;\n\n\t\t.controlButton {\n\t\t\twidth: 24px;\n\t\t\theight: 24px;\n\t\t\tborder: none;\n\t\t\tborder-radius: 4px;\n\t\t\tbackground: rgba(255, 255, 255, 0.1);\n\t\t\tcolor: white;\n\t\t\tcursor: pointer;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tfont-size: 12px;\n\t\t\tline-height: 1;\n\n\t\t\t&:hover {\n\t\t\t\tbackground: rgba(255, 255, 255, 0.2);\n\t\t\t}\n\t\t}\n\n\t\t.stopButton {\n\t\t\tbackground: rgba(239, 68, 68, 0.2);\n\t\t\tcolor: rgb(255, 41, 41);\n\t\t\tfont-weight: 600;\n\n\t\t\t&:hover {\n\t\t\t\tbackground: rgba(239, 68, 68, 0.3);\n\t\t\t}\n\t\t}\n\t}\n}\n\n@keyframes statusTextFadeIn {\n\t0% {\n\t\topacity: 0;\n\t\ttransform: translateY(5px);\n\t}\n\t100% {\n\t\topacity: 1;\n\t\ttransform: translateY(0);\n\t}\n}\n\n@keyframes statusTextFadeOut {\n\t0% {\n\t\topacity: 1;\n\t\ttransform: translateY(0);\n\t}\n\t100% {\n\t\topacity: 0;\n\t\ttransform: translateY(-5px);\n\t}\n}\n\n/* 历史记录弹窗:普通流里排在面板前面(视觉上在上方),默认收起为 0 高度,\n * 展开时靠 JS 量出的真实内容高度做 max-height 动画(不用固定封顶值),\n * 撑开多高、要不要出现,跟面板本身完全无关,不用再算\"面板一共多高、弹窗该挂在哪个 bottom 偏移\"。 */\n.historySectionWrapper {\n\twidth: 100%;\n\toverflow: hidden;\n\tmax-height: 0;\n\tmargin-bottom: 0;\n\n\tbackground: rgba(2, 0, 20, 0.5);\n\tbackdrop-filter: blur(10px);\n\n\ttext-shadow: 0 0 1px rgba(0, 0, 0, 0.2);\n\n\tborder-radius: calc(var(--border-radius) + 4px);\n\tborder: 0 solid rgba(255, 255, 255, 0);\n\tbox-shadow: 0 4px 16px rgba(0, 0, 0, 0);\n\n\ttransition:\n\t\tmax-height 0.2s,\n\t\tmargin-bottom 0.2s,\n\t\tborder-width 0.2s,\n\t\tborder-color 0.2s,\n\t\tbox-shadow 0.2s;\n\n\t.wrapper.expanded & {\n\t\tmargin-bottom: 8px;\n\t\tborder-width: 2px;\n\t\tborder-color: rgba(255, 255, 255, 0.4);\n\t\tbox-shadow: 0 4px 16px rgba(0, 0, 0, 0.6);\n\t}\n\n\t.historySection {\n\t\tmax-height: min(\n\t\t\t500px,\n\t\t\tcalc(100vh - 200px - var(--height))\n\t\t); /* 列表本身的滚动上限,跟展开/收起动画解耦 */\n\t\toverflow-y: auto;\n\t\toverscroll-behavior: contain;\n\t\tscrollbar-width: none;\n\t\t/* 底部留白用 padding 撑,不依赖最后一项 margin-bottom:\n\t\t * overflow:auto 容器会裁掉末尾子元素的 margin,导致最后一条记录贴底 */\n\t\tpadding: 8px;\n\n\t\t.historyItem {\n\t\t\t/* backdrop-filter: blur(10px); */\n\t\t\tpadding: 8px 10px;\n\t\t\tmargin-bottom: 8px;\n\t\t\tbackground: linear-gradient(135deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.03));\n\t\t\tborder-radius: 8px;\n\t\t\tborder-left: 2px solid rgba(57, 182, 255, 0.5);\n\t\t\tfont-size: 12px;\n\t\t\tcolor: white;\n\t\t\t/* color: black; */\n\t\t\tline-height: 1.3;\n\t\t\tposition: relative;\n\t\t\toverflow: hidden;\n\n\t\t\t/* 微妙的内阴影 */\n\t\t\tbox-shadow:\n\t\t\t\t0 1px 0 rgba(255, 255, 255, 0.1),\n\t\t\t\t0 1px 3px rgba(0, 0, 0, 0.1);\n\n\t\t\t&::before {\n\t\t\t\tcontent: '';\n\t\t\t\tposition: absolute;\n\t\t\t\ttop: 0;\n\t\t\t\tleft: 0;\n\t\t\t\tright: 0;\n\t\t\t\theight: 1px;\n\t\t\t\tbackground: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);\n\t\t\t}\n\n\t\t\t&:hover {\n\t\t\t\tbackground: linear-gradient(135deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.06));\n\t\t\t\t/* transform: translateY(-1px); */\n\t\t\t\tbox-shadow:\n\t\t\t\t\tinset 0 1px 0 rgba(255, 255, 255, 0.15),\n\t\t\t\t\t0 2px 4px rgba(0, 0, 0, 0.15);\n\t\t\t}\n\n\t\t\t&.completed,\n\t\t\t&.input,\n\t\t\t&.output {\n\t\t\t\tborder-left-color: rgb(34, 197, 94);\n\t\t\t\tbackground: linear-gradient(135deg, rgba(34, 197, 94, 0.1), rgba(34, 197, 94, 0.05));\n\t\t\t}\n\n\t\t\t&.error {\n\t\t\t\tborder-left-color: rgb(239, 68, 68);\n\t\t\t\tbackground: linear-gradient(135deg, rgba(239, 68, 68, 0.1), rgba(239, 68, 68, 0.05));\n\t\t\t}\n\n\t\t\t&.retry {\n\t\t\t\tborder-left-color: rgb(255, 214, 0);\n\t\t\t\tbackground: linear-gradient(135deg, rgba(255, 214, 0, 0.1), rgba(255, 214, 0, 0.05));\n\t\t\t}\n\n\t\t\t&.observation {\n\t\t\t\tborder-left-color: rgb(147, 51, 234);\n\t\t\t\tbackground: linear-gradient(135deg, rgba(147, 51, 234, 0.1), rgba(147, 51, 234, 0.05));\n\t\t\t}\n\n\t\t\t&.question {\n\t\t\t\tborder-left-color: rgb(255, 159, 67);\n\t\t\t\tbackground: linear-gradient(135deg, rgba(255, 159, 67, 0.15), rgba(255, 159, 67, 0.08));\n\t\t\t}\n\n\t\t\t/* 突出显示 done 成功结果 */\n\t\t\t&.doneSuccess {\n\t\t\t\tbackground: linear-gradient(\n\t\t\t\t\t135deg,\n\t\t\t\t\trgba(34, 197, 94, 0.25),\n\t\t\t\t\trgba(34, 197, 94, 0.15),\n\t\t\t\t\trgba(34, 197, 94, 0.08)\n\t\t\t\t);\n\t\t\t\tborder: none;\n\t\t\t\tborder-left: 2px solid rgb(34, 197, 94);\n\t\t\t\tbox-shadow:\n\t\t\t\t\t0 4px 12px rgba(34, 197, 94, 0.3),\n\t\t\t\t\tinset 0 1px 0 rgba(255, 255, 255, 0.2),\n\t\t\t\t\t0 0 20px rgba(34, 197, 94, 0.1);\n\t\t\t\tfont-weight: 600;\n\t\t\t\tcolor: rgb(220, 252, 231);\n\t\t\t\tpadding: 10px 12px;\n\t\t\t\tmargin-bottom: 8px;\n\t\t\t\tborder-radius: 8px;\n\t\t\t\tposition: relative;\n\t\t\t\toverflow: hidden;\n\n\t\t\t\t&::before {\n\t\t\t\t\tbackground: linear-gradient(90deg, transparent, rgba(34, 197, 94, 0.4), transparent);\n\t\t\t\t}\n\n\t\t\t\t&::after {\n\t\t\t\t\tcontent: '';\n\t\t\t\t\tposition: absolute;\n\t\t\t\t\ttop: 0;\n\t\t\t\t\tleft: -100%;\n\t\t\t\t\twidth: 100%;\n\t\t\t\t\theight: 100%;\n\t\t\t\t\tbackground: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);\n\t\t\t\t\tanimation: shimmer 2s ease-in-out infinite;\n\t\t\t\t}\n\n\t\t\t\t.historyContent {\n\t\t\t\t\t.statusIcon {\n\t\t\t\t\t\tfont-size: 16px;\n\t\t\t\t\t\tanimation: celebrate 0.8s ease-in-out;\n\t\t\t\t\t\tfilter: drop-shadow(0 2px 4px rgba(34, 197, 94, 0.5));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* 突出显示 done 失败结果 */\n\t\t\t&.doneError {\n\t\t\t\tbackground: linear-gradient(\n\t\t\t\t\t135deg,\n\t\t\t\t\trgba(239, 68, 68, 0.25),\n\t\t\t\t\trgba(239, 68, 68, 0.15),\n\t\t\t\t\trgba(239, 68, 68, 0.08)\n\t\t\t\t);\n\t\t\t\tborder: none;\n\t\t\t\tborder-left: 2px solid rgb(239, 68, 68);\n\t\t\t\tbox-shadow:\n\t\t\t\t\t0 4px 12px rgba(239, 68, 68, 0.3),\n\t\t\t\t\tinset 0 1px 0 rgba(255, 255, 255, 0.2),\n\t\t\t\t\t0 0 20px rgba(239, 68, 68, 0.1);\n\t\t\t\tfont-weight: 600;\n\t\t\t\tcolor: rgb(254, 226, 226);\n\t\t\t\tpadding: 10px 12px;\n\t\t\t\tmargin-bottom: 8px;\n\t\t\t\tborder-radius: 8px;\n\t\t\t\tposition: relative;\n\t\t\t\toverflow: hidden;\n\n\t\t\t\t&::before {\n\t\t\t\t\tbackground: linear-gradient(90deg, transparent, rgba(239, 68, 68, 0.4), transparent);\n\t\t\t\t}\n\n\t\t\t\t.historyContent {\n\t\t\t\t\t.statusIcon {\n\t\t\t\t\t\tfont-size: 16px;\n\t\t\t\t\t\tfilter: drop-shadow(0 2px 4px rgba(239, 68, 68, 0.5));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t.historyContent {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tgap: 8px;\n\n\t\t\t\tword-break: break-all;\n\t\t\t\twhite-space: pre-wrap;\n\n\t\t\t\t/* overflow-x: auto; */\n\n\t\t\t\t.statusIcon {\n\t\t\t\t\tfont-size: 12px;\n\t\t\t\t\tflex-shrink: 0;\n\t\t\t\t\tline-height: 1;\n\t\t\t\t\ttransition: all 0.3s ease;\n\t\t\t\t}\n\n\t\t\t\t.reflectionLines {\n\t\t\t\t\tdisplay: flex;\n\t\t\t\t\tflex-direction: column;\n\t\t\t\t\tgap: 4px;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t.historyMeta {\n\t\t\t\tfont-size: 10px;\n\t\t\t\tcolor: rgba(255, 255, 255, 0.6);\n\t\t\t\t/* color: rgb(61, 61, 61); */\n\t\t\t\tmargin-top: 8px;\n\t\t\t\tline-height: 1;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/* 动画关键帧 - 更快的闪烁 */\n@keyframes pulse {\n\t0%,\n\t100% {\n\t\topacity: 1;\n\t\ttransform: scale(1);\n\t}\n\t50% {\n\t\topacity: 0.4;\n\t\ttransform: scale(1.3);\n\t}\n}\n\n/* 重试动画 - 旋转脉冲 */\n@keyframes retryPulse {\n\t0%,\n\t100% {\n\t\topacity: 1;\n\t\ttransform: scale(1) rotate(0deg);\n\t}\n\t25% {\n\t\topacity: 0.6;\n\t\ttransform: scale(1.2) rotate(90deg);\n\t}\n\t50% {\n\t\topacity: 0.8;\n\t\ttransform: scale(1.1) rotate(180deg);\n\t}\n\t75% {\n\t\topacity: 0.6;\n\t\ttransform: scale(1.2) rotate(270deg);\n\t}\n}\n\n/* 庆祝动画 */\n@keyframes celebrate {\n\t0%,\n\t100% {\n\t\ttransform: scale(1);\n\t}\n\t25% {\n\t\ttransform: scale(1.2) rotate(-5deg);\n\t}\n\t75% {\n\t\ttransform: scale(1.2) rotate(5deg);\n\t}\n}\n\n/* done 卡片的光泽效果 */\n@keyframes shimmer {\n\t0% {\n\t\tleft: -100%;\n\t}\n\t100% {\n\t\tleft: 100%;\n\t}\n}\n\n/* 输入区域样式 */\n.inputSectionWrapper {\n\twidth: 100%;\n\tmargin: 0 auto;\n\n\toverflow: hidden;\n\tmax-height: 0; /* 由 JS 同步为真实内容高度(含附件条、错误提示),见 #syncInputHeight */\n\n\ttransition: max-height 0.2s;\n\n\t/* 与 header 保持同色调,让面板看起来是一体的深色玻璃卡片 */\n\tbackground: rgba(0, 0, 0, 0.4);\n\tbackdrop-filter: blur(10px);\n\n\tborder-bottom-left-radius: calc(var(--border-radius) + 4px);\n\tborder-bottom-right-radius: calc(var(--border-radius) + 4px);\n\n\t&.hidden {\n\t\tmax-height: 0;\n\t}\n\n\t.inputSection {\n\t\tposition: relative;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\theight: var(--input-height);\n\t\tpadding: 8px; /* 与上方状态条 padding 保持一致 */\n\n\t\t.attachButton {\n\t\t\tposition: absolute;\n\t\t\tright: 14px;\n\t\t\tbottom: 12px;\n\t\t\tz-index: 1;\n\t\t\twidth: 26px;\n\t\t\theight: 26px;\n\t\t\tborder: none;\n\t\t\tborder-radius: 4px;\n\t\t\tbackground: transparent;\n\t\t\tcolor: rgba(255, 255, 255, 0.85);\n\t\t\tfont-size: 13px;\n\t\t\tline-height: 1;\n\t\t\tcursor: pointer;\n\t\t}\n\n\t\t.taskInput {\n\t\t\tflex: 1;\n\t\t\theight: 100%;\n\t\t\tbackground: rgba(255, 255, 255, 0.08);\n\t\t\tborder: 1px solid rgba(255, 255, 255, 0.15);\n\t\t\tborder-radius: 12px;\n\t\t\tpadding: 8px 40px 8px 14px; /* 右侧留白避免文字与悬浮的附件按钮重叠 */\n\t\t\tcolor: rgb(240, 240, 245);\n\t\t\tfont-family: inherit;\n\t\t\tfont-size: 14px;\n\t\t\tline-height: 1.4;\n\t\t\toutline: none;\n\t\t\tresize: none;\n\t\t\toverflow-y: auto;\n\t\t\ttransition: all 0.2s ease;\n\n\t\t\t&::placeholder {\n\t\t\t\tcolor: rgba(255, 255, 255, 0.45);\n\t\t\t}\n\n\t\t\t&:focus {\n\t\t\t\tbackground: rgba(255, 255, 255, 0.14);\n\t\t\t\tborder-color: rgba(57, 182, 255, 0.6);\n\t\t\t\tbox-shadow: 0 0 0 2px rgba(57, 182, 255, 0.2);\n\t\t\t}\n\t\t}\n\t}\n\n\t.attachmentsBar {\n\t\tdisplay: flex;\n\t\tflex-wrap: wrap;\n\t\tgap: 6px;\n\t\tpadding: 8px 8px 0;\n\n\t\t&:empty {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n\n\t.attachmentChip {\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tgap: 4px;\n\t\tmax-width: 160px;\n\t\tpadding: 3px 4px 3px 8px;\n\t\tborder-radius: 10px;\n\t\tbackground: rgba(255, 255, 255, 0.1);\n\t\tcolor: rgba(255, 255, 255, 0.85);\n\t\tfont-size: 12px;\n\t\twhite-space: nowrap;\n\n\t\tspan {\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t}\n\t}\n\n\t.attachmentChipRemove {\n\t\tflex-shrink: 0;\n\t\tborder: none;\n\t\tbackground: transparent;\n\t\tcolor: rgba(255, 255, 255, 0.6);\n\t\tcursor: pointer;\n\t\tfont-size: 13px;\n\t\tline-height: 1;\n\t\tpadding: 0 4px;\n\n\t\t&:hover {\n\t\t\tcolor: rgba(255, 255, 255, 0.95);\n\t\t}\n\t}\n\n\t.attachError {\n\t\tpadding: 4px 8px 0;\n\t\tcolor: rgb(255, 138, 138);\n\t\tfont-size: 12px;\n\n\t\t&:empty {\n\t\t\tdisplay: none;\n\t\t\tpadding: 0;\n\t\t}\n\t}\n\n\t.fileInput {\n\t\tdisplay: none;\n\t}\n}\n","/**\n * UI 的卡片 HTML 生成工具\n */\nimport { escapeHtml } from './utils'\n\nimport styles from './styles/UI.module.css'\n\n// 卡片类型:default(默认)/ input(输入)/ output(输出)/ question(提问)/ observation(观察)/ doneSuccess(成功)/ doneError(失败)\ntype CardType =\n\t'default' | 'input' | 'output' | 'question' | 'observation' | 'doneSuccess' | 'doneError'\n\n// 卡片渲染选项\ninterface CardOptions {\n\ticon: string // 图标(emoji)\n\tcontent: string | string[] // 卡片内容,字符串或反思行数组\n\tmeta?: string // 元信息(如步骤号)\n\ttype?: CardType // 卡片类型,用于选择对应样式\n}\n\n/** 创建单条历史卡片 */\nexport function createCard({ icon, content, meta, type }: CardOptions): string {\n\tconst typeClass = type ? styles[type] : ''\n\tconst contentHtml = Array.isArray(content)\n\t\t? `<div class=\"${styles.reflectionLines}\">${content.map((line) => `<span>${escapeHtml(line)}</span>`).join('')}</div>`\n\t\t: `<span>${escapeHtml(content)}</span>`\n\n\treturn `\n\t\t<div class=\"${styles.historyItem} ${typeClass}\">\n\t\t\t<div class=\"${styles.historyContent}\">\n\t\t\t\t<span class=\"${styles.statusIcon}\">${icon}</span>\n\t\t\t\t${contentHtml}\n\t\t\t</div>\n\t\t\t${meta ? `<div class=\"${styles.historyMeta}\">${meta}</div>` : ''}\n\t\t</div>\n\t`\n}\n\n/** 从反思对象生成反思行 */\nexport function createReflectionLines(reflection: {\n\tevaluation_previous_goal?: string // 对上一目标的评估\n\tmemory?: string // 记忆信息\n\tnext_goal?: string // 下一步目标\n}): string[] {\n\tconst lines: string[] = []\n\tif (reflection.evaluation_previous_goal) {\n\t\tlines.push(`🔍 ${reflection.evaluation_previous_goal}`)\n\t}\n\tif (reflection.memory) {\n\t\tlines.push(`💾 ${reflection.memory}`)\n\t}\n\tif (reflection.next_goal) {\n\t\tlines.push(`🎯 ${reflection.next_goal}`)\n\t}\n\treturn lines\n}\n","import { I18n, type SupportedLanguage } from './i18n'\nimport { truncate, escapeHtml } from './utils'\nimport { createCard, createReflectionLines } from './cards'\nimport type { AgentActivity, Attachment, UIAdapter } from './types'\n\nimport styles from './styles/UI.module.css'\n\nexport type { Attachment, UIAdapter } from './types'\n\n/** 单个附件最大体积(5MB) */\nconst MAX_ATTACHMENT_SIZE = 5 * 1024 * 1024\n/** 单次任务最多允许携带的附件数量 */\nconst MAX_ATTACHMENTS = 5\n/** 支持的文本类附件扩展名(图片按 MIME 类型单独判断) */\nconst TEXT_ATTACHMENT_EXTENSIONS = ['.txt', '.md', '.json', '.csv']\n\n/**\n * UI 配置\n */\nexport interface UIConfig {\n\tlanguage?: SupportedLanguage // 界面语言,可选\n\t/**\n\t * 任务完成后是否提示输入下一个任务\n\t * @default true\n\t */\n\tpromptForNextTask?: boolean\n}\n\n/**\n * Agent 控制 UI\n *\n * 架构:\n * - 历史列表:直接从 agent.history 渲染(历史事件)\n * - 头部栏:展示活动事件(瞬时状态)和 agent 状态\n *\n * 这种分离保证了数据一致性 - 历史是已完成内容的唯一数据源,\n * 而活动状态展示的是当前正在发生的事情。\n */\nexport class UI {\n\t#wrapper: HTMLElement // 整体容器\n\t#indicator: HTMLElement // 状态指示器(圆点)\n\t#statusText: HTMLElement // 状态文本\n\t#historySectionWrapper: HTMLElement // 历史区域外层容器(控制 max-height 动画)\n\t#historySection: HTMLElement // 历史卡片列表容器\n\t#expandButton: HTMLElement // 展开/收起按钮\n\t#actionButton: HTMLElement // 动作按钮(运行中为停止,空闲为关闭)\n\t#inputSection: HTMLElement // 输入区域容器\n\t#taskInput: HTMLTextAreaElement // 任务输入框\n\t#attachButton: HTMLElement // 附件按钮(📎)\n\t#fileInput: HTMLInputElement // 隐藏的文件选择输入框\n\t#attachmentsBar: HTMLElement // 已选附件的预览条\n\t#attachError: HTMLElement // 附件校验错误提示\n\n\t#agent: UIAdapter // 绑定的 agent 适配器\n\t#config: UIConfig // 用户配置\n\t#isExpanded = false // 历史区是否展开\n\t#i18n: I18n // 国际化实例\n\t#userAnswerResolver: ((input: string) => void) | null = null // 用户回答的 resolve 回调\n\t#userAnswerRejecter: ((reason: DOMException) => void) | null = null // 用户回答的 reject 回调\n\t#userAnswerSignal: AbortSignal | null = null // 用户回答的取消信号\n\t#userAnswerAbortHandler: (() => void) | null = null // 取消信号触发时的处理函数\n\t#isWaitingForUserAnswer: boolean = false // 是否正在等待用户回答\n\t#pendingAttachments: Attachment[] = [] // 当前已选中、待随任务一起提交的附件\n\t#headerUpdateTimer: ReturnType<typeof setInterval> | null = null // 头部更新循环的定时器\n\t#timers = new Set<ReturnType<typeof setTimeout>>() // 内部延时定时器集合\n\t#pendingHeaderText: string | null = null // 待显示的头部文本\n\t#isAnimating = false // 是否正在执行文本切换动画\n\t#disposed = false // 是否已销毁\n\n\t// 事件处理器(绑定后用于移除)\n\t#onStatusChange = () => this.#handleStatusChange() // 状态变化事件处理\n\t#onHistoryChange = () => this.#handleHistoryChange() // 历史变化事件处理\n\t#onActivity = (e: Event) => this.#handleActivity((e as CustomEvent<AgentActivity>).detail) // 活动事件处理\n\t#onAgentDispose = () => this.dispose() // agent 销毁事件处理\n\t#askUserCallback = (question: string, options?: { signal: AbortSignal }) =>\n\t\t// 转发给内部 askUser 的回调\n\t\tthis.#askUser(question, options?.signal)\n\n\t/** 获取 UI 的根容器元素 */\n\tget wrapper(): HTMLElement {\n\t\treturn this.#wrapper\n\t}\n\n\t/**\n\t * 创建绑定到某个 agent 的 UI\n\t * @param agent - 实现 UIAdapter 的 Agent 实例\n\t * @param config - 可选的 UI 配置\n\t */\n\tconstructor(agent: UIAdapter, config: UIConfig = {}) {\n\t\tthis.#agent = agent\n\t\tthis.#config = config\n\t\tthis.#i18n = new I18n(config.language ?? 'en-US')\n\n\t\t// 在 agent 上设置 askUser 回调\n\t\tthis.#agent.onAskUser = this.#askUserCallback\n\n\t\t// 创建 UI 元素\n\t\tthis.#wrapper = this.#createWrapper()\n\t\tthis.#indicator = this.#wrapper.querySelector(`.${styles.indicator}`)!\n\t\tthis.#statusText = this.#wrapper.querySelector(`.${styles.statusText}`)!\n\t\tthis.#historySectionWrapper = this.#wrapper.querySelector(`.${styles.historySectionWrapper}`)!\n\t\tthis.#historySection = this.#wrapper.querySelector(`.${styles.historySection}`)!\n\t\tthis.#expandButton = this.#wrapper.querySelector(`.${styles.expandButton}`)!\n\t\tthis.#actionButton = this.#wrapper.querySelector(`.${styles.stopButton}`)!\n\t\tthis.#inputSection = this.#wrapper.querySelector(`.${styles.inputSectionWrapper}`)!\n\t\tthis.#taskInput = this.#wrapper.querySelector(`.${styles.taskInput}`)!\n\t\tthis.#attachButton = this.#wrapper.querySelector(`.${styles.attachButton}`)!\n\t\tthis.#fileInput = this.#wrapper.querySelector(`.${styles.fileInput}`)!\n\t\tthis.#attachmentsBar = this.#wrapper.querySelector(`.${styles.attachmentsBar}`)!\n\t\tthis.#attachError = this.#wrapper.querySelector(`.${styles.attachError}`)!\n\n\t\t// 监听 agent 事件\n\t\tthis.#agent.addEventListener('statuschange', this.#onStatusChange)\n\t\tthis.#agent.addEventListener('historychange', this.#onHistoryChange)\n\t\tthis.#agent.addEventListener('activity', this.#onActivity)\n\t\tthis.#agent.addEventListener('dispose', this.#onAgentDispose)\n\n\t\tthis.#setupEventListeners()\n\t\tthis.#startHeaderUpdateLoop()\n\n\t\tthis.#showInputArea()\n\n\t\tthis.hide() // 初始状态为隐藏\n\t}\n\n\t// ========== Agent 事件处理 ==========\n\n\t/** 处理 agent 状态变化 */\n\t#handleStatusChange(): void {\n\t\tconst status = this.#agent.status\n\n\t\t// 将 agent 状态映射为 UI 指示器。`completed` 运行但其结果\n\t\t// 上报失败时显示为 error;其他状态映射到各自的指示器。\n\t\tconst failed = status === 'completed' && this.#agent.lastResult?.success === false\n\t\tthis.#updateStatusIndicator(failed ? 'error' : status)\n\n\t\t// 变换动作按钮:运行中 = 停止(■),未运行 = 关闭(X)\n\t\tif (status === 'running') {\n\t\t\tthis.#actionButton.textContent = '■'\n\t\t\tthis.#actionButton.title = this.#i18n.t('ui.stop')\n\t\t} else {\n\t\t\tthis.#actionButton.textContent = 'X'\n\t\t\tthis.#actionButton.title = this.#i18n.t('ui.close')\n\t\t}\n\n\t\t// 根据状态显示/隐藏\n\t\tif (status === 'running') {\n\t\t\tthis.show()\n\t\t\tthis.#hideInputArea() // 运行期间隐藏输入框\n\t\t\tif (!this.#isExpanded) {\n\t\t\t\tthis.#expand() // 任务开始时展开历史,方便观察执行过程\n\t\t\t}\n\t\t}\n\n\t\t// 处理完成状态\n\t\tif (status === 'completed' || status === 'error' || status === 'stopped') {\n\t\t\tif (!this.#isExpanded) {\n\t\t\t\tthis.#expand()\n\t\t\t}\n\t\t\tif (this.#shouldShowInputArea()) {\n\t\t\t\tthis.#showInputArea()\n\t\t\t}\n\t\t}\n\t}\n\n\t/** 处理 agent 历史变化 - 根据 agent.history 重新渲染历史列表 */\n\t#handleHistoryChange(): void {\n\t\tthis.#renderHistory()\n\t}\n\n\t/**\n\t * 处理 agent 活动 - 用于即时 UI 反馈的瞬时状态\n\t * 活动事件不会持久化到历史中,仅用于头部栏更新\n\t */\n\t#handleActivity(activity: AgentActivity): void {\n\t\tswitch (activity.type) {\n\t\t\tcase 'thinking':\n\t\t\t\tthis.#pendingHeaderText = this.#i18n.t('ui.thinking')\n\t\t\t\tthis.#updateStatusIndicator('thinking')\n\t\t\t\tbreak\n\n\t\t\tcase 'executing':\n\t\t\t\tthis.#pendingHeaderText = this.#getToolExecutingText(activity.tool, activity.input)\n\t\t\t\tthis.#updateStatusIndicator('executing')\n\t\t\t\tbreak\n\n\t\t\tcase 'executed':\n\t\t\t\tthis.#pendingHeaderText = truncate(activity.output, 50)\n\t\t\t\tbreak\n\n\t\t\tcase 'retrying':\n\t\t\t\tthis.#pendingHeaderText = `Retrying (${activity.attempt}/${activity.maxAttempts})`\n\t\t\t\tthis.#updateStatusIndicator('retrying')\n\t\t\t\tbreak\n\n\t\t\tcase 'error':\n\t\t\t\tthis.#pendingHeaderText = truncate(activity.message, 50)\n\t\t\t\tthis.#updateStatusIndicator('error')\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t/**\n\t * 请求用户输入(内部方法,由 agent 通过 onAskUser 调用)。\n\t * 当 `signal` 中止(任务被停止或销毁)时拒绝该请求,\n\t * 并清理问题卡片和挂起状态,以便 agent 循环能够收尾。\n\t */\n\t#askUser(question: string, signal?: AbortSignal): Promise<string> {\n\t\tif (this.#disposed) return Promise.reject(this.#createAbortError())\n\t\tthis.#rejectPendingQuestion()\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\t// 设置\"等待用户回答\"状态\n\t\t\tthis.#isWaitingForUserAnswer = true\n\t\t\tthis.#userAnswerResolver = resolve\n\t\t\tthis.#userAnswerRejecter = reject\n\n\t\t\t// 展开历史 UI\n\t\t\tif (!this.#isExpanded) {\n\t\t\t\tthis.#expand()\n\t\t\t}\n\n\t\t\t// 添加临时问题卡片,让用户能看到完整的问题\n\t\t\tconst tempCard = document.createElement('div')\n\t\t\ttempCard.innerHTML = createCard({\n\t\t\t\ticon: '❓',\n\t\t\t\tcontent: `Question: ${question}`,\n\t\t\t\ttype: 'question',\n\t\t\t})\n\t\t\tconst cardElement = tempCard.firstElementChild as HTMLElement\n\t\t\tcardElement.setAttribute('data-temp-card', 'true')\n\t\t\tthis.#historySection.appendChild(cardElement)\n\t\t\tthis.#scrollToBottom()\n\t\t\tthis.#syncHistoryHeight()\n\n\t\t\tthis.#showInputArea(this.#i18n.t('ui.userAnswerPrompt'))\n\n\t\t\tif (signal) {\n\t\t\t\tthis.#userAnswerSignal = signal\n\t\t\t\tthis.#userAnswerAbortHandler = () => this.#rejectPendingQuestion()\n\t\t\t\tif (signal.aborted) {\n\t\t\t\t\tthis.#rejectPendingQuestion()\n\t\t\t\t} else {\n\t\t\t\t\tsignal.addEventListener('abort', this.#userAnswerAbortHandler, { once: true })\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\t/** 创建标准的中止异常(AbortError) */\n\t#createAbortError(): DOMException {\n\t\treturn new DOMException('The operation was aborted.', 'AbortError')\n\t}\n\n\t/** 清空用户回答相关状态并移除临时卡片 */\n\t#clearQuestionState(): void {\n\t\tif (this.#userAnswerSignal && this.#userAnswerAbortHandler) {\n\t\t\tthis.#userAnswerSignal.removeEventListener('abort', this.#userAnswerAbortHandler)\n\t\t}\n\t\tthis.#userAnswerSignal = null\n\t\tthis.#userAnswerAbortHandler = null\n\t\tthis.#userAnswerResolver = null\n\t\tthis.#userAnswerRejecter = null\n\t\tthis.#isWaitingForUserAnswer = false\n\t\tthis.#removeTempCards()\n\t}\n\n\t/** 拒绝挂起的用户问题(存在待回答问题时) */\n\t#rejectPendingQuestion(): void {\n\t\tconst reject = this.#userAnswerRejecter\n\t\tif (!reject) return\n\t\tthis.#clearQuestionState()\n\t\treject(this.#createAbortError())\n\t}\n\n\t/** 移除临时问题卡片(为安全起见仅移除直接子节点) */\n\t#removeTempCards(): void {\n\t\tArray.from(this.#historySection.children).forEach((child) => {\n\t\t\tif (child.getAttribute('data-temp-card') === 'true') {\n\t\t\t\tchild.remove()\n\t\t\t}\n\t\t})\n\t\tthis.#syncHistoryHeight()\n\t}\n\n\t// ========== 公共控制方法 ==========\n\n\t/** 显示 UI(淡入 + 平移效果) */\n\tshow(): void {\n\t\tthis.wrapper.style.display = 'block'\n\t\tvoid this.wrapper.offsetHeight\n\t\tthis.wrapper.style.opacity = '1'\n\t\tthis.wrapper.style.transform = 'translateX(-50%) translateY(0)'\n\t}\n\n\t/** 隐藏 UI(淡出 + 下移效果) */\n\thide(): void {\n\t\tthis.wrapper.style.opacity = '0'\n\t\tthis.wrapper.style.transform = 'translateX(-50%) translateY(20px)'\n\t\tthis.wrapper.style.display = 'none'\n\t}\n\n\t/** 重置 UI 状态(拒绝挂起问题、清空状态文本、收起历史并显示输入框) */\n\treset(): void {\n\t\tthis.#rejectPendingQuestion()\n\t\tthis.#statusText.textContent = this.#i18n.t('ui.ready')\n\t\tthis.#updateStatusIndicator('thinking')\n\t\tthis.#renderHistory()\n\t\tthis.#collapse()\n\t\t// 显示输入区域\n\t\tthis.#showInputArea()\n\t}\n\n\t/** 展开历史弹窗(公开方法) */\n\texpand(): void {\n\t\tthis.#expand()\n\t}\n\n\t/** 收起历史弹窗(公开方法) */\n\tcollapse(): void {\n\t\tthis.#collapse()\n\t}\n\n\t/**\n\t * 销毁 UI 并清理事件监听器\n\t */\n\tdispose(): void {\n\t\tif (this.#disposed) return\n\t\tthis.#disposed = true\n\n\t\t// 移除 agent 事件监听器\n\t\tthis.#agent.removeEventListener('statuschange', this.#onStatusChange)\n\t\tthis.#agent.removeEventListener('historychange', this.#onHistoryChange)\n\t\tthis.#agent.removeEventListener('activity', this.#onActivity)\n\t\tthis.#agent.removeEventListener('dispose', this.#onAgentDispose)\n\t\tif (this.#agent.onAskUser === this.#askUserCallback) {\n\t\t\tthis.#agent.onAskUser = undefined\n\t\t}\n\n\t\t// 清理 UI\n\t\tthis.#rejectPendingQuestion()\n\t\tthis.#clearQuestionState()\n\t\tthis.#stopHeaderUpdateLoop()\n\t\tfor (const timer of this.#timers) clearTimeout(timer)\n\t\tthis.#timers.clear()\n\t\tthis.wrapper.remove()\n\t}\n\n\t// ========== 私有方法 ==========\n\n\t/** 根据工具名称和参数生成\"正在执行\"提示文本 */\n\t#getToolExecutingText(toolName: string, args: unknown): string {\n\t\tconst a = args as Record<string, string | number>\n\t\tswitch (toolName) {\n\t\t\tcase 'click_element_by_index':\n\t\t\t\treturn this.#i18n.t('ui.tools.clicking', { index: a.index })\n\t\t\tcase 'input_text':\n\t\t\t\treturn this.#i18n.t('ui.tools.inputting', { index: a.index })\n\t\t\tcase 'select_dropdown_option':\n\t\t\t\treturn this.#i18n.t('ui.tools.selecting', { text: a.text })\n\t\t\tcase 'scroll':\n\t\t\t\treturn this.#i18n.t('ui.tools.scrolling')\n\t\t\tcase 'wait':\n\t\t\t\treturn this.#i18n.t('ui.tools.waiting', { seconds: a.seconds })\n\t\t\tcase 'ask_user':\n\t\t\t\treturn this.#i18n.t('ui.tools.askingUser')\n\t\t\tcase 'done':\n\t\t\t\treturn this.#i18n.t('ui.tools.done')\n\t\t\tdefault:\n\t\t\t\treturn this.#i18n.t('ui.tools.executing', { toolName })\n\t\t}\n\t}\n\n\t/**\n\t * 动作按钮处理:运行中则停止,空闲则关闭(销毁)\n\t */\n\t#handleActionButton(): void {\n\t\tif (this.#agent.status === 'running') {\n\t\t\tthis.#agent.stop()\n\t\t} else {\n\t\t\tthis.#agent.dispose()\n\t\t}\n\t}\n\n\t/**\n\t * 提交任务\n\t */\n\t#submitTask() {\n\t\tconst input = this.#taskInput.value.trim()\n\t\tif (!input) return\n\n\t\t// 隐藏输入区域\n\t\tthis.#hideInputArea()\n\n\t\tif (this.#isWaitingForUserAnswer) {\n\t\t\t// 处理用户输入模式\n\t\t\tthis.#handleUserAnswer(input)\n\t\t\treturn\n\t\t}\n\n\t\tconst attachments = this.#pendingAttachments\n\t\tthis.#pendingAttachments = []\n\t\tthis.#renderAttachments()\n\t\tthis.#setAttachError('')\n\n\t\tconst onExecuteError = (error: unknown) => {\n\t\t\tif ((error as { name?: string })?.name === 'AbortError') return\n\t\t\tconsole.error('[UI] Failed to execute task:', error)\n\t\t\tif (!this.#disposed) this.#showInputArea()\n\t\t}\n\n\t\t// 通过 agent 执行任务(仅当存在附件时才多传一个参数,保持无附件场景的调用签名不变)\n\t\tconst promise =\n\t\t\tattachments.length > 0 ? this.#agent.execute(input, attachments) : this.#agent.execute(input)\n\t\tvoid promise.catch(onExecuteError)\n\t}\n\n\t/**\n\t * 处理用户回答\n\t */\n\t#handleUserAnswer(input: string): void {\n\t\tthis.#removeTempCards()\n\n\t\t// 调用 resolve 回调返回用户输入\n\t\tconst resolve = this.#userAnswerResolver\n\t\tthis.#clearQuestionState()\n\t\tresolve?.(input)\n\t}\n\n\t/**\n\t * 显示输入区域\n\t */\n\t#showInputArea(placeholder?: string): void {\n\t\t// 清空输入框\n\t\tthis.#taskInput.value = ''\n\t\tthis.#taskInput.placeholder = placeholder || this.#i18n.t('ui.taskInput')\n\t\tthis.#inputSection.classList.remove(styles.hidden)\n\t\t// 等待用户回答追问时不支持附加文件\n\t\tthis.#attachButton.style.display = this.#isWaitingForUserAnswer ? 'none' : ''\n\t\tthis.#syncInputHeight()\n\t\t// 聚焦到输入框\n\t\tthis.#setTimer(() => {\n\t\t\tthis.#taskInput.focus()\n\t\t}, 100)\n\t}\n\n\t/**\n\t * 隐藏输入区域\n\t */\n\t#hideInputArea(): void {\n\t\tthis.#inputSection.classList.add(styles.hidden)\n\t\tthis.#inputSection.style.maxHeight = '0px'\n\t}\n\n\t/** 将输入区域外层容器的 max-height 同步为当前真实内容高度(含附件条、错误提示) */\n\t#syncInputHeight(): void {\n\t\tif (this.#inputSection.classList.contains(styles.hidden)) return\n\t\tthis.#inputSection.style.maxHeight = `${this.#inputSection.scrollHeight}px`\n\t}\n\n\t/** 设置(或清空)附件校验错误提示,并同步输入区域高度 */\n\t#setAttachError(message: string): void {\n\t\tthis.#attachError.textContent = message\n\t\tthis.#syncInputHeight()\n\t}\n\n\t/** 判断文件是否为支持的附件类型:图片,或指定扩展名的文本文件 */\n\t#isSupportedAttachment(file: File): boolean {\n\t\tif (file.type.startsWith('image/')) return true\n\t\tconst name = file.name.toLowerCase()\n\t\treturn TEXT_ATTACHMENT_EXTENSIONS.some((ext) => name.endsWith(ext))\n\t}\n\n\t/** 将文件读取为 base64 data URL */\n\t#readFileAsDataUrl(file: File): Promise<string> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst reader = new FileReader()\n\t\t\treader.onload = () => resolve(reader.result as string)\n\t\t\treader.onerror = () => reject(reader.error)\n\t\t\treader.readAsDataURL(file)\n\t\t})\n\t}\n\n\t/**\n\t * 校验并读取用户选中的文件,追加为待提交附件。\n\t * 任一文件超出数量/大小/类型限制时,拒绝本次整批选择并提示错误。\n\t */\n\tasync #handleFileSelection(files: FileList): Promise<void> {\n\t\tconst incoming = Array.from(files)\n\n\t\tif (this.#pendingAttachments.length + incoming.length > MAX_ATTACHMENTS) {\n\t\t\tthis.#setAttachError(this.#i18n.t('ui.attachTooMany', { max: MAX_ATTACHMENTS }))\n\t\t\treturn\n\t\t}\n\n\t\tfor (const file of incoming) {\n\t\t\tif (file.size > MAX_ATTACHMENT_SIZE) {\n\t\t\t\tthis.#setAttachError(\n\t\t\t\t\tthis.#i18n.t('ui.attachTooLarge', {\n\t\t\t\t\t\tname: file.name,\n\t\t\t\t\t\tmaxMB: MAX_ATTACHMENT_SIZE / (1024 * 1024),\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.#isSupportedAttachment(file)) {\n\t\t\t\tthis.#setAttachError(this.#i18n.t('ui.attachUnsupportedType', { name: file.name }))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.#setAttachError('')\n\t\tconst dataUrls = await Promise.all(incoming.map((file) => this.#readFileAsDataUrl(file)))\n\t\tincoming.forEach((file, index) => {\n\t\t\tthis.#pendingAttachments.push({\n\t\t\t\tname: file.name,\n\t\t\t\tmimeType: file.type,\n\t\t\t\tdataUrl: dataUrls[index],\n\t\t\t})\n\t\t})\n\n\t\tthis.#renderAttachments()\n\t}\n\n\t/** 重新渲染已选附件的预览条 */\n\t#renderAttachments(): void {\n\t\tthis.#attachmentsBar.innerHTML = this.#pendingAttachments\n\t\t\t.map((attachment, index) => {\n\t\t\t\tconst icon = attachment.mimeType.startsWith('image/') ? '🖼️' : '📄'\n\t\t\t\treturn `\n\t\t\t\t\t<span class=\"${styles.attachmentChip}\">\n\t\t\t\t\t\t<span>${icon} ${escapeHtml(truncate(attachment.name, 20))}</span>\n\t\t\t\t\t\t<button type=\"button\" class=\"${styles.attachmentChipRemove}\" data-remove-index=\"${index}\">×</button>\n\t\t\t\t\t</span>\n\t\t\t\t`\n\t\t\t})\n\t\t\t.join('')\n\t\tthis.#syncInputHeight()\n\t}\n\n\t/** 移除一个已选附件 */\n\t#removeAttachment(index: number): void {\n\t\tthis.#pendingAttachments.splice(index, 1)\n\t\tthis.#renderAttachments()\n\t}\n\n\t/**\n\t * 检查是否应显示输入区域\n\t */\n\t#shouldShowInputArea(): boolean {\n\t\t// 等待用户输入时始终显示输入区域\n\t\tif (this.#isWaitingForUserAnswer) return true\n\n\t\tconst history = this.#agent.history\n\t\tif (history.length === 0) {\n\t\t\treturn true // 初始状态\n\t\t}\n\n\t\tconst status = this.#agent.status\n\t\tconst isTaskEnded = status === 'completed' || status === 'error' || status === 'stopped'\n\n\t\t// 仅当配置允许时,才在任务完成后显示输入区域\n\t\tif (isTaskEnded) {\n\t\t\treturn this.#config.promptForNextTask ?? true\n\t\t}\n\n\t\treturn false\n\t}\n\n\t/** 创建 UI 根元素并挂载到 body,返回根容器 */\n\t#createWrapper(): HTMLElement {\n\t\tconst taskInputMaxLength = 1000\n\t\tconst wrapper = document.createElement('div')\n\t\twrapper.id = 'my-page-agent-runtime_agent-ui'\n\t\twrapper.className = styles.wrapper\n\t\twrapper.setAttribute('data-browser-use-ignore', 'true')\n\t\twrapper.setAttribute('data-page-agent-ignore', 'true')\n\n\t\twrapper.innerHTML = `\n\t\t\t<div class=\"${styles.historySectionWrapper}\">\n\t\t\t\t<div class=\"${styles.historySection}\">\n\t\t\t\t\t<div class=\"${styles.historyItem}\">\n\t\t\t\t\t\t<div class=\"${styles.historyContent}\">\n\t\t\t\t\t\t\t<span class=\"${styles.statusIcon}\">🧠</span>\n\t\t\t\t\t\t\t<span>${this.#i18n.t('ui.waitingPlaceholder')}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"${styles.shell}\">\n\t\t\t\t<div class=\"${styles.background}\"></div>\n\t\t\t\t<div class=\"${styles.shellInner}\">\n\t\t\t\t\t<div class=\"${styles.header}\">\n\t\t\t\t\t\t<div class=\"${styles.statusSection}\">\n\t\t\t\t\t\t\t<div class=\"${styles.indicator} ${styles.thinking}\"></div>\n\t\t\t\t\t\t\t<div class=\"${styles.statusText}\">${this.#i18n.t('ui.ready')}</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"${styles.controls}\">\n\t\t\t\t\t\t\t<button class=\"${styles.controlButton} ${styles.expandButton}\" title=\"${this.#i18n.t('ui.expand')}\">\n\t\t\t\t\t\t\t\t▼\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button class=\"${styles.controlButton} ${styles.stopButton}\" title=\"${this.#i18n.t('ui.close')}\">\n\t\t\t\t\t\t\t\tX\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"${styles.inputSectionWrapper} ${styles.hidden}\">\n\t\t\t\t\t\t<div class=\"${styles.attachmentsBar}\"></div>\n\t\t\t\t\t\t<div class=\"${styles.attachError}\"></div>\n\t\t\t\t\t\t<div class=\"${styles.inputSection}\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"${styles.attachButton}\"\n\t\t\t\t\t\t\t\ttitle=\"${this.#i18n.t('ui.attachButtonTitle')}\"\n\t\t\t\t\t\t\t>📎</button>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"file\"\n\t\t\t\t\t\t\t\tclass=\"${styles.fileInput}\"\n\t\t\t\t\t\t\t\taccept=\"image/*,${TEXT_ATTACHMENT_EXTENSIONS.join(',')}\"\n\t\t\t\t\t\t\t\tmultiple\n\t\t\t\t\t\t\t\thidden\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\t\tclass=\"${styles.taskInput}\"\n\t\t\t\t\t\t\t\tmaxlength=\"${taskInputMaxLength}\"\n\t\t\t\t\t\t\t\trows=\"1\"\n\t\t\t\t\t\t\t></textarea>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t`\n\n\t\tdocument.body.appendChild(wrapper)\n\t\treturn wrapper\n\t}\n\n\t/** 绑定所有 DOM 事件监听器(头部点击、展开按钮、动作按钮、回车提交等) */\n\t#setupEventListeners(): void {\n\t\t// 点击头部区域可展开/收起\n\t\tconst header = this.wrapper.querySelector(`.${styles.header}`)!\n\t\theader.addEventListener('click', (e) => {\n\t\t\t// 点击按钮时不触发展开/收起\n\t\t\tif ((e.target as HTMLElement).closest(`.${styles.controlButton}`)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.#toggle()\n\t\t})\n\n\t\t// 展开按钮\n\t\tthis.#expandButton.addEventListener('click', (e) => {\n\t\t\te.stopPropagation()\n\t\t\tthis.#toggle()\n\t\t})\n\n\t\t// 动作按钮(停止 / 关闭)\n\t\tthis.#actionButton.addEventListener('click', (e) => {\n\t\t\te.stopPropagation()\n\t\t\tthis.#handleActionButton()\n\t\t})\n\n\t\t// 回车提交;Shift+Enter 插入换行(多行输入)\n\t\tthis.#taskInput.addEventListener('keydown', (e) => {\n\t\t\tif (e.isComposing) return // 忽略输入法组合键\n\t\t\tif (e.key === 'Enter' && !e.shiftKey) {\n\t\t\t\te.preventDefault()\n\t\t\t\tthis.#submitTask()\n\t\t\t}\n\t\t})\n\n\t\t// 阻止输入区域点击事件冒泡\n\t\tthis.#inputSection.addEventListener('click', (e) => {\n\t\t\te.stopPropagation()\n\t\t})\n\n\t\t// 附件按钮:点击触发隐藏的文件选择框\n\t\tthis.#attachButton.addEventListener('click', () => {\n\t\t\tthis.#fileInput.click()\n\t\t})\n\n\t\t// 文件选择变化:校验并读取选中的文件\n\t\tthis.#fileInput.addEventListener('change', () => {\n\t\t\tconst files = this.#fileInput.files\n\t\t\tif (files?.length) void this.#handleFileSelection(files)\n\t\t\tthis.#fileInput.value = '' // 清空选择,允许重复选择同一文件\n\t\t})\n\n\t\t// 点击附件预览条上的删除按钮\n\t\tthis.#attachmentsBar.addEventListener('click', (e) => {\n\t\t\tconst removeButton = (e.target as HTMLElement).closest(`.${styles.attachmentChipRemove}`)\n\t\t\tif (!(removeButton instanceof HTMLElement)) return\n\t\t\tthis.#removeAttachment(Number(removeButton.dataset.removeIndex))\n\t\t})\n\t}\n\n\t/** 在展开/收起状态之间切换 */\n\t#toggle(): void {\n\t\tif (this.#isExpanded) {\n\t\t\tthis.#collapse()\n\t\t} else {\n\t\t\tthis.#expand()\n\t\t}\n\t}\n\n\t/**\n\t * 展开历史弹窗。\n\t *\n\t * `max-height` 由 JS 测量(而非动画到固定的 CSS 上限),因此\n\t * 过渡距离始终匹配真实内容高度 — 否则展开/收起会给人\n\t * 速度不一致的感觉(展开会提前达到真实高度并停滞,\n\t * 而收起每次都按完整上限做动画)。\n\t */\n\t#expand(): void {\n\t\tthis.#isExpanded = true\n\t\tthis.wrapper.classList.add(styles.expanded)\n\t\tthis.#expandButton.textContent = '▲'\n\t\tthis.#syncHistoryHeight()\n\t}\n\n\t/** 收起历史弹窗 */\n\t#collapse(): void {\n\t\tthis.#isExpanded = false\n\t\tthis.wrapper.classList.remove(styles.expanded)\n\t\tthis.#expandButton.textContent = '▼'\n\t\tthis.#historySectionWrapper.style.maxHeight = '0px'\n\t}\n\n\t/** 将历史弹窗的 max-height 同步为当前(已封顶的)内容高度 */\n\t#syncHistoryHeight(): void {\n\t\tif (!this.#isExpanded) return\n\t\tthis.#historySectionWrapper.style.maxHeight = `${this.#historySectionWrapper.scrollHeight}px`\n\t}\n\n\t/**\n\t * 启动周期性的头部更新循环\n\t */\n\t#startHeaderUpdateLoop(): void {\n\t\t// 每 450ms 检查一次(与动画总时长一致)\n\t\tthis.#headerUpdateTimer = setInterval(() => {\n\t\t\tthis.#checkAndUpdateHeader()\n\t\t}, 450)\n\t}\n\n\t/**\n\t * 停止周期性的头部更新循环\n\t */\n\t#stopHeaderUpdateLoop(): void {\n\t\tif (this.#headerUpdateTimer) {\n\t\t\tclearInterval(this.#headerUpdateTimer)\n\t\t\tthis.#headerUpdateTimer = null\n\t\t}\n\t}\n\n\t/** 设置一个受控延时定时器(销毁后自动忽略回调) */\n\t#setTimer(callback: () => void, delay: number): void {\n\t\tconst timer = setTimeout(() => {\n\t\t\tthis.#timers.delete(timer)\n\t\t\tif (!this.#disposed) callback()\n\t\t}, delay)\n\t\tthis.#timers.add(timer)\n\t}\n\n\t/**\n\t * 检查头部是否需要更新,若当前未在动画中则触发动画\n\t */\n\t#checkAndUpdateHeader(): void {\n\t\t// 无待处理文本或当前正在动画中,则跳过\n\t\tif (!this.#pendingHeaderText || this.#isAnimating) {\n\t\t\treturn\n\t\t}\n\n\t\t// 若文本已显示,则清空待处理文本并跳过\n\t\tif (this.#statusText.textContent === this.#pendingHeaderText) {\n\t\t\tthis.#pendingHeaderText = null\n\t\t\treturn\n\t\t}\n\n\t\t// 开始动画\n\t\tconst textToShow = this.#pendingHeaderText\n\t\tthis.#pendingHeaderText = null\n\t\tthis.#animateTextChange(textToShow)\n\t}\n\n\t/**\n\t * 以淡出/淡入效果动画切换文本\n\t */\n\t#animateTextChange(newText: string): void {\n\t\tthis.#isAnimating = true\n\n\t\t// 淡出当前文本\n\t\tthis.#statusText.classList.add(styles.fadeOut)\n\n\t\tthis.#setTimer(() => {\n\t\t\t// 更新文本内容\n\t\t\tthis.#statusText.textContent = newText\n\n\t\t\t// 淡入新文本\n\t\t\tthis.#statusText.classList.remove(styles.fadeOut)\n\t\t\tthis.#statusText.classList.add(styles.fadeIn)\n\n\t\t\tthis.#setTimer(() => {\n\t\t\t\tthis.#statusText.classList.remove(styles.fadeIn)\n\t\t\t\tthis.#isAnimating = false\n\t\t\t}, 300)\n\t\t}, 150) // 淡出动画时长的一半\n\t}\n\n\t/** 更新状态指示器的样式类 */\n\t#updateStatusIndicator(\n\t\ttype:\n\t\t\t| 'idle'\n\t\t\t| 'running'\n\t\t\t| 'thinking'\n\t\t\t| 'executing'\n\t\t\t| 'executed'\n\t\t\t| 'retrying'\n\t\t\t| 'completed'\n\t\t\t| 'error'\n\t\t\t| 'stopped'\n\t): void {\n\t\t// `running` 按 thinking 方式动画;`idle`/`stopped` 使用中性基础样式。\n\t\tconst variant =\n\t\t\ttype === 'running'\n\t\t\t\t? 'thinking'\n\t\t\t\t: type === 'executing'\n\t\t\t\t\t? 'tool_executing'\n\t\t\t\t\t: type === 'retrying'\n\t\t\t\t\t\t? 'retry'\n\t\t\t\t\t\t: type\n\t\tthis.#indicator.className = styles.indicator\n\t\tif (variant !== 'idle' && variant !== 'stopped') {\n\t\t\tthis.#indicator.classList.add(styles[variant])\n\t\t}\n\t}\n\n\t/** 将历史区域滚动到底部 */\n\t#scrollToBottom(): void {\n\t\t// 在下一轮事件循环中执行,确保 DOM 更新完成\n\t\tthis.#setTimer(() => {\n\t\t\tthis.#historySection.scrollTop = this.#historySection.scrollHeight\n\t\t}, 0)\n\t}\n\n\t/**\n\t * 直接从 agent.history 渲染历史\n\t *\n\t * 渲染内容:\n\t * 1. 任务卡片(每个 task_start 事件对应一张,history 跨任务持久保留,因此可展示历次任务)\n\t * 2. 反思卡片(评估、记忆、下一步目标)\n\t * 3. 工具执行及输出\n\t * 4. 观察\n\t */\n\t#renderHistory(): void {\n\t\tconst items: string[] = []\n\n\t\tconst history = this.#agent.history\n\n\t\t// 兼容旧状态:若历史中还没有 task_start 事件(例如尚未产生任何事件),\n\t\t// 用当前 agent.task 兜底渲染一张任务卡片\n\t\tif (history.length === 0 && this.#agent.task) {\n\t\t\titems.push(this.#createTaskCard(this.#agent.task))\n\t\t}\n\n\t\tfor (const event of history) {\n\t\t\titems.push(...this.#createHistoryCards(event))\n\t\t}\n\n\t\tthis.#historySection.innerHTML = items.join('')\n\t\tthis.#scrollToBottom()\n\t\tthis.#syncHistoryHeight()\n\t}\n\n\t/** 生成任务卡片 HTML;若任务附带附件,附加一行文件名列表 */\n\t#createTaskCard(task: string, attachmentNames?: string[]): string {\n\t\tif (!attachmentNames?.length) {\n\t\t\treturn createCard({ icon: '🎯', content: task, type: 'input' })\n\t\t}\n\t\treturn createCard({\n\t\t\ticon: '🎯',\n\t\t\tcontent: [task, this.#i18n.t('ui.attachmentsLabel', { names: attachmentNames.join(', ') })],\n\t\t\ttype: 'input',\n\t\t})\n\t}\n\n\t/** 为一条历史事件生成卡片 */\n\t#createHistoryCards(event: UIAdapter['history'][number]): string[] {\n\t\tconst cards: string[] = []\n\t\tconst meta =\n\t\t\tevent.type === 'step' && event.stepIndex !== undefined\n\t\t\t\t? this.#i18n.t('ui.step', {\n\t\t\t\t\t\tnumber: (event.stepIndex + 1).toString(),\n\t\t\t\t\t})\n\t\t\t\t: undefined\n\n\t\tif (event.type === 'task_start') {\n\t\t\tif (event.task) cards.push(this.#createTaskCard(event.task, event.attachmentNames))\n\t\t} else if (event.type === 'step') {\n\t\t\t// 反思卡片\n\t\t\tif (event.reflection) {\n\t\t\t\tconst lines = createReflectionLines(event.reflection)\n\t\t\t\tif (lines.length > 0) {\n\t\t\t\t\tcards.push(createCard({ icon: '🧠', content: lines, meta }))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 动作卡片\n\t\t\tconst action = event.action\n\t\t\tif (action) {\n\t\t\t\tcards.push(...this.#createActionCards(action, meta))\n\t\t\t}\n\t\t} else if (event.type === 'observation') {\n\t\t\tcards.push(\n\t\t\t\tcreateCard({ icon: '👁️', content: event.content || '', meta, type: 'observation' })\n\t\t\t)\n\t\t} else if (event.type === 'user_takeover') {\n\t\t\tcards.push(createCard({ icon: '👤', content: 'User takeover', meta, type: 'input' }))\n\t\t} else if (event.type === 'retry') {\n\t\t\tconst retryInfo = `${event.message || 'Retrying'} (${event.attempt}/${event.maxAttempts})`\n\t\t\tcards.push(createCard({ icon: '🔄', content: retryInfo, meta, type: 'observation' }))\n\t\t} else if (event.type === 'error') {\n\t\t\tcards.push(\n\t\t\t\tcreateCard({ icon: '❌', content: event.message || 'Error', meta, type: 'observation' })\n\t\t\t)\n\t\t}\n\n\t\treturn cards\n\t}\n\n\t/** 为一个动作生成卡片 */\n\t#createActionCards(\n\t\taction: { name: string; input: unknown; output: string },\n\t\tmeta?: string\n\t): string[] {\n\t\tconst cards: string[] = []\n\n\t\tif (action.name === 'done') {\n\t\t\tconst input = action.input as { text?: string; success?: boolean }\n\t\t\tconst text = input.text || action.output || ''\n\t\t\tif (text) {\n\t\t\t\tcards.push(\n\t\t\t\t\tcreateCard({\n\t\t\t\t\t\ticon: '🤖',\n\t\t\t\t\t\tcontent: text,\n\t\t\t\t\t\tmeta,\n\t\t\t\t\t\ttype: input.success === true ? 'doneSuccess' : 'doneError',\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t}\n\t\t} else if (action.name === 'ask_user') {\n\t\t\tconst input = action.input as { question?: string }\n\t\t\tconst answer = action.output.replace(/^User answered:\\s*/i, '')\n\t\t\tcards.push(\n\t\t\t\tcreateCard({\n\t\t\t\t\ticon: '❓',\n\t\t\t\t\tcontent: `Question: ${input.question || ''}`,\n\t\t\t\t\tmeta,\n\t\t\t\t\ttype: 'question',\n\t\t\t\t})\n\t\t\t)\n\t\t\tcards.push(createCard({ icon: '💬', content: `Answer: ${answer}`, meta, type: 'input' }))\n\t\t} else {\n\t\t\tconst toolText = this.#getToolExecutingText(action.name, action.input)\n\t\t\tcards.push(createCard({ icon: '🔨', content: toolText, meta }))\n\t\t\tif (action.output?.length > 0) {\n\t\t\t\tcards.push(createCard({ icon: '🔨', content: action.output, meta, type: 'output' }))\n\t\t\t}\n\t\t}\n\n\t\treturn cards\n\t}\n}\n","/**\n * LLM 调用相关的错误类型与错误处理。\n */\n\nexport const InvokeErrorTypes = {\n\t// 可重试\n\tNETWORK_ERROR: 'network_error', // 网络错误,可重试\n\tRATE_LIMIT: 'rate_limit', // 触发限流,可重试\n\tSERVER_ERROR: 'server_error', // 5xx 服务端错误,可重试\n\tNO_TOOL_CALL: 'no_tool_call', // 模型没有调用工具\n\tINVALID_TOOL_ARGS: 'invalid_tool_args', // 工具参数与 schema 不匹配\n\tTOOL_EXECUTION_ERROR: 'tool_execution_error', // 工具执行出错\n\tINVALID_RESPONSE: 'invalid_response', // 响应体不是合法 JSON\n\tINVALID_SCHEMA: 'invalid_schema', // 响应是合法 JSON,但不符合预期结构\n\n\tUNKNOWN: 'unknown', // 未知错误(默认视为可重试)\n\n\t// 不可重试\n\tCONFIG_ERROR: 'config_error', // 本地配置或 hook 无效\n\tAUTH_ERROR: 'auth_error', // 认证失败\n\tCONTEXT_LENGTH: 'context_length', // 提示词过长\n\tCONTENT_FILTER: 'content_filter', // 内容被安全系统过滤\n} as const\n\ntype InvokeErrorType = (typeof InvokeErrorTypes)[keyof typeof InvokeErrorTypes] // 所有错误类型的联合类型\n\n// 可重试的错误类型列表,命中这些类型的错误可进行重试\nconst RETRYABLE_TYPES: readonly InvokeErrorType[] = [\n\tInvokeErrorTypes.NETWORK_ERROR,\n\tInvokeErrorTypes.RATE_LIMIT,\n\tInvokeErrorTypes.SERVER_ERROR,\n\tInvokeErrorTypes.NO_TOOL_CALL,\n\tInvokeErrorTypes.INVALID_TOOL_ARGS,\n\tInvokeErrorTypes.TOOL_EXECUTION_ERROR,\n\tInvokeErrorTypes.INVALID_RESPONSE,\n\tInvokeErrorTypes.INVALID_SCHEMA,\n\tInvokeErrorTypes.UNKNOWN,\n]\n\nexport class InvokeError extends Error {\n\ttype: InvokeErrorType // 错误类型\n\tretryable: boolean // 是否可重试\n\tstatusCode?: number // HTTP 状态码\n\t/* 原始错误对象(当本错误由其他错误引发时提供) */\n\trawError?: unknown // 原始错误对象(当本错误由其他错误引发时提供)\n\t/* API 原始响应(当本错误由 API 调用引发时提供) */\n\trawResponse?: unknown // API 原始响应(当本错误由 API 调用引发时提供)\n\n\t// 构造函数:根据错误类型创建错误实例,并自动判断是否可重试\n\tconstructor(type: InvokeErrorType, message: string, rawError?: unknown, rawResponse?: unknown) {\n\t\tsuper(message)\n\t\tthis.name = 'InvokeError'\n\t\tthis.type = type\n\t\tthis.retryable = RETRYABLE_TYPES.includes(type)\n\t\tthis.rawError = rawError\n\t\tthis.rawResponse = rawResponse\n\t}\n}\n","// 终端支持的颜色名称,对应 ANSI 标准色\ntype TerminalColor = 'blue' | 'cyan' | 'gray' | 'green' | 'red' | 'yellow'\n\n// 颜色名称到 ANSI 颜色码的映射表\nconst colorCodes: Record<TerminalColor, number> = {\n\tblue: 34, // 蓝色前景色码\n\tcyan: 36, // 青色前景色码\n\tgray: 90, // 灰色前景色码\n\tgreen: 32, // 绿色前景色码\n\tred: 31, // 红色前景色码\n\tyellow: 33, // 黄色前景色码\n}\n\n/**\n * 使用标准 ANSI 终端颜色转义序列格式化文本。\n * 给文本添加标准的 ANSI 终端颜色转义序列,使文本在终端中以指定颜色显示。\n * @param text 需要上色的原始文本\n * @param color 终端颜色名称\n * @param bold 是否加粗显示,默认 false\n * @returns 带 ANSI 颜色转义序列的文本\n */\nexport function formatTerminalText(text: string, color: TerminalColor, bold = false): string {\n\tconst weight = bold ? '\\u001B[1m' : '' // 加粗转义序列,非加粗时为空字符串\n\treturn `${weight}\\u001B[${colorCodes[color]}m${text}\\u001B[0m`\n}\n","/**\n * LLM 集成的工具函数\n */\nimport * as z from 'zod/v4'\n\nimport { formatTerminalText } from '../shared/terminal'\n\nimport type { Tool } from './types'\n\nconst debug = console.debug.bind(console, formatTerminalText('[LLM]', 'gray'))\n\n/**\n * 将 Zod schema 转换为 OpenAI 工具格式\n * 使用 Zod 4 原生 z.toJSONSchema()\n *\n * @param name - 工具名称\n * @param tool - 工具定义\n * @returns OpenAI 格式的工具定义\n */\nexport function zodToOpenAITool(name: string, tool: Tool) {\n\treturn {\n\t\ttype: 'function' as const, // 工具类型,固定为函数调用\n\t\tfunction: {\n\t\t\tname, // 工具名称\n\t\t\tdescription: tool.description, // 工具描述\n\t\t\tparameters: z.toJSONSchema(tool.inputSchema, { target: 'openapi-3.0' }), // 参数 JSON Schema(OpenAPI 3.0 格式)\n\t\t},\n\t}\n}\n\n/**\n * 为特定模型打补丁。仅修补已知的模型。\n *\n * @purpose\n * - 调和各模型所接受的参数 schema 之间的差异。\n * - 禁用思考/推理,或在无法完全禁用时将其降到最低。\n * - 尽量减少返回的 token。\n * - 对已知的较小模型提高 temperature,以提升自动恢复的几率。\n * @note 若用户显式设置了 temperature 则尊重该设置\n *\n * @todo 需要针对不同厂商的补丁。\n * 本地托管和第三方托管的模型可能有不同的 schema。\n *\n * @param body - 请求体对象(会就地修改)\n * @param baseURL - 模型服务的基础 URL,用于识别 provider\n * @returns 修改后的请求体对象\n */\nexport function modelPatch(body: Record<string, any>, baseURL?: string) {\n\tconst model: string = body.model || '' // 模型名称\n\tif (!model) return body\n\n\tconst provider = getProvider(baseURL) // 判断服务提供商\n\n\tconst modelName = normalizeModelName(model) // 归一化后的模型名\n\n\tif (modelName.startsWith('qwen')) {\n\t\tdebug('Patch Qwen: disable thinking')\n\t\tbody.enable_thinking = false\n\t\tif (body.temperature === undefined && !/max|plus/.test(modelName)) {\n\t\t\tdebug('Patch Qwen: raise temperature to 1.0')\n\t\t\tbody.temperature = 1.0\n\t\t}\n\t}\n\n\tif (modelName.startsWith('deepseek')) {\n\t\tdebug('Patch DeepSeek: disable thinking, remove tool_choice')\n\t\tbody.thinking = { type: 'disabled' }\n\t\tdelete body.tool_choice\n\t}\n\n\tif (modelName.startsWith('gpt')) {\n\t\tif (modelName.startsWith('gpt-5')) {\n\t\t\tbody.verbosity = 'low'\n\t\t}\n\n\t\t// 自 gpt-5.4 起,/chat/completions 在存在函数工具时会拒绝任何显式的 reasoning_effort。\n\t\t// 预期更新的模型也会遵循这一点。\n\t\t// - gpt-5.1 / gpt-5.2 可以完全禁用推理\n\t\t// - gpt-5 / -mini / -nano 最低只能到 \"minimal\"\n\t\t// - 其余模型(gpt-4.x、chat-latest、gpt-5.4+)都不能传入该字段\n\t\tif (modelName.includes('chat-latest')) {\n\t\t\tdebug('Patch chat-latest: omit reasoning_effort and temperature')\n\t\t\tdelete body.reasoning_effort\n\t\t\tdelete body.temperature\n\t\t} else if (/^gpt-5[12](-|$)/.test(modelName)) {\n\t\t\tdebug('Patch GPT-5.1/5.2: reasoning_effort=none')\n\t\t\tbody.reasoning_effort = 'none'\n\t\t} else if (/^gpt-5(-|$)/.test(modelName)) {\n\t\t\tdebug('Patch GPT-5: reasoning_effort=minimal')\n\t\t\tbody.reasoning_effort = 'minimal'\n\t\t} else {\n\t\t\tdebug('Patch GPT: omit reasoning_effort')\n\t\t\tdelete body.reasoning_effort\n\t\t}\n\t}\n\n\tif (modelName.startsWith('claude')) {\n\t\tif (/opus|sonnet|haiku/.test(modelName)) {\n\t\t\tdebug('Patch Claude: disable thinking')\n\t\t\tbody.thinking = { type: 'disabled' }\n\n\t\t\tif (provider !== 'openrouter') {\n\t\t\t\t// 将 tool_choice 转换为 Claude 格式\n\t\t\t\tif (body.tool_choice === 'required') {\n\t\t\t\t\t// 'required' -> { type: 'any' }(必须调用某个工具)\n\t\t\t\t\tdebug('Applying Claude patch: convert tool_choice \"required\" to { type: \"any\" }')\n\t\t\t\t\tbody.tool_choice = { type: 'any' }\n\t\t\t\t} else if (body.tool_choice?.function?.name) {\n\t\t\t\t\t// { type: 'function', function: { name: '...' } } -> { type: 'tool', name: '...' }\n\t\t\t\t\tdebug('Applying Claude patch: convert tool_choice format')\n\t\t\t\t\tbody.tool_choice = { type: 'tool', name: body.tool_choice.function.name }\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tdebug('Patch Claude: reasoning_effort=low')\n\t\t\tbody.reasoning_effort = 'low'\n\n\t\t\t// Fable 和 mythos 无法禁用自适应思考。\n\t\t\t// Claude 在扩展思考(extended thinking)下不支持 tool_choice。\n\t\t\t// 这两个概念界限模糊,基本上开启思考就不能用 tool_choice。\n\t\t\tdelete body.tool_choice\n\t\t}\n\t}\n\n\tif (modelName.startsWith('gemini')) {\n\t\tdebug('Patch Gemini: reasoning_effort=low')\n\t\tbody.reasoning_effort = 'low'\n\t\tif (/^gemini-25(?!.*pro)/.test(modelName)) {\n\t\t\tdebug('Patch Gemini 2.5 non-Pro: reasoning_effort=none')\n\t\t\tbody.reasoning_effort = 'none'\n\t\t} else if (\n\t\t\tmodelName.startsWith('gemini-35-flash') ||\n\t\t\tmodelName.startsWith('gemini-31-flash-lite') ||\n\t\t\tmodelName.startsWith('gemini-3-flash')\n\t\t) {\n\t\t\tdebug('Patch Gemini 3.x Flash/Lite: reasoning_effort=minimal')\n\t\t\tbody.reasoning_effort = 'minimal'\n\t\t}\n\t}\n\n\tif (modelName.startsWith('glm')) {\n\t\tdebug('Patch GLM: disable thinking')\n\t\tbody.thinking = { type: 'disabled' }\n\t}\n\n\tif (modelName.startsWith('hy')) {\n\t\tdebug('Patch Hunyuan: disable thinking, reasoning_effort=low')\n\t\tbody.thinking = { type: 'disabled' }\n\t\tbody.reasoning_effort = 'low'\n\t}\n\n\tif (modelName.startsWith('grok')) {\n\t\tif (/^grok-4-?3/.test(modelName)) {\n\t\t\tdebug('Patch Grok 4.3: reasoning_effort=none')\n\t\t\tbody.reasoning_effort = 'none'\n\t\t} else if (modelName.startsWith('grok-3-mini') || modelName.startsWith('grok-code-fast')) {\n\t\t\tdebug('Patch Grok mini/code: reasoning_effort=low')\n\t\t\tbody.reasoning_effort = 'low'\n\t\t}\n\t}\n\n\tif (modelName.startsWith('kimi')) {\n\t\tif (modelName.startsWith('kimi-k3')) {\n\t\t\t// Kimi K3 始终会思考,且在思考时会拒绝指定工具名(named tool choice)。\n\t\t\tdebug('Patch Kimi K3: use required tool choice, remove parallel tool calls')\n\t\t\tdelete body.parallel_tool_calls\n\t\t\tif (body.tool_choice?.function?.name) body.tool_choice = 'required'\n\t\t} else if (!modelName.includes('code')) {\n\t\t\t// kimi-k2.7-code 无法禁用思考\n\t\t\tdebug('Patch Kimi: disable thinking')\n\t\t\tbody.thinking = { type: 'disabled' }\n\t\t}\n\t}\n\n\tif (modelName.startsWith('minimax')) {\n\t\tdebug('Patch MiniMax: remove parallel_tool_calls')\n\t\tdelete body.parallel_tool_calls\n\n\t\tif (modelName.includes('m3')) {\n\t\t\t// 只有 M3 可以禁用思考\n\t\t\tdebug('Patch MiniMax: disable thinking')\n\t\t\tbody.thinking = { type: 'disabled' }\n\t\t}\n\t}\n\n\t// provider 补丁\n\n\tif (provider === 'openrouter') {\n\t\t// openrouter 使用 reasoning 对象而不是 reasoning_effort\n\n\t\t// 读取当前是否禁用推理及推理等级\n\t\tconst reasoningEffort = body.reasoning_effort\n\t\tconst reasoningDisabled =\n\t\t\tbody.thinking?.type === 'disabled' ||\n\t\t\tbody.enable_thinking === false ||\n\t\t\treasoningEffort === 'none'\n\n\t\tif (reasoningDisabled) {\n\t\t\t// 禁用推理\n\t\t\tbody.reasoning = { enabled: false }\n\t\t} else if (reasoningEffort) {\n\t\t\t// 启用推理并指定等级\n\t\t\tbody.reasoning = { enabled: true, effort: reasoningEffort }\n\t\t}\n\t}\n\n\treturn body\n}\n\n/**\n * 检查给定的模型 ID 是否符合某个具体模型名称\n *\n * @note\n * 不同的模型服务商可能对同一模型使用不同的模型 ID。\n * 例如,OpenAI 的 `gpt-5.2` 可能被命名为:\n *\n * - `gpt-5.2-version`\n * - `gpt-5_2-date`\n * - `GPT-52-version-date`\n * - `openai/gpt-5.2-chat`\n *\n * 它们应被视为同一模型。\n * 将其归一化为 `gpt-52`\n */\n/**\n * 归一化模型名称,将不同厂商对同一模型的命名统一为同一形式(如 `gpt-5.2` -> `gpt52`)\n *\n * @param modelName - 原始模型名称\n * @returns 归一化后的模型名称(小写、去掉前缀与特殊字符)\n */\nexport function normalizeModelName(modelName: string): string {\n\tlet normalizedName = modelName.toLowerCase()\n\n\t// 去掉 '/' 之前的前缀\n\tif (normalizedName.includes('/')) {\n\t\tnormalizedName = normalizedName.split('/')[1]\n\t}\n\n\t// 去掉 '_'\n\tnormalizedName = normalizedName.replace(/_/g, '')\n\n\t// 去掉 '.'\n\tnormalizedName = normalizedName.replace(/\\./g, '')\n\n\treturn normalizedName\n}\n\n/**\n * 根据 baseURL 判断服务提供商(目前仅识别 openrouter)\n *\n * @param baseURL - 模型服务的基础 URL\n * @returns 'openrouter' 或 undefined\n */\nexport function getProvider(baseURL?: string): 'openrouter' | undefined {\n\tif (!baseURL) return undefined\n\ttry {\n\t\tconst url = new URL(baseURL)\n\t\tconst hostname = url.hostname\n\t\tif (hostname === 'openrouter.ai') return 'openrouter'\n\t\treturn undefined\n\t} catch {\n\t\treturn undefined\n\t}\n}\n","/**\n * OpenAI 客户端实现\n */\nimport * as z from 'zod/v4'\n\nimport { InvokeError, InvokeErrorTypes } from './errors'\nimport type {\n\tInvokeOptions,\n\tInvokeResult,\n\tLLMClient,\n\tMessage,\n\tResolvedLLMConfig,\n\tTool,\n} from './types'\nimport { modelPatch, zodToOpenAITool } from './utils'\n\n/**\n * 兼容 OpenAI 的 API 客户端\n */\nexport class OpenAICompatibleClient implements LLMClient {\n\tconfig: ResolvedLLMConfig // 解析后的 LLM 配置\n\tprivate fetch: typeof globalThis.fetch // 实际发起请求用的 fetch 函数\n\n\t// 构造函数:保存配置并绑定自定义 fetch 函数\n\tconstructor(config: ResolvedLLMConfig) {\n\t\tthis.config = config\n\t\tthis.fetch = config.customFetch\n\t}\n\n\t/**\n\t * 调用 LLM 接口并执行模型请求的工具\n\t * 流程:转换工具定义 -> 构建并转换请求体 -> 调用 API -> 处理 HTTP 错误 -> 解析并校验响应 -> 执行工具\n\t *\n\t * @param messages - 对话消息列表\n\t * @param tools - 可用工具集合\n\t * @param abortSignal - 中止信号,用于取消请求\n\t * @param options - 可选的调用选项\n\t * @returns 工具调用结果\n\t */\n\tasync invoke(\n\t\tmessages: Message[],\n\t\ttools: Record<string, Tool>,\n\t\tabortSignal?: AbortSignal,\n\t\toptions?: InvokeOptions\n\t): Promise<InvokeResult> {\n\t\tabortSignal?.throwIfAborted()\n\n\t\t// 1. 将工具转换为 OpenAI 格式\n\t\tconst openaiTools = Object.entries(tools).map(([name, t]) => zodToOpenAITool(name, t))\n\n\t\t// 构建请求体\n\n\t\t// 工具选择策略:未指定具体工具且允许命名工具时使用 'required',否则指定具体工具名\n\t\tlet toolChoice: unknown = 'required'\n\t\tif (options?.toolChoiceName && !this.config.disableNamedToolChoice) {\n\t\t\t// 指定要强制调用的工具\n\t\t\ttoolChoice = { type: 'function', function: { name: options.toolChoiceName } }\n\t\t}\n\n\t\tconst requestBody: Record<string, unknown> = {\n\t\t\tmodel: this.config.model, // 模型名称\n\t\t\tmessages, // 对话消息列表\n\t\t\ttools: openaiTools, // OpenAI 格式的工具定义列表\n\t\t\tparallel_tool_calls: false, // 禁用并行工具调用\n\t\t\ttool_choice: toolChoice, // 工具选择策略\n\t\t}\n\t\t// 仅在调用方显式设置了 temperature 时才发送该字段。大多数新模型会在设置了该字段时直接报错。\n\t\tif (this.config.temperature !== undefined) {\n\t\t\trequestBody.temperature = this.config.temperature\n\t\t}\n\n\t\t// 根据模型类型打补丁(调整不同模型的参数差异)\n\t\tmodelPatch(requestBody, this.config.baseURL)\n\n\t\t// 应用 transformRequestBody 进行请求体自定义转换\n\t\tlet transformedBody: Record<string, unknown> | undefined\n\t\ttry {\n\t\t\ttransformedBody = this.config.transformRequestBody(requestBody)\n\t\t} catch (error) {\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.CONFIG_ERROR,\n\t\t\t\t`transformRequestBody failed: ${(error as Error).message}`,\n\t\t\t\terror\n\t\t\t)\n\t\t}\n\t\tconst finalRequestBody = transformedBody ?? requestBody\n\n\t\t// 2. 调用 API\n\t\tlet response: Response\n\t\ttry {\n\t\t\tresponse = await this.fetch(`${this.config.baseURL}/chat/completions`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t...(this.config.apiKey && { Authorization: `Bearer ${this.config.apiKey}` }),\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(finalRequestBody),\n\t\t\t\tsignal: abortSignal,\n\t\t\t})\n\t\t} catch (error: unknown) {\n\t\t\tif ((error as any)?.name === 'AbortError') throw error\n\t\t\tconsole.error(error)\n\t\t\tthrow new InvokeError(InvokeErrorTypes.NETWORK_ERROR, 'Network request failed', error)\n\t\t}\n\n\t\t// 3. 处理 HTTP 错误\n\t\tif (!response.ok) {\n\t\t\tlet errorData: any\n\t\t\ttry {\n\t\t\t\terrorData = await response.json()\n\t\t\t} catch (error) {\n\t\t\t\tif ((error as any)?.name === 'AbortError') throw error\n\t\t\t}\n\t\t\tconst errorMessage = errorData?.error?.message || response.statusText\n\n\t\t\tif (response.status === 401 || response.status === 403) {\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.AUTH_ERROR,\n\t\t\t\t\t`Authentication failed: ${errorMessage}`,\n\t\t\t\t\terrorData\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (response.status === 429) {\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.RATE_LIMIT,\n\t\t\t\t\t`Rate limit exceeded: ${errorMessage}`,\n\t\t\t\t\terrorData\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (response.status >= 500) {\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.SERVER_ERROR,\n\t\t\t\t\t`Server error: ${errorMessage}`,\n\t\t\t\t\terrorData\n\t\t\t\t)\n\t\t\t}\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.UNKNOWN,\n\t\t\t\t`HTTP ${response.status}: ${errorMessage}`,\n\t\t\t\terrorData\n\t\t\t)\n\t\t}\n\n\t\t// 4. 解析并校验响应\n\t\tlet data: any\n\t\ttry {\n\t\t\tdata = await response.json()\n\t\t} catch (error) {\n\t\t\tif ((error as any)?.name === 'AbortError') throw error\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.INVALID_RESPONSE,\n\t\t\t\t'Response body is not valid JSON',\n\t\t\t\terror\n\t\t\t)\n\t\t}\n\n\t\tconst choice = data.choices?.[0]\n\t\tif (!choice) {\n\t\t\tthrow new InvokeError(InvokeErrorTypes.INVALID_SCHEMA, 'No choices in response', data)\n\t\t}\n\n\t\t// 检查 finish_reason\n\t\tswitch (choice.finish_reason) {\n\t\t\tcase 'tool_calls':\n\t\t\tcase 'function_call': // gemini\n\t\t\tcase 'stop': // 部分模型即使带工具调用也会返回 stop\n\t\t\t\tbreak\n\t\t\tcase 'length':\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.CONTEXT_LENGTH,\n\t\t\t\t\t'Response truncated: max tokens reached',\n\t\t\t\t\tundefined,\n\t\t\t\t\tdata\n\t\t\t\t)\n\t\t\tcase 'content_filter':\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.CONTENT_FILTER,\n\t\t\t\t\t'Content filtered by safety system',\n\t\t\t\t\tundefined,\n\t\t\t\t\tdata\n\t\t\t\t)\n\t\t\tdefault:\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.INVALID_SCHEMA,\n\t\t\t\t\t`Unexpected finish_reason: ${choice.finish_reason}`,\n\t\t\t\t\tundefined,\n\t\t\t\t\tdata\n\t\t\t\t)\n\t\t}\n\n\t\t// 若提供了 normalizeResponse 则应用之(用于自动修复格式问题)\n\t\tconst normalizedData = options?.normalizeResponse ? options.normalizeResponse(data) : data\n\t\tconst normalizedChoice = (normalizedData as any).choices?.[0]\n\n\t\t// 从响应中获取工具名称\n\t\tconst toolCallName = normalizedChoice?.message?.tool_calls?.[0]?.function?.name\n\t\tif (!toolCallName) {\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.NO_TOOL_CALL,\n\t\t\t\t'No tool call found in response',\n\t\t\t\tundefined,\n\t\t\t\tdata\n\t\t\t)\n\t\t}\n\n\t\tconst tool = tools[toolCallName]\n\t\tif (!tool) {\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.UNKNOWN,\n\t\t\t\t`Tool \"${toolCallName}\" not found in tools`,\n\t\t\t\tundefined,\n\t\t\t\tdata\n\t\t\t)\n\t\t}\n\n\t\t// 提取并解析工具参数\n\t\tconst argString = normalizedChoice.message?.tool_calls?.[0]?.function?.arguments\n\t\tif (!argString) {\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.INVALID_TOOL_ARGS,\n\t\t\t\t'No tool call arguments found',\n\t\t\t\tundefined,\n\t\t\t\tdata\n\t\t\t)\n\t\t}\n\n\t\tlet parsedArgs: unknown\n\t\ttry {\n\t\t\tparsedArgs = JSON.parse(argString)\n\t\t} catch (error) {\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.INVALID_TOOL_ARGS,\n\t\t\t\t'Failed to parse tool arguments as JSON',\n\t\t\t\terror,\n\t\t\t\tdata\n\t\t\t)\n\t\t}\n\n\t\t// 用 schema 校验\n\t\tconst validation = tool.inputSchema.safeParse(parsedArgs)\n\t\tif (!validation.success) {\n\t\t\tconsole.error(z.prettifyError(validation.error))\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.INVALID_TOOL_ARGS,\n\t\t\t\t'Tool arguments validation failed',\n\t\t\t\tvalidation.error,\n\t\t\t\tdata\n\t\t\t)\n\t\t}\n\t\tconst toolInput = validation.data\n\n\t\t// 5. 执行工具\n\t\tlet toolResult: unknown\n\t\ttry {\n\t\t\ttoolResult = await tool.execute(toolInput)\n\t\t} catch (error: unknown) {\n\t\t\tif ((error as any)?.name === 'AbortError') throw error\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.TOOL_EXECUTION_ERROR,\n\t\t\t\t`Tool execution failed: ${(error as Error)?.message}`,\n\t\t\t\terror,\n\t\t\t\tdata\n\t\t\t)\n\t\t}\n\n\t\t// 返回结果\n\t\treturn {\n\t\t\ttoolCall: {\n\t\t\t\tname: toolCallName, // 被调用的工具名称\n\t\t\t\targs: toolInput, // 校验后的工具参数\n\t\t\t},\n\t\t\ttoolResult, // 工具执行结果\n\t\t\tusage: {\n\t\t\t\tpromptTokens: data.usage?.prompt_tokens ?? 0, // 输入(提示词)token 数\n\t\t\t\tcompletionTokens: data.usage?.completion_tokens ?? 0, // 输出(生成)token 数\n\t\t\t\ttotalTokens: data.usage?.total_tokens ?? 0, // 总 token 数\n\t\t\t\tcachedTokens: data.usage?.prompt_tokens_details?.cached_tokens, // 缓存命中的 token 数\n\t\t\t\treasoningTokens: data.usage?.completion_tokens_details?.reasoning_tokens, // 推理 token 数\n\t\t\t},\n\t\t\trawResponse: data, // 原始响应数据(用于调试)\n\t\t\trawRequest: finalRequestBody, // 最终发送的请求体(用于调试)\n\t\t}\n\t}\n}\n","import { OpenAICompatibleClient } from './OpenAICompatibleClient'\nimport { InvokeError, InvokeErrorTypes } from './errors'\nimport type {\n\tInvokeOptions,\n\tInvokeResult,\n\tLLMClient,\n\tLLMConfig,\n\tMessage,\n\tResolvedLLMConfig,\n\tTool,\n} from './types'\n\nexport { InvokeError, InvokeErrorTypes }\nexport type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool }\n\n/**\n * LLM 模块\n */\nexport class LLM extends EventTarget {\n\tconfig: ResolvedLLMConfig // 解析后的 LLM 配置\n\tclient: LLMClient // LLM 客户端实例\n\n\t// 构造函数:解析配置并创建默认的 OpenAI 兼容客户端\n\tconstructor(config: LLMConfig) {\n\t\tsuper()\n\t\tthis.config = parseLLMConfig(config)\n\n\t\t// 默认使用 OpenAI 兼容客户端\n\t\tthis.client = new OpenAICompatibleClient(this.config)\n\t}\n\n\t/**\n\t * - 调用一次 LLM API\n\t * - 调用一次工具\n\t * - 返回工具执行结果\n\t */\n\tasync invoke(\n\t\tmessages: Message[],\n\t\ttools: Record<string, Tool>,\n\t\tabortSignal: AbortSignal,\n\t\toptions?: InvokeOptions\n\t): Promise<InvokeResult> {\n\t\treturn await withRetry(async () => this.client.invoke(messages, tools, abortSignal, options), {\n\t\t\tmaxRetries: this.config.maxRetries, // 最大重试次数\n\t\t\tonRetry: (attempt, lastError) => { // 每次重试前的回调,用于派发 retry 事件\n\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\tnew CustomEvent('retry', {\n\t\t\t\t\t\tdetail: { attempt, maxAttempts: this.config.maxRetries, lastError },\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t},\n\t\t})\n\t}\n}\n\n/**\n * 重试某个函数,直到成功或达到最大重试次数。\n */\nasync function withRetry<T>(\n\tfn: () => Promise<T>,\n\tsettings: {\n\t\tmaxRetries: number\n\t\tonRetry: (attempt: number, lastError: Error) => void\n\t}\n): Promise<T> {\n\tlet attempt = 0\n\twhile (true) {\n\t\ttry {\n\t\t\treturn await fn()\n\t\t} catch (error: unknown) {\n\t\t\tif ((error as any)?.name === 'AbortError') throw error\n\t\t\tif (error instanceof InvokeError && !error.retryable) throw error\n\t\t\tattempt++\n\t\t\tif (attempt > settings.maxRetries) throw error\n\n\t\t\tconsole.debug('[LLM] retryable failure, will retry:', error)\n\t\t\tsettings.onRetry(attempt, error as Error)\n\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 100))\n\t\t}\n\t}\n}\n\n/**\n * 解析并校验 LLM 配置,填充默认值后返回完整配置\n *\n * @param config - 用户传入的 LLM 配置\n * @returns 解析后的完整配置\n */\nexport function parseLLMConfig(config: LLMConfig): ResolvedLLMConfig {\n\t// 运行时校验作为防御性编程(类型系统已保证这些条件)\n\tif (!config.baseURL || !config.model) {\n\t\tthrow new Error(\n\t\t\t'[PageAgent] LLM configuration required. Please provide: baseURL, model. ' +\n\t\t\t\t'See: https://alibaba.github.io/page-agent/docs/features/models'\n\t\t)\n\t}\n\n\tif (config.temperature !== undefined) {\n\t\tconsole.warn(\n\t\t\t'[PageAgent] LLMConfig.temperature is deprecated and will be removed in a future version. ' +\n\t\t\t\t'Use transformRequestBody to set it only for models you have verified accept it.'\n\t\t)\n\t}\n\n\treturn {\n\t\tbaseURL: config.baseURL, // 基础 URL\n\t\tmodel: config.model, // 模型名称\n\t\tapiKey: config.apiKey || '', // API 密钥,缺省为空字符串\n\t\ttemperature: config.temperature, // 采样温度(已废弃,透传保持兼容)\n\t\tmaxRetries: config.maxRetries ?? 2, // 最大重试次数,默认 2\n\t\ttransformRequestBody: config.transformRequestBody ?? ((requestBody) => requestBody), // 请求体转换函数,缺省为原样返回\n\t\tdisableNamedToolChoice: config.disableNamedToolChoice ?? false, // 是否禁用指定工具选择,默认 false\n\t\tcustomFetch: (config.customFetch ?? fetch).bind(globalThis), // 不绑定 this 的话,调用 fetch 会报非法调用\n\t}\n}\n","import { InvokeError, InvokeErrorTypes } from '../../llm/errors'\nimport * as z from 'zod/v4'\n\nimport { formatTerminalText } from '../../shared/terminal'\n\nimport type { AgentTool } from '../tools'\n\nconst log = console.log.bind(console, formatTerminalText('[autoFixer]', 'yellow')) // 带 [autoFixer] 前缀的日志函数\n\n/**\n * 规范化 LLM 响应并修复常见的格式问题。\n *\n * 处理:\n * - 没有 tool_calls 但 message.content 中含 JSON(回退)\n * - 模型将动作名作为工具调用返回而非 AgentOutput\n * - 参数被双重 JSON 字符串化\n * - 嵌套的函数调用格式\n * - 缺少 action 字段(回退为 wait)\n * - 单字段工具的原始动作输入(如 `{\"click_element_by_index\": 2}`)\n * - 等等\n */\nexport function normalizeResponse(response: any, tools?: Map<string, AgentTool>): any {\n\tlet resolvedArguments: any\n\n\tconst choice = (response as { choices?: Choice[] }).choices?.[0]\n\tif (!choice) throw new Error('No choices in response')\n\n\tconst message = choice.message\n\tif (!message) throw new Error('No message in choice')\n\n\tconst toolCall = message.tool_calls?.[0]\n\n\t// 修复 arguments 的层级和位置\n\n\tif (toolCall?.function?.arguments) {\n\t\tresolvedArguments = safeJsonParse(toolCall.function.arguments)\n\n\t\t// 情况:有时模型只返回动作层级\n\t\tif (toolCall.function.name && toolCall.function.name !== 'AgentOutput') {\n\t\t\tlog(`#1: fixing tool_call`)\n\t\t\tresolvedArguments = { action: safeJsonParse(resolvedArguments) }\n\t\t}\n\t} else {\n\t\t// 情况:有时模型在 content 中返回 json 而不是 tool_calls\n\t\tif (message.content) {\n\t\t\tconst content = message.content.trim()\n\t\t\tconst jsonInContent = retrieveJsonFromString(content)\n\t\t\tif (jsonInContent) {\n\t\t\t\tresolvedArguments = safeJsonParse(jsonInContent)\n\n\t\t\t\t// 情况:有时 content json 包含外层包装\n\t\t\t\tif (resolvedArguments?.name === 'AgentOutput') {\n\t\t\t\t\tlog(`#2: fixing tool_call`)\n\t\t\t\t\tresolvedArguments = safeJsonParse(resolvedArguments.arguments)\n\t\t\t\t}\n\n\t\t\t\t// 情况:有时甚至有两层包装\n\t\t\t\tif (resolvedArguments?.type === 'function') {\n\t\t\t\t\tlog(`#3: fixing tool_call`)\n\t\t\t\t\tresolvedArguments = safeJsonParse(resolvedArguments.function.arguments)\n\t\t\t\t}\n\n\t\t\t\t// 情况:还有时候只有动作层级\n\t\t\t\t// todo: 需要更好的检测逻辑\n\t\t\t\tif (\n\t\t\t\t\t!resolvedArguments?.action &&\n\t\t\t\t\t!resolvedArguments?.evaluation_previous_goal &&\n\t\t\t\t\t!resolvedArguments?.memory &&\n\t\t\t\t\t!resolvedArguments?.next_goal &&\n\t\t\t\t\t!resolvedArguments?.thinking\n\t\t\t\t) {\n\t\t\t\t\tlog(`#4: fixing tool_call`)\n\t\t\t\t\tresolvedArguments = { action: safeJsonParse(resolvedArguments) }\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new Error('No tool_call and the message content does not contain valid JSON')\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new Error('No tool_call nor message content is present')\n\t\t}\n\t}\n\n\t// 修复双重字符串化的参数\n\tresolvedArguments = safeJsonParse(resolvedArguments)\n\tif (resolvedArguments.action) {\n\t\tresolvedArguments.action = safeJsonParse(resolvedArguments.action)\n\t}\n\n\t// 使用工具 schema 校验并修复动作输入\n\tif (resolvedArguments.action && tools) {\n\t\tresolvedArguments.action = validateAction(resolvedArguments.action, tools)\n\t}\n\n\t// 修复不完整的格式\n\tif (!resolvedArguments.action) {\n\t\tlog(`#5: fixing tool_call`)\n\t\tresolvedArguments.action = { wait: { seconds: 1 } }\n\t}\n\n\t// 重新打包为标准格式\n\treturn {\n\t\t...response,\n\t\tchoices: [\n\t\t\t{\n\t\t\t\t...choice,\n\t\t\t\tmessage: {\n\t\t\t\t\t...message,\n\t\t\t\t\ttool_calls: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t...(toolCall || {}),\n\t\t\t\t\t\t\tfunction: {\n\t\t\t\t\t\t\t\t...(toolCall?.function || {}),\n\t\t\t\t\t\t\t\tname: 'AgentOutput',\n\t\t\t\t\t\t\t\targuments: JSON.stringify(resolvedArguments),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t}\n}\n\n/**\n * 根据工具 schema 校验动作。提供清晰的错误信息,\n * 而不是让联合 schema 产生难以阅读的错误。\n *\n * 同时为单字段工具强制转换原始输入:\n * 如 `{\"click_element_by_index\": 2}` → `{\"click_element_by_index\": {\"index\": 2}}`\n */\nfunction validateAction(action: any, tools: Map<string, AgentTool>): any {\n\tif (typeof action !== 'object' || action === null) return action\n\n\tconst toolName = Object.keys(action)[0]\n\tif (!toolName) return action\n\n\tconst tool = tools.get(toolName)\n\tif (!tool) {\n\t\tconst available = Array.from(tools.keys()).join(', ')\n\t\tthrow new InvokeError(\n\t\t\tInvokeErrorTypes.INVALID_TOOL_ARGS,\n\t\t\t`Unknown action \"${toolName}\". Available: ${available}`\n\t\t)\n\t}\n\n\tlet value = action[toolName]\n\tconst schema = tool.inputSchema\n\n\t// 为单字段工具强制转换原始输入\n\tif (schema instanceof z.ZodObject && value !== null && typeof value !== 'object') {\n\t\tconst requiredKey = Object.keys(schema.shape).find(\n\t\t\t(k) => !(schema.shape as Record<string, z.ZodType>)[k].safeParse(undefined).success\n\t\t)\n\t\tif (requiredKey) {\n\t\t\tlog(`coercing primitive action input for \"${toolName}\"`)\n\t\t\tvalue = { [requiredKey]: value }\n\t\t}\n\t}\n\n\tconst result = schema.safeParse(value)\n\tif (!result.success) {\n\t\tthrow new InvokeError(\n\t\t\tInvokeErrorTypes.INVALID_TOOL_ARGS,\n\t\t\t`Invalid input for action \"${toolName}\": ${z.prettifyError(result.error)}`\n\t\t)\n\t}\n\n\treturn { [toolName]: result.data }\n}\n\n/**\n * 安全解析 JSON,如果不是 JSON 则返回原始输入。\n */\nfunction safeJsonParse(input: any): any {\n\tif (typeof input === 'string') {\n\t\ttry {\n\t\t\treturn JSON.parse(input.trim())\n\t\t} catch {\n\t\t\treturn input\n\t\t}\n\t}\n\treturn input\n}\n\n/**\n * 从字符串中提取并解析 JSON。\n * - 将第一个 `{` 与最后一个 `}` 之间的内容视为 JSON。\n * - 尝试将该内容解析为 JSON 并返回解析后的值(对象/数组/原始值),成功则返回,否则返回 null。\n */\nfunction retrieveJsonFromString(str: string): any {\n\ttry {\n\t\tconst json = /({[\\s\\S]*})/.exec(str) ?? []\n\t\tif (json.length === 0) {\n\t\t\treturn null\n\t\t}\n\t\treturn JSON.parse(json[0]!)\n\t} catch {\n\t\treturn null\n\t}\n}\n\ninterface Choice {\n\tmessage?: {\n\t\trole?: 'assistant' // 消息角色\n\t\tcontent?: string // 消息内容(无 tool_calls 时可能内嵌 JSON)\n\t\ttool_calls?: {\n\t\t\tid?: string // 工具调用 ID\n\t\t\ttype?: 'function' // 工具调用类型\n\t\t\tfunction?: {\n\t\t\t\tname?: string // 函数名\n\t\t\t\targuments?: string // 函数参数的 JSON 字符串\n\t\t\t}\n\t\t}[]\n\t}\n\tindex?: 0 // 选择序号\n\tfinish_reason?: 'tool_calls' // 结束原因\n}\n","import { formatTerminalText } from '../../shared/terminal'\n\nexport * from './autoFixer'\n\n/**\n * 等待 `seconds` 秒。若提供了 `signal`,则等待可被取消:\n * 中止时会以 signal 的 reason(一个 `AbortError`)reject。\n */\nexport async function waitFor(seconds: number, signal?: AbortSignal): Promise<void> {\n\tif (!signal) {\n\t\tawait new Promise((resolve) => setTimeout(resolve, seconds * 1000))\n\t\treturn\n\t}\n\tsignal.throwIfAborted()\n\tawait new Promise<void>((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tsignal.removeEventListener('abort', onAbort)\n\t\t\tresolve()\n\t\t}, seconds * 1000)\n\t\tconst onAbort = () => {\n\t\t\tclearTimeout(timer)\n\t\t\t// reason 是一个 DOMException AbortError。\n\t\t\treject(signal.reason as DOMException)\n\t\t}\n\t\tsignal.addEventListener('abort', onAbort, { once: true })\n\t})\n}\n\n//\n\n// 截断文本到指定最大长度,超出部分以省略号结尾\nexport function truncate(text: string, maxLength: number): string {\n\tif (text.length > maxLength) {\n\t\treturn text.substring(0, maxLength) + '...'\n\t}\n\treturn text\n}\n\n//\n\n// 生成一个随机 ID;若传入 existingIDs,则保证不与已有 ID 重复(最多尝试 1000 次)\nexport function randomID(existingIDs?: string[]): string {\n\tlet id = Math.random().toString(36).substring(2, 11)\n\n\tif (!existingIDs) {\n\t\treturn id\n\t}\n\n\tconst MAX_TRY = 1000\n\tlet tryCount = 0\n\n\twhile (existingIDs.includes(id)) {\n\t\tid = Math.random().toString(36).substring(2, 11)\n\t\ttryCount++\n\t\tif (tryCount > MAX_TRY) {\n\t\t\tthrow new Error('randomID: too many tries')\n\t\t}\n\t}\n\n\treturn id\n}\n\n//\nconst _global = globalThis as any // 全局对象(用于跨模块共享 ID 列表)\n\n// 初始化全局 ID 列表(若尚未存在)\nif (!_global.__PAGE_AGENT_IDS__) {\n\t_global.__PAGE_AGENT_IDS__ = []\n}\n\nconst ids = _global.__PAGE_AGENT_IDS__ // 当前窗口已生成的 ID 列表\n\n/**\n * 生成一个随机 ID。\n * @note 在当前窗口内唯一。\n */\nexport function uid() {\n\tconst id = randomID(ids)\n\tids.push(id)\n\treturn id\n}\n\nconst llmsTxtCache = new Map<string, string | null>()\n\n/** 获取某个 URL 源的 /llms.txt。按源缓存,`null` 表示已尝试过但未找到。 */\nexport async function fetchLlmsTxt(url: string): Promise<string | null> {\n\tlet origin: string\n\ttry {\n\t\torigin = new URL(url).origin\n\t} catch {\n\t\treturn null // URL 无效\n\t}\n\t// about:blank、data:、file:\n\tif (origin === 'null') return null\n\n\tif (llmsTxtCache.has(origin)) return llmsTxtCache.get(origin)!\n\n\tconst endpoint = `${origin}/llms.txt`\n\tlet result: string | null = null\n\ttry {\n\t\tconsole.log(formatTerminalText(`[llms.txt] Fetching ${endpoint}`, 'gray'))\n\t\tconst res = await fetch(endpoint, { signal: AbortSignal.timeout(3000) })\n\t\tif (res.ok) {\n\t\t\tresult = await res.text()\n\t\t\tconsole.log(formatTerminalText(`[llms.txt] Found (${result.length} chars)`, 'green'))\n\t\t\tif (result.length > 1000) {\n\t\t\t\tconsole.log(formatTerminalText(`[llms.txt] Truncating to 1000 chars`, 'yellow'))\n\t\t\t\tresult = truncate(result, 1000)\n\t\t\t}\n\t\t} else {\n\t\t\tconsole.debug(formatTerminalText(`[llms.txt] ${res.status} for ${endpoint}`, 'gray'))\n\t\t}\n\t} catch (e) {\n\t\tconsole.debug(formatTerminalText(`[llms.txt] not found for ${endpoint}`, 'gray'), e)\n\t}\n\tllmsTxtCache.set(origin, result)\n\treturn result\n}\n\n/**\n * 简单的断言函数:条件为假时抛出错误\n * @param condition - 要断言的条件\n * @param message - 可选的错误信息\n * @throws 条件为假时抛出错误\n */\nexport function assert(condition: unknown, message?: string, silent?: boolean): asserts condition {\n\tif (!condition) {\n\t\tconst errorMessage = message ?? 'Assertion failed'\n\n\t\tif (!silent) console.error(formatTerminalText(`❌ assert: ${errorMessage}`, 'red'))\n\n\t\tthrow new Error(errorMessage)\n\t}\n}\n\n/**\n * 抑制函数产生的错误。\n */\nexport async function suppress<T>(fn: () => T | Promise<T>): Promise<Awaited<T> | undefined> {\n\ttry {\n\t\treturn await fn()\n\t} catch (error) {\n\t\tconsole.error(error)\n\t\treturn undefined\n\t}\n}\n","/**\n * PageAgent 的内部工具。\n * @note 改编自 browser-use\n */\nimport * as z from 'zod/v4'\n\nimport type { AgentRuntime } from '../AgentRuntime'\nimport { waitFor } from '../utils'\n\n/**\n * 每次工具调用都会传入的上下文。\n * 工具必须遵守 `signal` 以支持协作式取消。\n */\nexport interface ToolContext {\n\tsignal: AbortSignal // 取消信号,工具需配合以支持协作式取消\n}\n\n/**\n * 内部工具定义,可访问 PageAgent 的 `this` 上下文\n */\nexport interface AgentTool<TParams = any> {\n\t// name: string\n\tdescription: string // 工具描述,会注入到 LLM 提示词中\n\tinputSchema: z.ZodType<TParams> // 工具入参的 zod 校验 schema\n\texecute: (this: AgentRuntime, args: TParams, ctx: ToolContext) => Promise<string> // 工具执行逻辑\n}\n\n/** 创建一个工具定义(直接透传 options) */\nexport function tool<TParams>(options: AgentTool<TParams>): AgentTool<TParams> {\n\treturn options\n}\n\n/**\n * PageAgent 的内部工具。\n * 注:使用 any 以允许每个工具拥有不同的参数类型\n */\nexport const tools = new Map<string, AgentTool>()\n\ntools.set(\n\t'done',\n\ttool({\n\t\tdescription:\n\t\t\t'Complete task. Text is your final response to the user — keep it concise unless the user explicitly asks for detail.',\n\t\tinputSchema: z.object({\n\t\t\ttext: z.string(), // 最终回复用户的文本\n\t\t\tsuccess: z.boolean().default(true), // 任务是否成功\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, _input) {\n\t\t\t// @note 主循环会处理这个工具\n\t\t\treturn Promise.resolve('Task completed')\n\t\t},\n\t})\n)\n\ntools.set(\n\t'wait',\n\ttool({\n\t\tdescription: 'Wait for x seconds. Can be used to wait until the page or data is fully loaded.',\n\t\tinputSchema: z.object({\n\t\t\tseconds: z.number().min(1).max(10).default(1), // 等待秒数\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input, { signal }) {\n\t\t\t// 尝试从实际等待时间中扣减 LLM 调用耗时\n\t\t\tconst lastTimeUpdate = await this.browserController.getLastUpdateTime()\n\t\t\tconst secondsSinceLastUpdate = (Date.now() - lastTimeUpdate) / 1000\n\t\t\tconst actualWaitTime = Math.max(0, input.seconds - secondsSinceLastUpdate)\n\t\t\tconsole.log(`actualWaitTime: ${actualWaitTime} seconds`)\n\t\t\tawait waitFor(actualWaitTime, signal)\n\n\t\t\tconst waitedSeconds = (secondsSinceLastUpdate + actualWaitTime).toFixed(2)\n\t\t\treturn `✅ Waited for ${waitedSeconds} seconds.`\n\t\t},\n\t})\n)\n\ntools.set(\n\t'ask_user',\n\ttool({\n\t\tdescription:\n\t\t\t'Ask the user a question and wait for their answer. Use this if you need more information or clarification.',\n\t\tinputSchema: z.object({\n\t\t\tquestion: z.string(), // 要询问用户的问题\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input, { signal }) {\n\t\t\tif (!this.onAskUser) {\n\t\t\t\tthrow new Error('ask_user tool requires onAskUser callback to be set')\n\t\t\t}\n\t\t\tconst answer = await this.onAskUser(input.question, { signal })\n\t\t\treturn `User answered: ${answer}`\n\t\t},\n\t})\n)\n\ntools.set(\n\t'click_element_by_index',\n\ttool({\n\t\tdescription: 'Click element by index',\n\t\tinputSchema: z.object({\n\t\t\tindex: z.int().min(0), // 要点击的元素索引\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input) {\n\t\t\tconst result = await this.browserController.clickElement(input.index)\n\t\t\treturn result.message\n\t\t},\n\t})\n)\n\ntools.set(\n\t'input_text',\n\ttool({\n\t\tdescription: 'Click and type text into an interactive input element',\n\t\tinputSchema: z.object({\n\t\t\tindex: z.int().min(0), // 目标输入框的元素索引\n\t\t\ttext: z.string(), // 要输入的文本\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input) {\n\t\t\tconst result = await this.browserController.inputText(input.index, input.text)\n\t\t\treturn result.message\n\t\t},\n\t})\n)\n\ntools.set(\n\t'select_dropdown_option',\n\ttool({\n\t\tdescription:\n\t\t\t'Select dropdown option for interactive element index by the text of the option you want to select',\n\t\tinputSchema: z.object({\n\t\t\tindex: z.int().min(0), // 下拉框元素索引\n\t\t\ttext: z.string(), // 要选中的选项文本\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input) {\n\t\t\tconst result = await this.browserController.selectOption(input.index, input.text)\n\t\t\treturn result.message\n\t\t},\n\t})\n)\n\n/**\n * @note 参考自 browser-use\n */\ntools.set(\n\t'scroll',\n\ttool({\n\t\tdescription:\n\t\t\t'Scroll vertically. Without index: scrolls the document. With index: scrolls the container at that index (or its nearest scrollable ancestor). Use index of a data-scrollable element to scroll a specific area.',\n\t\tinputSchema: z.object({\n\t\t\tdown: z.boolean().default(true), // 是否向下滚动\n\t\t\tnum_pages: z.number().min(0).max(10).optional().default(0.1), // 滚动页数\n\t\t\tpixels: z.number().int().min(0).optional(), // 指定滚动的像素数(可选)\n\t\t\tindex: z.number().int().min(0).optional(), // 目标容器索引(可选)\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input) {\n\t\t\tconst result = await this.browserController.scroll({\n\t\t\t\t...input,\n\t\t\t\tnumPages: input.num_pages,\n\t\t\t})\n\t\t\treturn result.message\n\t\t},\n\t})\n)\n\n/**\n * @todo 表格需要专门的解析器来提取结构化数据。这个工具有点鸡肋。\n */\ntools.set(\n\t'scroll_horizontally',\n\ttool({\n\t\tdescription:\n\t\t\t'Scroll horizontally. Without index: scrolls the document. With index: scrolls the container at that index (or its nearest scrollable ancestor). Use index of a data-scrollable element to scroll a specific area.',\n\t\tinputSchema: z.object({\n\t\t\tright: z.boolean().default(true), // 是否向右滚动\n\t\t\tpixels: z.number().int().min(0), // 滚动的像素数\n\t\t\tindex: z.number().int().min(0).optional(), // 目标容器索引(可选)\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input) {\n\t\t\tconst result = await this.browserController.scrollHorizontally(input)\n\t\t\treturn result.message\n\t\t},\n\t})\n)\n\ntools.set(\n\t'execute_javascript',\n\ttool({\n\t\tdescription:\n\t\t\t'Execute JavaScript code on the current page. Supports async/await syntax. Use with caution! ' +\n\t\t\t'An `AbortSignal` named `signal` is available in scope: long-running async code MUST honor it ' +\n\t\t\t'(e.g. `await fetch(url, { signal })`, or `signal.throwIfAborted()` in loops)',\n\t\tinputSchema: z.object({\n\t\t\tscript: z.string(), // 要执行的 JavaScript 代码\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input, { signal }) {\n\t\t\tconst result = await this.browserController.executeJavascript(input.script, signal)\n\t\t\tsignal.throwIfAborted()\n\t\t\treturn result.message\n\t\t},\n\t})\n)\n\n// @todo send_keys(待实现)\n// @todo upload_file(待实现)\n// @todo extract_structured_data(待实现)\n","/**\n * Copyright (C) 2025 Alibaba Group Holding Limited\n * Copyright (C) 2026 SimonLuvRamen\n * All rights reserved.\n */\nimport type { BrowserState } from '../browser/dom'\nimport type { BrowserController } from '../browser/BrowserController'\nimport { InvokeError } from '../llm/errors'\nimport { LLM } from '../llm/LLM'\nimport type { ContentPart, Tool } from '../llm/types'\nimport { formatTerminalText } from '../shared/terminal'\nimport * as z from 'zod/v4'\n\nimport SYSTEM_PROMPT from './prompts/system_prompt.md?raw'\nimport { tools } from './tools'\nimport type {\n\tAgentActivity,\n\tAgentConfig,\n\tAgentReflection,\n\tAgentStatus,\n\tAgentStepEvent,\n\tAttachment,\n\tExecutionResult,\n\tHistoricalEvent,\n\tMacroToolInput,\n\tMacroToolResult,\n} from './types'\nimport { assert, fetchLlmsTxt, normalizeResponse, suppress, truncate, uid, waitFor } from './utils'\n\n/** 文本类附件注入 prompt 时的最大字符数,避免超大文件把上下文撑爆 */\nconst ATTACHMENT_TEXT_MAX_LENGTH = 20000\n\nexport { tool, type AgentTool, type ToolContext } from './tools'\nexport type * from './types'\n\nexport type AgentRuntimeConfig = AgentConfig & { browserController: BrowserController }\n\n/**\n * 用于浏览器自动化的 AI 智能体。\n *\n * @remarks\n * ## 反应式 Agent 循环\n * - 步骤\n * - 观察(收集当前环境与上下文信息)\n * - 思考(调用 LLM)\n * - 反思(评估历史、生成记忆、短期规划)\n * - 行动(给出接近下一个目标的动作)\n * - 执行(执行动作)\n * - 循环\n *\n * ## 事件系统\n * - `statuschange` - Agent 状态变更(idle → running → completed/error/stopped)\n * - `historychange` - 历史事件更新(持久化,属于 Agent 记忆的一部分)\n * - `activity` - 实时活动反馈(瞬态,仅用于 UI)\n * - `dispose` - 触发 Agent 清理\n *\n * ## 信息流\n * 1. **历史事件**(`history` 数组)\n * - 构成 Agent 记忆的持久事件流\n * - 跨步骤包含在 LLM 上下文中\n * - 类型:步骤、观察、用户接管、LLM 错误\n *\n * 2. **活动事件**(通过 `activity` 事件)\n * - 任务执行期间的瞬时 UI 反馈\n * - 不包含在 LLM 上下文中\n * - 类型:thinking、executing、executed、retrying、error\n */\nexport class AgentRuntime extends EventTarget {\n\t/** Agent 唯一 ID */\n\treadonly id = uid()\n\t/** 合并后的 Agent 配置(含最大步数上限) */\n\treadonly config: AgentRuntimeConfig & { maxSteps: number; historyWindow: number }\n\t/** 内部工具集合(按工具名索引) */\n\treadonly tools: typeof tools\n\t/** 用于 DOM 操作的 BrowserController */\n\treadonly browserController: BrowserController\n\n\t/** 当前任务描述 */\n\ttask = ''\n\t/** 当前任务 ID */\n\ttaskId = ''\n\t/** 历史事件 */\n\thistory: HistoricalEvent[] = []\n\t/** 该 Agent 是否已被销毁 */\n\tdisposed = false\n\n\t/**\n\t * 当 Agent 需要向用户提问时调用。\n\t * 若未设置,`ask_user` 工具将被禁用。\n\t * 实现应在 `signal` 中止时 reject 该 Promise。\n\t * @example onAskUser: (q) => window.prompt(q) || ''\n\t */\n\tonAskUser?: (question: string, options?: { signal: AbortSignal }) => Promise<string>\n\n\t/** 当前 Agent 状态 */\n\t#status: AgentStatus = 'idle'\n\t/** LLM 实例(负责模型调用) */\n\t#llm: LLM\n\t/**\n\t * 任务取消原语:其 signal 会传递给 LLM fetch、工具(通过 `ctx.signal`)和异步回调。\n\t * 仅由 `stop`/`dispose`(任务进行中)或任务初始化时中止,且始终不带 reason,\n\t * 这样 `signal.reason` 保持为标准的 `AbortError`。\n\t */\n\t#abortController = new AbortController()\n\t/** 本轮任务收集的观察消息(暂存,稍后统一写入历史) */\n\t#observations: string[] = []\n\t/** 本次任务上传的附件;仅在任务第一步注入 LLM prompt,之后步骤不重复携带原文件以节省 token */\n\t#taskAttachments: Attachment[] = []\n\t/** 当前任务在 history 数组中的起始下标(用于跨任务保留历史时,把 prompt/循环检测限定在当前任务范围内) */\n\t#taskHistoryStart = 0\n\n\t/** 当前一次运行完全结束时 resolve。由 `stop()` 等待。 */\n\t#running: Promise<void> = Promise.resolve()\n\t#lastResult: ExecutionResult | null = null\n\n\t/** 单次任务执行期间的内部状态 */\n\t#states = {\n\t\t/** 累计等待时间(秒) */\n\t\ttotalWaitTime: 0,\n\t\t/** 用于检测页面导航 */\n\t\tlastURL: '',\n\t\t/** 浏览器状态 */\n\t\tbrowserState: null as BrowserState | null,\n\t}\n\n\t/** 构造函数:初始化配置、LLM、工具集,并注册重试事件监听与注入自定义工具 */\n\tconstructor(config: AgentRuntimeConfig) {\n\t\tsuper()\n\n\t\tthis.config = {\n\t\t\t...config,\n\t\t\tmaxSteps: config.maxSteps ?? 40,\n\t\t\thistoryWindow: config.historyWindow ?? 5,\n\t\t}\n\n\t\tthis.#llm = new LLM(this.config)\n\t\tthis.tools = new Map(tools)\n\t\tthis.browserController = config.browserController\n\n\t\tthis.#llm.addEventListener('retry', (e) => {\n\t\t\tconst { attempt, maxAttempts, lastError } = (e as CustomEvent).detail\n\t\t\tthis.#emitActivity({ type: 'retrying', attempt, maxAttempts })\n\t\t\tthis.history.push({\n\t\t\t\ttype: 'error',\n\t\t\t\tmessage: String(lastError),\n\t\t\t\trawResponse: (lastError as InvokeError).rawResponse,\n\t\t\t})\n\t\t\tthis.history.push({\n\t\t\t\ttype: 'retry',\n\t\t\t\tmessage: `LLM retry attempt ${attempt} of ${maxAttempts}`,\n\t\t\t\tattempt,\n\t\t\t\tmaxAttempts,\n\t\t\t})\n\t\t\tthis.#emitHistoryChange()\n\t\t})\n\n\t\tif (this.config.customTools) {\n\t\t\tfor (const [name, tool] of Object.entries(this.config.customTools)) {\n\t\t\t\tif (tool === null) {\n\t\t\t\t\tthis.tools.delete(name)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tthis.tools.set(name, tool)\n\t\t\t}\n\t\t}\n\n\t\tif (!this.config.experimentalScriptExecutionTool) {\n\t\t\tthis.tools.delete('execute_javascript')\n\t\t}\n\t}\n\n\t/** 获取当前 Agent 状态 */\n\tget status(): AgentStatus {\n\t\treturn this.#status\n\t}\n\n\t/** 最近一次运行的结果,首次运行完成前为 `null`。 */\n\tget lastResult(): ExecutionResult | null {\n\t\treturn this.#lastResult\n\t}\n\n\t/** 当前任务自身的历史事件(不含之前任务留下的记录),用于 prompt 拼装、循环检测等按任务隔离的场景 */\n\tget #currentTaskHistory(): HistoricalEvent[] {\n\t\treturn this.history.slice(this.#taskHistoryStart)\n\t}\n\n\t/** 触发 statuschange 事件 */\n\t#emitStatusChange(): void {\n\t\tthis.dispatchEvent(new Event('statuschange'))\n\t}\n\n\t/** 触发 historychange 事件 */\n\t#emitHistoryChange(pushHistoricalEvent?: HistoricalEvent): void {\n\t\tif (pushHistoricalEvent) this.history.push(pushHistoricalEvent)\n\t\tthis.dispatchEvent(new Event('historychange'))\n\t}\n\n\t/**\n\t * 触发 activity 事件 - 用于瞬态的 UI 反馈\n\t * @param activity - 当前的 Agent 活动\n\t */\n\t#emitActivity(activity: AgentActivity): void {\n\t\tthis.dispatchEvent(new CustomEvent('activity', { detail: activity }))\n\t}\n\n\t/** 更新状态并触发事件 */\n\t#setStatus(status: AgentStatus): void {\n\t\tif (this.#status !== status) {\n\t\t\tthis.#status = status\n\t\t\tthis.#emitStatusChange()\n\t\t}\n\t}\n\n\t/**\n\t * 向历史事件流推送一条观察消息。\n\t * 它会出现在 <agent_history> 中,并在跨步骤的记忆中保持持久。\n\t * @experimental @internal\n\t * @note 历史变更会在下一步开始前触发\n\t */\n\tpushObservation(content: string): void {\n\t\tthis.#observations.push(content)\n\t}\n\n\t/**\n\t * 停止当前任务,并等待运行完全结束(包括生命周期钩子)。\n\t * @note 切勿在生命周期钩子中 await .stop()。\n\t */\n\tasync stop(): Promise<void> {\n\t\tif (this.#status !== 'running') return\n\t\tthis.#abortController.abort()\n\t\tawait this.#running\n\t}\n\n\t/**\n\t * 外部错误(前置检查/配置/钩子)会直接抛出;\n\t * Agent 内部错误会被捕获并加入历史,同时返回失败结果\n\t */\n\tasync execute(task: string, attachments: Attachment[] = []): Promise<ExecutionResult> {\n\t\t// 前置检查\n\t\tif (this.disposed) throw new Error('PageAgent has been disposed. Create a new instance.')\n\t\tif (this.#status === 'running') throw new Error('A task is already running.')\n\t\tif (!task) throw new Error('Task is required')\n\n\t\tthis.task = task\n\t\tthis.taskId = uid()\n\t\tthis.#taskAttachments = attachments\n\n\t\t// @note 跨任务保留 history(不再清空),便于 UI 展示历次任务的完整操作记录。\n\t\t// 通过 task_start 标记本任务的起点,prompt 拼装/循环检测等仅在当前任务范围内取用(见 #currentTaskHistory)。\n\t\tthis.#taskHistoryStart = this.history.length\n\t\tthis.#emitHistoryChange({\n\t\t\ttype: 'task_start',\n\t\t\ttask,\n\t\t\t...(attachments.length > 0 ? { attachmentNames: attachments.map((a) => a.name) } : {}),\n\t\t})\n\t\tthis.#observations = []\n\t\tthis.#states = { totalWaitTime: 0, lastURL: '', browserState: null }\n\t\tthis.#abortController = new AbortController()\n\t\tconst signal = this.#abortController.signal\n\n\t\tlet resolveRunning!: () => void\n\t\tthis.#running = new Promise<void>((r) => (resolveRunning = r))\n\n\t\tthis.#setStatus('running')\n\t\tthis.#emitHistoryChange()\n\n\t\t// 若未设置 onAskUser,则禁用 ask_user 工具\n\t\tif (!this.onAskUser) this.tools.delete('ask_user')\n\n\t\tconst onBeforeStep = this.config.onBeforeStep\n\t\tconst onAfterStep = this.config.onAfterStep\n\t\tconst onBeforeTask = this.config.onBeforeTask\n\t\tconst onAfterTask = this.config.onAfterTask\n\t\tconst stepDelay = this.config.stepDelay ?? 0.2\n\t\tconst maxSteps = this.config.maxSteps\n\n\t\tlet step = 0\n\t\tlet taskResult: ExecutionResult\n\t\tlet finalStatus: AgentStatus = 'error'\n\n\t\tawait suppress(() => this.browserController.showMask())\n\n\t\t// 优雅退出\n\t\ttry {\n\t\t\tawait onBeforeTask?.(this)\n\n\t\t\twhile (true) {\n\t\t\t\tif (step >= maxSteps) {\n\t\t\t\t\tconst message = 'Step count exceeded maximum limit'\n\t\t\t\t\tconsole.error(message)\n\t\t\t\t\tthis.#emitActivity({ type: 'error', message: message })\n\t\t\t\t\tthis.#emitHistoryChange({ type: 'error', message: message })\n\t\t\t\t\ttaskResult = { success: false, data: message, history: this.#currentTaskHistory }\n\t\t\t\t\tthis.#lastResult = taskResult\n\t\t\t\t\tfinalStatus = 'error'\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tawait onBeforeStep?.(this, step)\n\n\t\t\t\t// 处理 Agent 内部错误\n\t\t\t\ttry {\n\t\t\t\t\tconsole.group(`step: ${step}`)\n\n\t\t\t\t\t// @note 把 stepDelay 视为下一步的一部分很方便。\n\t\t\t\t\t// 也许应移到一个独立的 try 块中以获得更好的语义?\n\t\t\t\t\tif (step > 0) await waitFor(stepDelay, signal)\n\n\t\t\t\t\tsignal.throwIfAborted()\n\n\t\t\t\t\t// 观察\n\n\t\t\t\t\tconsole.log(formatTerminalText('👀 Observing...', 'blue', true))\n\n\t\t\t\t\tthis.#states.browserState = await this.browserController.getBrowserState()\n\t\t\t\t\tawait this.#handleObservations(step)\n\n\t\t\t\t\t// 组装提示词\n\n\t\t\t\t\tconst messages = [\n\t\t\t\t\t\t{ role: 'system' as const, content: this.#getSystemPrompt() },\n\t\t\t\t\t\t{ role: 'user' as const, content: await this.#assembleUserPrompt() },\n\t\t\t\t\t]\n\n\t\t\t\t\tconst macroTool = { AgentOutput: this.#packMacroTool() }\n\n\t\t\t\t\t// 调用 LLM\n\n\t\t\t\t\tconsole.log(formatTerminalText('🧠 Thinking...', 'blue', true))\n\t\t\t\t\tthis.#emitActivity({ type: 'thinking' })\n\n\t\t\t\t\tconst result = await this.#llm.invoke(messages, macroTool, signal, {\n\t\t\t\t\t\ttoolChoiceName: 'AgentOutput',\n\t\t\t\t\t\tnormalizeResponse: (res) => normalizeResponse(res, this.tools),\n\t\t\t\t\t})\n\n\t\t\t\t\t// 组装历史\n\n\t\t\t\t\tconst macroResult = result.toolResult as MacroToolResult\n\t\t\t\t\tconst input = macroResult.input\n\t\t\t\t\tconst output = macroResult.output\n\t\t\t\t\tconst reflection: Partial<AgentReflection> = {\n\t\t\t\t\t\tevaluation_previous_goal: input.evaluation_previous_goal,\n\t\t\t\t\t\tmemory: input.memory,\n\t\t\t\t\t\tnext_goal: input.next_goal,\n\t\t\t\t\t}\n\t\t\t\t\tconst actionName = Object.keys(input.action)[0]\n\t\t\t\t\tconst action: AgentStepEvent['action'] = {\n\t\t\t\t\t\tname: actionName,\n\t\t\t\t\t\tinput: input.action[actionName],\n\t\t\t\t\t\toutput: output,\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.#emitHistoryChange({\n\t\t\t\t\t\ttype: 'step',\n\t\t\t\t\t\tstepIndex: step,\n\t\t\t\t\t\treflection,\n\t\t\t\t\t\taction,\n\t\t\t\t\t\tusage: result.usage,\n\t\t\t\t\t\trawResponse: result.rawResponse,\n\t\t\t\t\t\trawRequest: result.rawRequest,\n\t\t\t\t\t})\n\n\t\t\t\t\tif (actionName === 'done') {\n\t\t\t\t\t\tconst success = action.input?.success ?? false\n\t\t\t\t\t\tconst data = action.input?.text || 'no text provided'\n\t\t\t\t\t\tconsole.log(formatTerminalText('Task completed', 'green', true), success, data)\n\t\t\t\t\t\ttaskResult = { success, data, history: this.#currentTaskHistory }\n\t\t\t\t\t\tthis.#lastResult = taskResult\n\t\t\t\t\t\tfinalStatus = 'completed'\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t// catch 块绝不能抛出错误,否则若 finally 块也抛出错误,错误可能被覆盖。\n\n\t\t\t\t\tconst isAbortError = (error as any)?.name === 'AbortError'\n\t\t\t\t\tif (!isAbortError) console.error('Task failed', error)\n\t\t\t\t\tconst message = isAbortError ? 'Task aborted' : String(error)\n\t\t\t\t\tthis.#emitActivity({ type: 'error', message: message })\n\t\t\t\t\tthis.#emitHistoryChange({ type: 'error', message: message, rawResponse: error })\n\t\t\t\t\ttaskResult = { success: false, data: message, history: this.#currentTaskHistory }\n\t\t\t\t\tthis.#lastResult = taskResult\n\t\t\t\t\tfinalStatus = isAbortError ? 'stopped' : 'error'\n\t\t\t\t\tbreak\n\t\t\t\t} finally {\n\t\t\t\t\t// finally 块会在上面的 break 之前执行。\n\n\t\t\t\t\tconsole.groupEnd()\n\t\t\t\t\t// @note 钩子可能抛出错误。\n\t\t\t\t\t// 它会覆盖上面的 `break`,并作为外部错误被处理。\n\t\t\t\t\t// 符合预期。\n\t\t\t\t\tawait onAfterStep?.(this, this.#currentTaskHistory)\n\t\t\t\t}\n\n\t\t\t\tstep++\n\t\t\t} // while\n\n\t\t\tawait onAfterTask?.(this, taskResult)\n\n\t\t\treturn taskResult\n\t\t} catch (error) {\n\t\t\tthis.#emitActivity({ type: 'error', message: String(error) })\n\t\t\tfinalStatus = 'error'\n\t\t\tthrow error\n\t\t} finally {\n\t\t\tawait suppress(() => this.browserController.cleanUpHighlights())\n\t\t\tawait suppress(() => this.browserController.hideMask())\n\t\t\tthis.#abortController.abort()\n\t\t\tresolveRunning()\n\t\t\tthis.#setStatus(finalStatus)\n\t\t}\n\t}\n\n\t/**\n\t * 将所有工具合并为单个 MacroTool,其输入如下:\n\t * - thinking: string\n\t * - evaluation_previous_goal: string\n\t * - memory: string\n\t * - next_goal: string\n\t * - action: { toolName: toolInput }\n\t * 其中 action 必须从 this.tools 中定义的工具中选择\n\t */\n\t#packMacroTool(): Tool<MacroToolInput, MacroToolResult> {\n\t\tconst tools = this.tools\n\n\t\tconst actionSchemas = Array.from(tools.entries()).map(([toolName, tool]) => {\n\t\t\treturn z.object({ [toolName]: tool.inputSchema }).describe(tool.description)\n\t\t})\n\n\t\tconst actionSchema = z.union(actionSchemas as unknown as [z.ZodType, z.ZodType, ...z.ZodType[]])\n\n\t\tconst macroToolSchema = z.object({\n\t\t\t// thinking: z.string().optional(),\n\t\t\tevaluation_previous_goal: z.string().optional(),\n\t\t\tmemory: z.string().optional(),\n\t\t\tnext_goal: z.string().optional(),\n\t\t\taction: actionSchema,\n\t\t})\n\n\t\treturn {\n\t\t\tdescription: 'You MUST call this tool every step!',\n\t\t\tinputSchema: macroToolSchema as z.ZodType<MacroToolInput>,\n\t\t\texecute: async (input: MacroToolInput): Promise<MacroToolResult> => {\n\t\t\t\tconst signal = this.#abortController.signal\n\t\t\t\tsignal.throwIfAborted()\n\n\t\t\t\tconsole.log(formatTerminalText('MacroTool input', 'blue', true), input)\n\t\t\t\tconst action = input.action\n\n\t\t\t\tconst toolName = Object.keys(action)[0]\n\t\t\t\tconst toolInput = action[toolName]\n\n\t\t\t\t// 构建反思文本,仅包含非空字段\n\t\t\t\tconst reflectionLines: string[] = []\n\t\t\t\tif (input.evaluation_previous_goal)\n\t\t\t\t\treflectionLines.push(`✅: ${input.evaluation_previous_goal}`)\n\t\t\t\tif (input.memory) reflectionLines.push(`💾: ${input.memory}`)\n\t\t\t\tif (input.next_goal) reflectionLines.push(`🎯: ${input.next_goal}`)\n\n\t\t\t\tconst reflectionText = reflectionLines.length > 0 ? reflectionLines.join('\\n') : ''\n\n\t\t\t\tif (reflectionText) {\n\t\t\t\t\tconsole.log(reflectionText)\n\t\t\t\t}\n\n\t\t\t\t// 查找对应的工具\n\t\t\t\tconst tool = tools.get(toolName)\n\t\t\t\tassert(tool, `Tool ${toolName} not found`)\n\n\t\t\t\tconsole.log(formatTerminalText(`Executing tool: ${toolName}`, 'blue', true), toolInput)\n\n\t\t\t\t// 触发 executing 活动\n\t\t\t\tthis.#emitActivity({ type: 'executing', tool: toolName, input: toolInput })\n\n\t\t\t\tconst startTime = Date.now()\n\n\t\t\t\tconst result = await tool.execute.bind(this)(toolInput, { signal })\n\t\t\t\t// 即使工具忽略了 signal 并正常 resolve,也要强制中止。\n\t\t\t\tsignal.throwIfAborted()\n\n\t\t\t\tconst duration = Date.now() - startTime\n\t\t\t\tconsole.log(\n\t\t\t\t\tformatTerminalText(`Tool (${toolName}) executed for ${duration}ms`, 'green', true),\n\t\t\t\t\tresult\n\t\t\t\t)\n\n\t\t\t\t// 触发 executed 活动\n\t\t\t\tthis.#emitActivity({\n\t\t\t\t\ttype: 'executed',\n\t\t\t\t\ttool: toolName,\n\t\t\t\t\tinput: toolInput,\n\t\t\t\t\toutput: result,\n\t\t\t\t\tduration,\n\t\t\t\t})\n\n\t\t\t\t// 累计等待时间\n\t\t\t\tif (toolName === 'wait') {\n\t\t\t\t\tthis.#states.totalWaitTime += toolInput?.seconds || 0\n\t\t\t\t} else {\n\t\t\t\t\tthis.#states.totalWaitTime = 0\n\t\t\t\t}\n\n\t\t\t\t// 返回结构化结果\n\t\t\t\treturn {\n\t\t\t\t\tinput,\n\t\t\t\t\toutput: result,\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t}\n\n\t/**\n\t * 获取系统提示词,根据配置的语言动态替换语言设置\n\t */\n\t#getSystemPrompt(): string {\n\t\tif (this.config.customSystemPrompt) {\n\t\t\treturn this.config.customSystemPrompt\n\t\t}\n\n\t\tconst targetLanguage = this.config.language === 'zh-CN' ? '中文' : 'English'\n\t\tconst systemPrompt = SYSTEM_PROMPT.replace(\n\t\t\t/Default working language: \\*\\*.*?\\*\\*/,\n\t\t\t`Default working language: **${targetLanguage}**`\n\t\t)\n\n\t\treturn systemPrompt\n\t}\n\n\t/**\n\t * 从配置中获取指令\n\t */\n\tasync #getInstructions(): Promise<string> {\n\t\tconst { instructions, experimentalLlmsTxt } = this.config\n\n\t\tconst systemInstructions = instructions?.system?.trim()\n\t\tlet pageInstructions: string | undefined\n\n\t\tconst url = this.#states.browserState?.url || ''\n\t\tif (instructions?.getPageInstructions && url) {\n\t\t\ttry {\n\t\t\t\tpageInstructions = instructions.getPageInstructions(url)?.trim()\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\n\t\t\t\t\tformatTerminalText('[PageAgent] Failed to execute getPageInstructions callback:', 'red'),\n\t\t\t\t\terror\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tconst llmsTxt = experimentalLlmsTxt && url ? await fetchLlmsTxt(url) : undefined\n\n\t\tif (!systemInstructions && !pageInstructions && !llmsTxt) return ''\n\n\t\tlet result = '<instructions>\\n'\n\n\t\tif (systemInstructions) {\n\t\t\tresult += `<system_instructions>\\n${systemInstructions}\\n</system_instructions>\\n`\n\t\t}\n\n\t\tif (pageInstructions) {\n\t\t\tresult += `<page_instructions>\\n${pageInstructions}\\n</page_instructions>\\n`\n\t\t}\n\n\t\tif (llmsTxt) {\n\t\t\tresult += `<llms_txt>\\n${llmsTxt}\\n</llms_txt>\\n`\n\t\t}\n\n\t\tresult += '</instructions>\\n\\n'\n\n\t\treturn result\n\t}\n\n\t/**\n\t * 在每一步之前生成系统观察\n\t * @todo 控制台错误\n\t */\n\tasync #handleObservations(step: number): Promise<void> {\n\t\t// 累计等待时间警告\n\t\tif (this.#states.totalWaitTime >= 3) {\n\t\t\tthis.pushObservation(\n\t\t\t\t`You have waited ${this.#states.totalWaitTime} seconds accumulatively. ` +\n\t\t\t\t\t`DO NOT wait any longer unless you have a good reason.`\n\t\t\t)\n\t\t}\n\n\t\t// 循环检测:相同的动作 + 相同的输入连续重复 3 次\n\t\tconst recentSteps = this.#currentTaskHistory\n\t\t\t.filter((e): e is AgentStepEvent => e.type === 'step')\n\t\t\t.slice(-3)\n\t\tif (recentSteps.length === 3) {\n\t\t\tconst isSameAction = (a: AgentStepEvent, b: AgentStepEvent) =>\n\t\t\t\ta.action.name === b.action.name &&\n\t\t\t\tJSON.stringify(a.action.input) === JSON.stringify(b.action.input)\n\t\t\tif (\n\t\t\t\tisSameAction(recentSteps[0], recentSteps[1]) &&\n\t\t\t\tisSameAction(recentSteps[1], recentSteps[2])\n\t\t\t) {\n\t\t\t\tthis.pushObservation(\n\t\t\t\t\t`⚠️ Loop detected: you repeated the exact same action ` +\n\t\t\t\t\t\t`(\"${recentSteps[0].action.name}\" with identical input) 3 times in a row with no new result. ` +\n\t\t\t\t\t\t`STOP repeating it. If the target genuinely does not exist (e.g. search returns empty), ` +\n\t\t\t\t\t\t`report this explicitly and call \\`done\\`, or try a clearly different action/strategy instead.`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// 检测 URL 变化\n\t\tconst currentURL = this.#states.browserState?.url || ''\n\t\tif (currentURL !== this.#states.lastURL) {\n\t\t\tthis.pushObservation(`Page navigated to → ${currentURL}`)\n\t\t\tthis.#states.lastURL = currentURL\n\t\t\tawait waitFor(0.5) // 等待页面稳定\n\t\t}\n\n\t\t// 剩余步数警告\n\t\tconst remaining = this.config.maxSteps - step\n\t\tif (remaining === 5) {\n\t\t\tthis.pushObservation(\n\t\t\t\t`⚠️ Only ${remaining} steps remaining. ` +\n\t\t\t\t\t`Consider wrapping up or calling done with partial results.`\n\t\t\t)\n\t\t} else if (remaining === 2) {\n\t\t\tthis.pushObservation(\n\t\t\t\t`⚠️ Critical: Only ${remaining} steps left! You must finish the task or call done immediately.`\n\t\t\t)\n\t\t}\n\n\t\t// 将观察推入历史并触发事件\n\t\tif (this.#observations.length > 0) {\n\t\t\tfor (const content of this.#observations) {\n\t\t\t\tthis.history.push({ type: 'observation', content })\n\t\t\t\tconsole.log(formatTerminalText('Observation:', 'cyan'), content)\n\t\t\t}\n\t\t\tthis.#observations = []\n\t\t\tthis.#emitHistoryChange()\n\t\t}\n\t}\n\n\t/**\n\t * 组装发给 LLM 的完整用户提示词(含指令、Agent 状态、历史、浏览器状态)\n\t */\n\tasync #assembleUserPrompt(): Promise<string | ContentPart[]> {\n\t\tconst browserState = this.#states.browserState!\n\n\t\tlet prompt = ''\n\n\t\t// <instructions> (可选)\n\n\t\tprompt += await this.#getInstructions()\n\n\t\t// <agent_state>\n\t\t// - <user_request>\n\t\t// - <step_info>\n\t\t// <agent_state>\n\n\t\t// @note 仅取当前任务自身的历史(history 现在跨任务持久保留,供 UI 展示)\n\t\tconst currentTaskHistory = this.#currentTaskHistory\n\t\tconst stepCount = currentTaskHistory.filter((e) => e.type === 'step').length\n\n\t\tprompt += '<agent_state>\\n'\n\t\tprompt += '<user_request>\\n'\n\t\tprompt += `${this.task}\\n`\n\t\tprompt += '</user_request>\\n'\n\t\tprompt += '<step_info>\\n'\n\t\tprompt += `Step ${stepCount + 1} of ${this.config.maxSteps} max possible steps\\n`\n\t\tprompt += `Current time: ${new Date().toLocaleString()}\\n`\n\t\tprompt += '</step_info>\\n'\n\t\tprompt += '</agent_state>\\n\\n'\n\n\t\t// <agent_history>\n\t\t// - <step_N> 用于步骤\n\t\t// - <sys> 用于观察和系统消息\n\t\t//\n\t\t// @edit @workaround 无上限的历史增长\n\t\t// 无限回放每个过去步骤的完整反思/动作文本,会让提示词(进而\"思考\"延迟)每一步都无上限地增长。\n\t\t// 只有最近的 historyWindow 步保留完整详情;更早的会被压缩成一行 ——\n\t\t// 模型自己的 `memory` 字段才是用来向前传递更早上下文的载体。\n\t\tconst windowStart = Math.max(0, stepCount - this.config.historyWindow)\n\n\t\tprompt += '<agent_history>\\n'\n\n\t\tlet stepIndex = 0\n\t\tfor (const event of currentTaskHistory) {\n\t\t\tif (event.type === 'step') {\n\t\t\t\tstepIndex++\n\t\t\t\tif (stepIndex <= windowStart) {\n\t\t\t\t\tprompt += `<step_${stepIndex}>Action: ${event.action.name} — Next Goal: ${event.reflection.next_goal}</step_${stepIndex}>\\n`\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tprompt += `<step_${stepIndex}>\\n`\n\t\t\t\tprompt += `Evaluation of Previous Step: ${event.reflection.evaluation_previous_goal}\\n`\n\t\t\t\tprompt += `Memory: ${event.reflection.memory}\\n`\n\t\t\t\tprompt += `Next Goal: ${event.reflection.next_goal}\\n`\n\t\t\t\tprompt += `Action Results: ${event.action.output}\\n`\n\t\t\t\tprompt += `</step_${stepIndex}>\\n`\n\t\t\t} else if (event.type === 'observation') {\n\t\t\t\tprompt += `<sys>${event.content}</sys>\\n`\n\t\t\t} else if (event.type === 'user_takeover') {\n\t\t\t\tprompt += `<sys>User took over control and made changes to the page</sys>\\n`\n\t\t\t} else if (event.type === 'error') {\n\t\t\t\t// 错误事件主要用于 UI 渲染,不包含在 LLM 上下文中,\n\t\t\t\t// 以免瞬态错误污染 Agent 的推理过程\n\t\t\t}\n\t\t}\n\n\t\tprompt += '</agent_history>\\n\\n'\n\n\t\t// <browser_state>\n\n\t\tlet pageContent = browserState.content\n\t\tif (this.config.transformPageContent) {\n\t\t\tpageContent = await this.config.transformPageContent(pageContent)\n\t\t}\n\n\t\tprompt += '<browser_state>\\n'\n\t\tprompt += browserState.header + '\\n'\n\t\tprompt += pageContent + '\\n'\n\t\tprompt += browserState.footer + '\\n\\n'\n\t\tprompt += '</browser_state>\\n\\n'\n\n\t\t// 附件仅在任务第一步注入,避免图片在后续每一步都重复消耗 token\n\t\tif (stepCount === 0 && this.#taskAttachments.length > 0) {\n\t\t\treturn await this.#injectAttachments(prompt, this.#taskAttachments)\n\t\t}\n\n\t\treturn prompt\n\t}\n\n\t/**\n\t * 把附件内容注入 prompt:图片转为 image_url 片段;文本类文件解码后截断拼进文字部分。\n\t * 仅当存在图片附件时才返回多模态数组,纯文本附件保持返回字符串(不影响不支持 vision 的模型)。\n\t */\n\tasync #injectAttachments(\n\t\tprompt: string,\n\t\tattachments: Attachment[]\n\t): Promise<string | ContentPart[]> {\n\t\tconst textSegments: string[] = [prompt]\n\t\tconst imageParts: ContentPart[] = []\n\n\t\tfor (const attachment of attachments) {\n\t\t\tif (attachment.mimeType.startsWith('image/')) {\n\t\t\t\timageParts.push({ type: 'image_url', image_url: { url: attachment.dataUrl } })\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst text = await suppress(() => fetch(attachment.dataUrl).then((r) => r.text()))\n\t\t\ttextSegments.push(\n\t\t\t\t`<uploaded_file name=\"${attachment.name}\">\\n${truncate(text ?? '', ATTACHMENT_TEXT_MAX_LENGTH)}\\n</uploaded_file>`\n\t\t\t)\n\t\t}\n\n\t\tif (imageParts.length === 0) return textSegments.join('\\n\\n')\n\n\t\treturn [{ type: 'text', text: textSegments.join('\\n\\n') }, ...imageParts]\n\t}\n\n\t/** 销毁 Agent:中止任务、释放浏览器控制器,并触发 dispose 事件(一次性) */\n\tdispose() {\n\t\tif (this.disposed) return\n\t\tthis.disposed = true\n\n\t\tconsole.log('Disposing PageAgent...')\n\t\tconst errors: unknown[] = []\n\t\tconst cleanUp = (callback: () => void) => {\n\t\t\ttry {\n\t\t\t\tcallback()\n\t\t\t} catch (error: unknown) {\n\t\t\t\terrors.push(error)\n\t\t\t}\n\t\t}\n\n\t\tcleanUp(() => this.#abortController.abort())\n\t\tcleanUp(() => this.browserController.dispose())\n\t\t// this.history = []\n\n\t\t// 触发 dispose 事件供 UI 清理\n\t\tcleanUp(() => this.dispatchEvent(new Event('dispose')))\n\n\t\tcleanUp(() => this.config.onDispose?.(this))\n\n\t\tif (errors.length === 1) throw errors[0]\n\t\tif (errors.length > 1) throw new AggregateError(errors, 'PageAgent disposal failed')\n\t}\n}\n","import { BrowserController, type BrowserControllerConfig } from '../browser/BrowserController'\nimport { UI, type UIConfig } from '../ui/UI'\nimport { AgentRuntime } from './AgentRuntime'\nimport type { AgentConfig } from './types'\n\n/** MyPageAgent 的配置:Agent 配置 + 浏览器控制器配置 + UI 配置(语言除外) */\nexport type MyPageAgentConfig = Omit<AgentConfig, 'experimentalScriptExecutionTool'> &\n\tBrowserControllerConfig &\n\tOmit<UIConfig, 'language'>\n\n/**\n * 完整页面智能体:组合了 AgentRuntime、BrowserController 与 UI\n */\nexport class MyPageAgent extends AgentRuntime {\n\t/** UI 实例(用于用户交互与反馈) */\n\treadonly ui: UI\n\n\t/** 构造函数:创建浏览器控制器、父类运行时与 UI 实例 */\n\tconstructor(config: MyPageAgentConfig) {\n\t\tconst browserController = new BrowserController({\n\t\t\t...config,\n\t\t\tenableMask: config.enableMask ?? true,\n\t\t})\n\n\t\tsuper({ ...config, experimentalScriptExecutionTool: false, browserController })\n\n\t\tthis.ui = new UI(this, {\n\t\t\tlanguage: config.language,\n\t\t\tpromptForNextTask: config.promptForNextTask,\n\t\t})\n\t}\n}\n","// 包入口文件:统一导出智能体(Agent)的主类、配置与工具相关类型,并提供创建实例的快捷函数\nimport { MyPageAgent, type MyPageAgentConfig } from './agent/MyPageAgent'\n\nexport { MyPageAgent } // 导出智能体主类,供外部实例化使用\nexport type { MyPageAgentConfig } // 导出智能体配置类型,用于约束 createAgent 的入参\nexport { tool } from './agent/tools' // 导出工具定义辅助函数\nexport type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from './agent/types' // 导出智能体活动、状态、执行结果与历史事件等通用类型\n\n/**\n * 创建智能体实例的快捷函数,内部直接实例化 MyPageAgent。\n * @param config 智能体的配置对象\n * @returns 创建好的 MyPageAgent 实例\n */\nexport function createAgent(config: MyPageAgentConfig): MyPageAgent {\n\treturn new MyPageAgent(config)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["#createCursor","#moveCursorToTarget","#cursor","#disposed","#currentCursorX","#targetCursorX","#currentCursorY","#targetCursorY","#handleStatusChange","#handleHistoryChange","#handleActivity","#askUser","#wrapper","#agent","#config","#i18n","#askUserCallback","#createWrapper","#indicator","#statusText","#historySectionWrapper","#historySection","#expandButton","#actionButton","#inputSection","#taskInput","#attachButton","#fileInput","#attachmentsBar","#attachError","#onStatusChange","#onHistoryChange","#onActivity","#onAgentDispose","#setupEventListeners","#startHeaderUpdateLoop","#showInputArea","#updateStatusIndicator","#hideInputArea","#isExpanded","#expand","#shouldShowInputArea","#renderHistory","#pendingHeaderText","#getToolExecutingText","#disposed","#createAbortError","#rejectPendingQuestion","#isWaitingForUserAnswer","#userAnswerResolver","#userAnswerRejecter","#scrollToBottom","#syncHistoryHeight","#userAnswerSignal","#userAnswerAbortHandler","#removeTempCards","#clearQuestionState","#collapse","#pendingAttachments","#renderAttachments","#setAttachError","#stopHeaderUpdateLoop","#timers","#handleUserAnswer","#lastSubmission","#syncInputHeight","#setTimer","#handleFileSelection","#isSupportedAttachment","#readFileAsDataUrl","#toggle","#handleActionButton","#submitTask","#restoreLastSubmission","#removeAttachment","#headerUpdateTimer","#checkAndUpdateHeader","#isAnimating","#animateTextChange","#createTaskCard","#createHistoryCards","#createActionCards","#llm","#emitActivity","#emitHistoryChange","#status","#lastResult","#currentTaskHistory","#taskHistoryStart","#emitStatusChange","#observations","#abortController","#running","#taskAttachments","#states","#setStatus","#handleObservations","#getSystemPrompt","#assembleUserPrompt","#packMacroTool","#getInstructions","#injectAttachments"],"sources":["../src/browser/utils/index.ts","../src/browser/actions.ts","../src/browser/dom/dom_tree/index.js","../src/browser/dom/index.ts","../src/browser/dom/getPageInfo.ts","../src/browser/patches/react.ts","../src/browser/visual/checkDarkMode.ts","../src/browser/visual/SimulatorMask.module.css","../src/browser/visual/cursor.module.css","../src/browser/visual/SimulatorMask.ts","../src/browser/BrowserController.ts","../src/ui/i18n/locales.ts","../src/ui/i18n/index.ts","../src/ui/utils.ts","../src/ui/styles/UI.module.css","../src/ui/cards.ts","../src/ui/UI.ts","../src/llm/errors.ts","../src/shared/terminal.ts","../src/llm/utils.ts","../src/llm/OpenAICompatibleClient.ts","../src/llm/LLM.ts","../src/agent/utils/autoFixer.ts","../src/agent/utils/index.ts","../src/agent/tools/index.ts","../src/agent/AgentRuntime.ts","../src/agent/MyPageAgent.ts","../src/index.ts"],"sourcesContent":["// ======= 类型守卫 =======\n// @note 在 iframe 内的元素上使用 instanceof 会失败\n\n/** 判断值是否为 HTMLElement(type guard)。 */\nexport function isHTMLElement(el: unknown): el is HTMLElement {\n\t// @todo 要么收窄为 HTMLElement,要么在这里放宽为 Element。\n\treturn !!el && (el as Node).nodeType === 1\n}\n\n/** 判断元素是否为 <input>(type guard)。 */\nexport function isInputElement(el: Element): el is HTMLInputElement {\n\treturn el?.nodeType === 1 && el.tagName === 'INPUT'\n}\n\n/** 判断元素是否为 <textarea>(type guard)。 */\nexport function isTextAreaElement(el: Element): el is HTMLTextAreaElement {\n\treturn el?.nodeType === 1 && el.tagName === 'TEXTAREA'\n}\n\n/** 判断元素是否为 <select>(type guard)。 */\nexport function isSelectElement(el: Element): el is HTMLSelectElement {\n\treturn el?.nodeType === 1 && el.tagName === 'SELECT'\n}\n\n/** 判断元素是否为 <a>(type guard)。 */\nexport function isAnchorElement(el: Element): el is HTMLAnchorElement {\n\treturn el?.nodeType === 1 && el.tagName === 'A'\n}\n\n// ======= iframe 辅助函数 =======\n\n/** 用于将元素坐标换算为顶层视口坐标的 iframe 偏移量。 */\nexport function getIframeOffset(element: HTMLElement): { x: number; y: number } {\n\tconst frame = element.ownerDocument.defaultView?.frameElement as HTMLElement | null\n\tif (!frame) return { x: 0, y: 0 } // 无 iframe 时,x/y 偏移均为 0\n\tconst rect = frame.getBoundingClientRect()\n\treturn { x: rect.left, y: rect.top } // 返回 iframe 左上角相对视口的坐标偏移 { x, y }\n}\n\n/**\n * 从元素自身的原型上获取原生 value setter(iframe 安全)。\n * @note 用于 React\n */\nexport function getNativeValueSetter(element: HTMLInputElement | HTMLTextAreaElement) {\n\treturn Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element) as object, 'value')!\n\t\t.set as (v: string) => void\n}\n\n// ======= 通用工具函数 =======\n\n/** 等待指定的秒数。 */\nexport async function waitFor(seconds: number): Promise<void> {\n\tawait new Promise((resolve) => setTimeout(resolve, seconds * 1000))\n}\n\n// ======= 遮罩事件 =======\n\n/**\n * 将视觉指针移动到元素内的某个位置。\n * @param x - 元素所属文档视口内的 x 坐标\n * @param y - 元素所属文档视口内的 y 坐标\n */\nexport async function movePointerToElement(element: HTMLElement, x: number, y: number) {\n\tconst offset = getIframeOffset(element)\n\n\twindow.dispatchEvent(\n\t\tnew CustomEvent('PageAgent::MovePointerTo', {\n\t\t\tdetail: { x: x + offset.x, y: y + offset.y }, // 目标点在顶层视口内的坐标 { x, y }\n\t\t})\n\t)\n\n\tawait waitFor(0.3)\n}\n\n/** 触发一次模拟光标点击动画。 */\nexport async function clickPointer() {\n\twindow.dispatchEvent(new CustomEvent('PageAgent::ClickPointer'))\n}\n\n/** 开启遮罩穿透,鼠标事件不再被遮罩拦截。 */\nexport async function enablePassThrough() {\n\twindow.dispatchEvent(new CustomEvent('PageAgent::EnablePassThrough'))\n}\n\n/** 关闭遮罩穿透,遮罩重新拦截鼠标事件。 */\nexport async function disablePassThrough() {\n\twindow.dispatchEvent(new CustomEvent('PageAgent::DisablePassThrough'))\n}\n","/**\n * 版权所有 (C) 2025 Alibaba Group Holding Limited\n * 保留所有权利。\n */\nimport type { InteractiveElementDomNode } from './dom/dom_tree/type'\nimport {\n\tclickPointer,\n\tdisablePassThrough,\n\tenablePassThrough,\n\tgetNativeValueSetter,\n\tisHTMLElement,\n\tisInputElement,\n\tisSelectElement,\n\tisTextAreaElement,\n\tmovePointerToElement,\n\twaitFor,\n} from './utils'\n\n/**\n * 根据索引从 selectorMap 中获取对应的 HTMLElement。\n * @private 内部方法,随时可能变更。\n */\nexport function getElementByIndex(\n\tselectorMap: Map<number, InteractiveElementDomNode>,\n\tindex: number\n): HTMLElement {\n\tconst interactiveNode = selectorMap.get(index)\n\tif (!interactiveNode) {\n\t\tthrow new Error(`No interactive element found at index ${index}`)\n\t}\n\n\tconst element = interactiveNode.ref\n\tif (!element) {\n\t\tthrow new Error(`Element at index ${index} does not have a reference`)\n\t}\n\n\tif (!isHTMLElement(element)) {\n\t\tthrow new Error(`Element at index ${index} is not an HTMLElement`)\n\t}\n\n\treturn element\n}\n\n// 记录最近一次被点击的元素,用于下次点击前触发失焦相关事件\nlet lastClickedElement: HTMLElement | null = null\n\n/**\n * 触发上次点击元素的失焦相关事件(pointerout/pointerleave/mouseout/mouseleave/blur)并清空记录。\n */\nfunction blurLastClickedElement() {\n\tif (lastClickedElement) {\n\t\tlastClickedElement.dispatchEvent(new PointerEvent('pointerout', { bubbles: true }))\n\t\tlastClickedElement.dispatchEvent(new PointerEvent('pointerleave', { bubbles: false }))\n\t\tlastClickedElement.dispatchEvent(new MouseEvent('mouseout', { bubbles: true }))\n\t\tlastClickedElement.dispatchEvent(new MouseEvent('mouseleave', { bubbles: false }))\n\t\tlastClickedElement.blur()\n\t\tlastClickedElement = null\n\t}\n}\n\n/**\n * 模拟一次完整的点击,遵循 W3C Pointer Events + UI Events 规范顺序:\n * pointerover/enter → mouseover/enter → pointerdown → mousedown → [focus] →\n * pointerup → mouseup → click\n *\n * @private 内部方法,随时可能变更。\n */\nexport async function clickElement(element: HTMLElement) {\n\tblurLastClickedElement()\n\n\tlastClickedElement = element\n\n\tawait scrollIntoViewIfNeeded(element)\n\tconst frame = element.ownerDocument.defaultView?.frameElement\n\tif (frame) await scrollIntoViewIfNeeded(frame)\n\n\tconst rect = element.getBoundingClientRect()\n\tconst x = rect.left + rect.width / 2\n\tconst y = rect.top + rect.height / 2\n\n\tawait movePointerToElement(element, x, y)\n\tawait clickPointer()\n\n\tawait waitFor(0.05)\n\n\t// 命中测试:找出点击坐标处最深的元素,与真实浏览器行为一致——事件会命中最内层的元素\n\t// @note 这可能会命中黑名单中的元素\n\t// TODO: 这是一个临时方案,本应在 DOM 提取阶段就处理。\n\tconst doc = element.ownerDocument\n\tawait enablePassThrough()\n\tconst hitTarget = doc.elementFromPoint(x, y)\n\tawait disablePassThrough()\n\tconst target =\n\t\thitTarget instanceof HTMLElement && element.contains(hitTarget) ? hitTarget : element\n\n\tconst pointerOpts = {\n\t\tbubbles: true, // 事件是否冒泡\n\t\tcancelable: true, // 事件是否可取消\n\t\tclientX: x, // 点击点的 X 坐标(视口相对)\n\t\tclientY: y, // 点击点的 Y 坐标(视口相对)\n\t\tpointerType: 'mouse', // 指针设备类型\n\t}\n\tconst mouseOpts = {\n\t\tbubbles: true, // 事件是否冒泡\n\t\tcancelable: true, // 事件是否可取消\n\t\tclientX: x, // 点击点的 X 坐标(视口相对)\n\t\tclientY: y, // 点击点的 Y 坐标(视口相对)\n\t\tbutton: 0, // 鼠标按钮(0 表示左键)\n\t}\n\n\t// 悬停 — 先派发指针事件,再派发鼠标事件(规范顺序)\n\ttarget.dispatchEvent(new PointerEvent('pointerover', pointerOpts))\n\ttarget.dispatchEvent(new PointerEvent('pointerenter', { ...pointerOpts, bubbles: false }))\n\ttarget.dispatchEvent(new MouseEvent('mouseover', mouseOpts))\n\ttarget.dispatchEvent(new MouseEvent('mouseenter', { ...mouseOpts, bubbles: false }))\n\n\t// 按下\n\ttarget.dispatchEvent(new PointerEvent('pointerdown', pointerOpts))\n\ttarget.dispatchEvent(new MouseEvent('mousedown', mouseOpts))\n\n\t// 焦点不属于标准的指针/鼠标事件序列,\n\t// 其行为“未定义且因浏览器而异”。\n\t// 这里聚焦的是原始元素(最近的可聚焦祖先),而非命中测试目标,以匹配浏览器行为。\n\telement.focus({ preventScroll: true })\n\n\t// 释放\n\ttarget.dispatchEvent(new PointerEvent('pointerup', pointerOpts))\n\ttarget.dispatchEvent(new MouseEvent('mouseup', mouseOpts))\n\n\t// 点击 — 激活行为(导航、表单提交等)通过从目标向上冒泡到可交互的祖先元素来触发\n\ttarget.click()\n\n\tawait waitFor(0.1)\n}\n\n/**\n * 向输入类元素(input / textarea / contenteditable)输入文本,并触发相应的输入事件。\n * @private 内部方法,随时可能变更。\n */\nexport async function inputTextElement(element: HTMLElement, text: string) {\n\tconst isContentEditable = element.isContentEditable\n\tif (!isInputElement(element) && !isTextAreaElement(element) && !isContentEditable) {\n\t\tthrow new Error('Element is not an input, textarea, or contenteditable')\n\t}\n\n\tawait clickElement(element)\n\n\tif (isContentEditable) {\n\t\t// 对 contenteditable 的支持(不完整)\n\t\t// 不支持:\n\t\t// - Monaco/CodeMirror:需要直接访问其 JS 实例,没有通用的获取方式。\n\t\t// - Draft.js:对合成事件 / execCommand / Range / DataTransfer 均无响应,且已不再维护。\n\t\t//\n\t\t// 策略:先尝试方案 A(合成事件),验证失败后回退到方案 B(execCommand)再插入文本。\n\t\t//\n\t\t// 方案 A:派发合成事件\n\t\t// 适用:React contenteditable、Quill。\n\t\t// 不适用:Slate.js 以及一些忽略合成事件的 contenteditable 编辑器。\n\t\t// 事件顺序:beforeinput -> mutation -> input -> change -> blur\n\n\t\t// 派发 beforeinput + mutation + input 以清空内容\n\t\tif (\n\t\t\telement.dispatchEvent(\n\t\t\t\tnew InputEvent('beforeinput', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tcancelable: true,\n\t\t\t\t\tinputType: 'deleteContent',\n\t\t\t\t})\n\t\t\t)\n\t\t) {\n\t\t\telement.innerText = ''\n\t\t\telement.dispatchEvent(\n\t\t\t\tnew InputEvent('input', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tinputType: 'deleteContent',\n\t\t\t\t})\n\t\t\t)\n\t\t}\n\n\t\t// 派发 beforeinput + mutation + input 以插入文本(对 React 应用很重要)\n\t\tif (\n\t\t\telement.dispatchEvent(\n\t\t\t\tnew InputEvent('beforeinput', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tcancelable: true,\n\t\t\t\t\tinputType: 'insertText',\n\t\t\t\t\tdata: text,\n\t\t\t\t})\n\t\t\t)\n\t\t) {\n\t\t\telement.innerText = text\n\t\t\telement.dispatchEvent(\n\t\t\t\tnew InputEvent('input', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tinputType: 'insertText',\n\t\t\t\t\tdata: text,\n\t\t\t\t})\n\t\t\t)\n\t\t}\n\n\t\t// 通过检查文本是否真正插入来验证方案 A 是否成功\n\t\tconst planASucceeded = element.innerText.trim() === text.trim()\n\n\t\tif (!planASucceeded) {\n\t\t\t// 方案 B:execCommand 回退(已弃用但广泛支持)\n\t\t\t// 适用:Quill、Slate.js、react contenteditable 组件。\n\t\t\t// 这种方式会接入浏览器的撤销栈,并被大多数富文本编辑器原生处理。\n\t\t\telement.focus()\n\n\t\t\t// 选中全部现有内容并删除\n\t\t\tconst doc = element.ownerDocument\n\t\t\tconst selection = (doc.defaultView || window).getSelection()\n\t\t\tconst range = doc.createRange()\n\t\t\trange.selectNodeContents(element)\n\t\t\tselection?.removeAllRanges()\n\t\t\tselection?.addRange(range)\n\n\t\t\tdoc.execCommand('delete', false)\n\t\t\tdoc.execCommand('insertText', false, text)\n\t\t}\n\n\t\t// 派发 change 事件(保险起见)\n\t\telement.dispatchEvent(new Event('change', { bubbles: true }))\n\n\t\t// 触发 blur 以用于校验\n\t\telement.blur()\n\t} else {\n\t\tgetNativeValueSetter(element as HTMLInputElement | HTMLTextAreaElement).call(element, text)\n\t}\n\n\t// 仅对非 contenteditable 元素派发共享的 input 事件(contenteditable 有自己的处理)\n\tif (!isContentEditable) {\n\t\telement.dispatchEvent(new Event('input', { bubbles: true }))\n\t}\n\n\tawait waitFor(0.05)\n\n\tblurLastClickedElement()\n}\n\n/**\n * 在下拉选择框(select)中按文本匹配并选中对应选项,然后触发 change 事件。\n * @todo browser-use 版本非常复杂且支持菜单标签,需要跟进\n * @private 内部方法,随时可能变更。\n */\nexport async function selectOptionElement(selectElement: HTMLSelectElement, optionText: string) {\n\tif (!isSelectElement(selectElement)) {\n\t\tthrow new Error('Element is not a select element')\n\t}\n\n\tconst options = Array.from(selectElement.options)\n\tconst option = options.find((opt) => opt.textContent?.trim() === optionText.trim())\n\n\tif (!option) {\n\t\tthrow new Error(`Option with text \"${optionText}\" not found in select element`)\n\t}\n\n\tselectElement.value = option.value\n\tselectElement.dispatchEvent(new Event('change', { bubbles: true }))\n\n\tawait waitFor(0.05) // 等待以确保 change 事件处理完成\n}\n\ninterface ScrollableElement extends Element {\n\tscrollIntoViewIfNeeded?: (centerIfNeeded?: boolean) => void // 可选的原生方法:必要时将元素滚动到可视区域\n}\n\n/**\n * 将元素滚动到可视区域内;优先使用原生 scrollIntoViewIfNeeded,不支持时回退为 scrollIntoView。\n * @private 内部方法,随时可能变更。\n */\nexport async function scrollIntoViewIfNeeded(element: Element) {\n\tconst el = element as ScrollableElement\n\tif (typeof el.scrollIntoViewIfNeeded === 'function') {\n\t\tel.scrollIntoViewIfNeeded()\n\t\t// await waitFor(0.5) // 动画播放\n\t} else {\n\t\t// @todo 可见性检查\n\t\telement.scrollIntoView({ behavior: 'auto', block: 'center', inline: 'nearest' })\n\t\t// await waitFor(0.5) // 动画播放\n\t}\n}\n\n/**\n * 垂直滚动页面或指定元素容器。\n * @param scroll_amount - 滚动像素数(正数向下,负数向上)\n * @param element - 可选;若传入,则优先滚动该元素及其祖先中最近的可纵向滚动容器,否则滚动页面\n * @returns 滚动结果的描述消息\n */\nexport async function scrollVertically(scroll_amount: number, element?: HTMLElement | null) {\n\t// 如果提供了元素,则针对该元素进行滚动\n\tif (element) {\n\t\tconst targetElement = element\n\t\tlet currentElement = targetElement as HTMLElement | null\n\t\tlet scrollSuccess = false\n\t\tlet scrolledElement: HTMLElement | null = null\n\t\tlet scrollDelta = 0\n\t\tlet attempts = 0\n\t\tconst dy = scroll_amount\n\n\t\twhile (currentElement && attempts < 10) {\n\t\t\tconst computedStyle = window.getComputedStyle(currentElement)\n\t\t\tconst hasScrollableY =\n\t\t\t\t/(auto|scroll|overlay)/.test(computedStyle.overflowY) ||\n\t\t\t\t(computedStyle.scrollbarWidth && computedStyle.scrollbarWidth !== 'auto') ||\n\t\t\t\t(computedStyle.scrollbarGutter && computedStyle.scrollbarGutter !== 'auto')\n\t\t\tconst canScrollVertically = currentElement.scrollHeight > currentElement.clientHeight\n\n\t\t\tif (hasScrollableY && canScrollVertically) {\n\t\t\t\tconst beforeScroll = currentElement.scrollTop\n\t\t\t\tconst maxScroll = currentElement.scrollHeight - currentElement.clientHeight\n\n\t\t\t\tlet scrollAmount = dy / 3\n\n\t\t\t\tif (scrollAmount > 0) {\n\t\t\t\t\tscrollAmount = Math.min(scrollAmount, maxScroll - beforeScroll)\n\t\t\t\t} else {\n\t\t\t\t\tscrollAmount = Math.max(scrollAmount, -beforeScroll)\n\t\t\t\t}\n\n\t\t\t\tcurrentElement.scrollTop = beforeScroll + scrollAmount\n\n\t\t\t\tconst afterScroll = currentElement.scrollTop\n\t\t\t\tconst actualScrollDelta = afterScroll - beforeScroll\n\n\t\t\t\tif (Math.abs(actualScrollDelta) > 0.5) {\n\t\t\t\t\tscrollSuccess = true\n\t\t\t\t\tscrolledElement = currentElement\n\t\t\t\t\tscrollDelta = actualScrollDelta\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentElement === document.body || currentElement === document.documentElement) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcurrentElement = currentElement.parentElement\n\t\t\tattempts++\n\t\t}\n\n\t\tif (scrollSuccess) {\n\t\t\treturn `Scrolled container (${scrolledElement?.tagName}) by ${scrollDelta}px`\n\t\t} else {\n\t\t\treturn `No scrollable container found for element (${targetElement.tagName})`\n\t\t}\n\t}\n\n\t// 页级滚动(默认或回退逻辑)\n\tconst dy = scroll_amount\n\t// 判断元素高度是否足够大(至少占视口一半),用于筛选页级滚动容器\n\tconst bigEnough = (el: HTMLElement) => el.clientHeight >= window.innerHeight * 0.5\n\t// 判断元素是否为可纵向滚动的容器\n\tconst canScroll = (el: HTMLElement | null): boolean =>\n\t\tBoolean(\n\t\t\tel &&\n\t\t\t/(auto|scroll|overlay)/.test(getComputedStyle(el).overflowY) &&\n\t\t\tel.scrollHeight > el.clientHeight &&\n\t\t\tbigEnough(el)\n\t\t)\n\n\t// @deprecated 启发式容器查找。\n\t// 在多区域布局中不可靠。应引导 LLM 使用带索引的滚动以保证一致性。\n\t// TODO: 移除该回退逻辑\n\n\t// 尝试查找最近的滚动容器\n\t// document.activeElement 通常是 body。\n\t// 在 element.focus() 成功之后,activeElement 变为最近的可聚焦父元素\n\n\tlet el: HTMLElement | null = document.activeElement as HTMLElement | null\n\twhile (el && !canScroll(el) && el !== document.body) el = el.parentElement\n\n\t// 如果回退到全局 '*' 搜索,说明有问题\n\t// TODO: 返回错误消息而不是全局 '*' 搜索\n\n\tel = canScroll(el)\n\t\t? el\n\t\t: Array.from(document.querySelectorAll<HTMLElement>('*')).find(canScroll) ||\n\t\t\t(document.scrollingElement as HTMLElement) ||\n\t\t\t(document.documentElement as HTMLElement)\n\n\tif (el === document.scrollingElement || el === document.documentElement || el === document.body) {\n\t\t// 页级滚动\n\t\tconst scrollBefore = window.scrollY\n\t\tconst scrollMax = document.documentElement.scrollHeight - window.innerHeight\n\n\t\twindow.scrollBy(0, dy)\n\n\t\tconst scrollAfter = window.scrollY\n\t\tconst scrolled = scrollAfter - scrollBefore\n\n\t\tif (Math.abs(scrolled) < 1) {\n\t\t\treturn dy > 0\n\t\t\t\t? `⚠️ Already at the bottom of the page, cannot scroll down further.`\n\t\t\t\t: `⚠️ Already at the top of the page, cannot scroll up further.`\n\t\t}\n\n\t\tconst reachedBottom = dy > 0 && scrollAfter >= scrollMax - 1\n\t\tconst reachedTop = dy < 0 && scrollAfter <= 1\n\n\t\tif (reachedBottom) return `✅ Scrolled page by ${scrolled}px. Reached the bottom of the page.`\n\t\tif (reachedTop) return `✅ Scrolled page by ${scrolled}px. Reached the top of the page.`\n\t\treturn `✅ Scrolled page by ${scrolled}px.`\n\t} else {\n\t\t// 容器滚动\n\n\t\tconst warningMsg = `The document is not scrollable. Falling back to container scroll.`\n\t\tconsole.log(`[BrowserController] ${warningMsg}`)\n\n\t\tconst scrollBefore = el!.scrollTop\n\t\tconst scrollMax = el!.scrollHeight - el!.clientHeight\n\n\t\tel!.scrollBy({ top: dy, behavior: 'smooth' })\n\t\tawait waitFor(0.1)\n\n\t\tconst scrollAfter = el!.scrollTop\n\t\tconst scrolled = scrollAfter - scrollBefore\n\n\t\tif (Math.abs(scrolled) < 1) {\n\t\t\treturn dy > 0\n\t\t\t\t? `⚠️ ${warningMsg} Already at the bottom of container (${el!.tagName}), cannot scroll down further.`\n\t\t\t\t: `⚠️ ${warningMsg} Already at the top of container (${el!.tagName}), cannot scroll up further.`\n\t\t}\n\n\t\tconst reachedBottom = dy > 0 && scrollAfter >= scrollMax - 1\n\t\tconst reachedTop = dy < 0 && scrollAfter <= 1\n\n\t\tif (reachedBottom)\n\t\t\treturn `✅ ${warningMsg} Scrolled container (${el!.tagName}) by ${scrolled}px. Reached the bottom.`\n\t\tif (reachedTop)\n\t\t\treturn `✅ ${warningMsg} Scrolled container (${el!.tagName}) by ${scrolled}px. Reached the top.`\n\t\treturn `✅ ${warningMsg} Scrolled container (${el!.tagName}) by ${scrolled}px.`\n\t}\n}\n\n/**\n * 水平滚动页面或指定元素容器。\n * @param scroll_amount - 滚动像素数(正数向右,负数向左)\n * @param element - 可选;若传入,则优先滚动该元素及其祖先中最近的可横向滚动容器,否则滚动页面\n * @returns 滚动结果的描述消息\n */\nexport async function scrollHorizontally(scroll_amount: number, element?: HTMLElement | null) {\n\t// 如果提供了元素,则针对该元素进行滚动\n\tif (element) {\n\t\tconst targetElement = element\n\t\tlet currentElement = targetElement as HTMLElement | null\n\t\tlet scrollSuccess = false\n\t\tlet scrolledElement: HTMLElement | null = null\n\t\tlet scrollDelta = 0\n\t\tlet attempts = 0\n\t\tconst dx = scroll_amount\n\n\t\twhile (currentElement && attempts < 10) {\n\t\t\tconst computedStyle = window.getComputedStyle(currentElement)\n\t\t\tconst hasScrollableX =\n\t\t\t\t/(auto|scroll|overlay)/.test(computedStyle.overflowX) ||\n\t\t\t\t(computedStyle.scrollbarWidth && computedStyle.scrollbarWidth !== 'auto') ||\n\t\t\t\t(computedStyle.scrollbarGutter && computedStyle.scrollbarGutter !== 'auto')\n\t\t\tconst canScrollHorizontally = currentElement.scrollWidth > currentElement.clientWidth\n\n\t\t\tif (hasScrollableX && canScrollHorizontally) {\n\t\t\t\tconst beforeScroll = currentElement.scrollLeft\n\t\t\t\tconst maxScroll = currentElement.scrollWidth - currentElement.clientWidth\n\n\t\t\t\tlet scrollAmount = dx / 3\n\n\t\t\t\tif (scrollAmount > 0) {\n\t\t\t\t\tscrollAmount = Math.min(scrollAmount, maxScroll - beforeScroll)\n\t\t\t\t} else {\n\t\t\t\t\tscrollAmount = Math.max(scrollAmount, -beforeScroll)\n\t\t\t\t}\n\n\t\t\t\tcurrentElement.scrollLeft = beforeScroll + scrollAmount\n\n\t\t\t\tconst afterScroll = currentElement.scrollLeft\n\t\t\t\tconst actualScrollDelta = afterScroll - beforeScroll\n\n\t\t\t\tif (Math.abs(actualScrollDelta) > 0.5) {\n\t\t\t\t\tscrollSuccess = true\n\t\t\t\t\tscrolledElement = currentElement\n\t\t\t\t\tscrollDelta = actualScrollDelta\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentElement === document.body || currentElement === document.documentElement) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcurrentElement = currentElement.parentElement\n\t\t\tattempts++\n\t\t}\n\n\t\tif (scrollSuccess) {\n\t\t\treturn `Scrolled container (${scrolledElement?.tagName}) horizontally by ${scrollDelta}px`\n\t\t} else {\n\t\t\treturn `No horizontally scrollable container found for element (${targetElement.tagName})`\n\t\t}\n\t}\n\n\t// 页级滚动(默认或回退逻辑)\n\n\tconst dx = scroll_amount\n\n\t// 判断元素宽度是否足够大(至少占视口一半),用于筛选页级滚动容器\n\tconst bigEnough = (el: HTMLElement) => el.clientWidth >= window.innerWidth * 0.5\n\t// 判断元素是否为可横向滚动的容器\n\tconst canScroll = (el: HTMLElement | null): boolean =>\n\t\tBoolean(\n\t\t\tel &&\n\t\t\t/(auto|scroll|overlay)/.test(getComputedStyle(el).overflowX) &&\n\t\t\tel.scrollWidth > el.clientWidth &&\n\t\t\tbigEnough(el)\n\t\t)\n\n\t// @deprecated 与 scrollVertically 相同的启发式容器查找。\n\t// TODO: 一旦 LLM 可靠地通过 data-scrollable 使用带索引的滚动,就移除此处逻辑。\n\n\tlet el: HTMLElement | null = document.activeElement as HTMLElement | null\n\twhile (el && !canScroll(el) && el !== document.body) el = el.parentElement\n\n\tel = canScroll(el)\n\t\t? el\n\t\t: Array.from(document.querySelectorAll<HTMLElement>('*')).find(canScroll) ||\n\t\t\t(document.scrollingElement as HTMLElement) ||\n\t\t\t(document.documentElement as HTMLElement)\n\n\tif (el === document.scrollingElement || el === document.documentElement || el === document.body) {\n\t\t// 页级滚动\n\t\tconst scrollBefore = window.scrollX\n\t\tconst scrollMax = document.documentElement.scrollWidth - window.innerWidth\n\n\t\twindow.scrollBy(dx, 0)\n\n\t\tconst scrollAfter = window.scrollX\n\t\tconst scrolled = scrollAfter - scrollBefore\n\n\t\tif (Math.abs(scrolled) < 1) {\n\t\t\treturn dx > 0\n\t\t\t\t? `⚠️ Already at the right edge of the page, cannot scroll right further.`\n\t\t\t\t: `⚠️ Already at the left edge of the page, cannot scroll left further.`\n\t\t}\n\n\t\tconst reachedRight = dx > 0 && scrollAfter >= scrollMax - 1\n\t\tconst reachedLeft = dx < 0 && scrollAfter <= 1\n\n\t\tif (reachedRight)\n\t\t\treturn `✅ Scrolled page by ${scrolled}px. Reached the right edge of the page.`\n\t\tif (reachedLeft) return `✅ Scrolled page by ${scrolled}px. Reached the left edge of the page.`\n\t\treturn `✅ Scrolled page horizontally by ${scrolled}px.`\n\t} else {\n\t\t// 容器滚动\n\t\tconst warningMsg = `The document is not scrollable. Falling back to container scroll.`\n\t\tconsole.log(`[BrowserController] ${warningMsg}`)\n\n\t\tconst scrollBefore = el!.scrollLeft\n\t\tconst scrollMax = el!.scrollWidth - el!.clientWidth\n\n\t\tel!.scrollBy({ left: dx, behavior: 'smooth' })\n\t\tawait waitFor(0.1)\n\n\t\tconst scrollAfter = el!.scrollLeft\n\t\tconst scrolled = scrollAfter - scrollBefore\n\n\t\tif (Math.abs(scrolled) < 1) {\n\t\t\treturn dx > 0\n\t\t\t\t? `⚠️ ${warningMsg} Already at the right edge of container (${el!.tagName}), cannot scroll right further.`\n\t\t\t\t: `⚠️ ${warningMsg} Already at the left edge of container (${el!.tagName}), cannot scroll left further.`\n\t\t}\n\n\t\tconst reachedRight = dx > 0 && scrollAfter >= scrollMax - 1\n\t\tconst reachedLeft = dx < 0 && scrollAfter <= 1\n\n\t\tif (reachedRight)\n\t\t\treturn `✅ ${warningMsg} Scrolled container (${el!.tagName}) by ${scrolled}px. Reached the right edge.`\n\t\tif (reachedLeft)\n\t\t\treturn `✅ ${warningMsg} Scrolled container (${el!.tagName}) by ${scrolled}px. Reached the left edge.`\n\t\treturn `✅ ${warningMsg} Scrolled container (${el!.tagName}) horizontally by ${scrolled}px.`\n\t}\n}\n","/**\n * @file port from browser-use\n * @see https://github.com/browser-use/browser-use/commits/main/browser_use/dom/dom_tree/index.js\n * @match 0.5.9 d51b6e73daff7165fdd3e44debd667e7f5f7fdc5\n *\n * search @edit for all the changed lines.\n *\n * @edit export\n * @edit add interactiveBlacklist interactiveWhitelist\n * @edit adjustable opacity\n * @edit direct dom ref\n * @edit @workaround input.checked\n * @edit smaller zIndex for highlight\n * @edit no need for xpath\n * @edit add `extra` field for extra data\n * @edit scrollable element detection\n * @edit add `data-browser-use-ignore` attribute\n * @edit improve `sampleRect`, filter out rects with 0 area\n * @edit exclude aria-hidden elements\n * @edit make sure attributes exist for interactive candidates.\n * @edit fix \"aria-*\" attributes check\n */\n\n/* eslint-disable @typescript-eslint/no-unused-vars, no-undef, no-useless-assignment */\n\nexport default (\n\targs = {\n\t\tdoHighlightElements: true,\n\t\tfocusHighlightIndex: -1,\n\t\tviewportExpansion: 0,\n\t\tdebugMode: false,\n\n\t\t/**\n\t\t * @edit\n\t\t */\n\t\t/** @type {Element[]} */\n\t\tinteractiveBlacklist: [],\n\t\t/** @type {Element[]} */\n\t\tinteractiveWhitelist: [],\n\t\thighlightOpacity: 0.1,\n\t\thighlightLabelOpacity: 0.5,\n\t}\n) => {\n\t/**\n\t * @edit\n\t */\n\tconst { interactiveBlacklist, interactiveWhitelist, highlightOpacity, highlightLabelOpacity } =\n\t\targs\n\n\tconst { doHighlightElements, focusHighlightIndex, viewportExpansion, debugMode } = args\n\tlet highlightIndex = 0 // Reset highlight index\n\n\t/**\n\t * @edit add `extra` field for extra data\n\t */\n\tconst extraData = new WeakMap()\n\tfunction addExtraData(element, data) {\n\t\tif (!element || element.nodeType !== Node.ELEMENT_NODE) return\n\t\textraData.set(element, { ...extraData.get(element), ...data })\n\t}\n\n\t// Add caching mechanisms at the top level\n\tconst DOM_CACHE = {\n\t\tboundingRects: new WeakMap(),\n\t\tclientRects: new WeakMap(),\n\t\tcomputedStyles: new WeakMap(),\n\t\tclearCache: () => {\n\t\t\tDOM_CACHE.boundingRects = new WeakMap()\n\t\t\tDOM_CACHE.clientRects = new WeakMap()\n\t\t\tDOM_CACHE.computedStyles = new WeakMap()\n\t\t},\n\t}\n\n\t/**\n\t * Gets the cached bounding rect for an element.\n\t *\n\t * @param {HTMLElement} element - The element to get the bounding rect for.\n\t * @returns {DOMRect | null} The cached bounding rect, or null if the element is not found.\n\t */\n\tfunction getCachedBoundingRect(element) {\n\t\tif (!element) return null\n\n\t\tif (DOM_CACHE.boundingRects.has(element)) {\n\t\t\treturn DOM_CACHE.boundingRects.get(element)\n\t\t}\n\n\t\tconst rect = element.getBoundingClientRect()\n\n\t\tif (rect) {\n\t\t\tDOM_CACHE.boundingRects.set(element, rect)\n\t\t}\n\t\treturn rect\n\t}\n\n\t/**\n\t * Gets the cached computed style for an element.\n\t *\n\t * @param {HTMLElement} element - The element to get the computed style for.\n\t * @returns {CSSStyleDeclaration | null} The cached computed style, or null if the element is not found.\n\t */\n\tfunction getCachedComputedStyle(element) {\n\t\tif (!element) return null\n\n\t\tif (DOM_CACHE.computedStyles.has(element)) {\n\t\t\treturn DOM_CACHE.computedStyles.get(element)\n\t\t}\n\n\t\tconst style = window.getComputedStyle(element)\n\n\t\tif (style) {\n\t\t\tDOM_CACHE.computedStyles.set(element, style)\n\t\t}\n\t\treturn style\n\t}\n\n\t/**\n\t * Gets the cached client rects for an element.\n\t *\n\t * @param {HTMLElement} element - The element to get the client rects for.\n\t * @returns {DOMRectList | null} The cached client rects, or null if the element is not found.\n\t */\n\tfunction getCachedClientRects(element) {\n\t\tif (!element) return null\n\n\t\tif (DOM_CACHE.clientRects.has(element)) {\n\t\t\treturn DOM_CACHE.clientRects.get(element)\n\t\t}\n\n\t\tconst rects = element.getClientRects()\n\n\t\tif (rects) {\n\t\t\tDOM_CACHE.clientRects.set(element, rects)\n\t\t}\n\t\treturn rects\n\t}\n\n\t/**\n\t * Hash map of DOM nodes indexed by their highlight index.\n\t *\n\t * @type {Object<string, any>}\n\t */\n\tconst DOM_HASH_MAP = {}\n\n\tconst ID = { current: 0 }\n\n\tconst HIGHLIGHT_CONTAINER_ID = 'playwright-highlight-container'\n\n\t// Add a WeakMap cache for XPath strings\n\tconst xpathCache = new WeakMap()\n\n\t// // Initialize once and reuse\n\t// const viewportObserver = new IntersectionObserver(\n\t// (entries) => {\n\t// entries.forEach(entry => {\n\t// elementVisibilityMap.set(entry.target, entry.isIntersecting);\n\t// });\n\t// },\n\t// { rootMargin: `${viewportExpansion}px` }\n\t// );\n\n\t/**\n\t * Highlights an element in the DOM and returns the index of the next element.\n\t *\n\t * @param {HTMLElement} element - The element to highlight.\n\t * @param {number} index - The index of the element.\n\t * @param {HTMLElement | null} parentIframe - The parent iframe node.\n\t * @returns {number} The index of the next element.\n\t */\n\tfunction highlightElement(element, index, parentIframe = null) {\n\t\tif (!element) return index\n\n\t\tconst overlays = []\n\t\t/**\n\t\t * @type {HTMLElement | null}\n\t\t */\n\t\tlet label = null\n\t\tlet labelWidth = 20\n\t\tlet labelHeight = 16\n\t\tlet cleanupFn = null\n\n\t\ttry {\n\t\t\t// Create or get highlight container\n\t\t\tlet container = document.getElementById(HIGHLIGHT_CONTAINER_ID)\n\t\t\tif (!container) {\n\t\t\t\tcontainer = document.createElement('div')\n\t\t\t\tcontainer.id = HIGHLIGHT_CONTAINER_ID\n\t\t\t\tcontainer.style.position = 'fixed'\n\t\t\t\tcontainer.style.pointerEvents = 'none'\n\t\t\t\tcontainer.style.top = '0'\n\t\t\t\tcontainer.style.left = '0'\n\t\t\t\tcontainer.style.width = '100%'\n\t\t\t\tcontainer.style.height = '100%'\n\n\t\t\t\t/**\n\t\t\t\t * @edit smaller zIndex for highlight\n\t\t\t\t */\n\t\t\t\tcontainer.style.zIndex = '5000'\n\n\t\t\t\tcontainer.style.backgroundColor = 'transparent'\n\t\t\t\tdocument.body.appendChild(container)\n\t\t\t}\n\n\t\t\t// Get element client rects\n\t\t\tconst rects = element.getClientRects() // Use getClientRects()\n\n\t\t\tif (!rects || rects.length === 0) return index // Exit if no rects\n\n\t\t\t// Generate a color based on the index\n\t\t\tconst colors = [\n\t\t\t\t'#FF0000',\n\t\t\t\t'#00FF00',\n\t\t\t\t'#0000FF',\n\t\t\t\t'#FFA500',\n\t\t\t\t'#800080',\n\t\t\t\t'#008080',\n\t\t\t\t'#FF69B4',\n\t\t\t\t'#4B0082',\n\t\t\t\t'#FF4500',\n\t\t\t\t'#2E8B57',\n\t\t\t\t'#DC143C',\n\t\t\t\t'#4682B4',\n\t\t\t]\n\t\t\tconst colorIndex = index % colors.length\n\t\t\tlet baseColor = colors[colorIndex]\n\n\t\t\t/**\n\t\t\t * @edit adjustable opacity\n\t\t\t */\n\t\t\t// const backgroundColor = baseColor + \"1A\"; // 10% opacity version of the color\n\t\t\tconst backgroundColor =\n\t\t\t\tbaseColor +\n\t\t\t\tMath.floor(highlightOpacity * 255)\n\t\t\t\t\t.toString(16)\n\t\t\t\t\t.padStart(2, '0')\n\t\t\tbaseColor =\n\t\t\t\tbaseColor +\n\t\t\t\tMath.floor(highlightLabelOpacity * 255)\n\t\t\t\t\t.toString(16)\n\t\t\t\t\t.padStart(2, '0')\n\n\t\t\t// Get iframe offset if necessary\n\t\t\tlet iframeOffset = { x: 0, y: 0 }\n\t\t\tif (parentIframe) {\n\t\t\t\tconst iframeRect = parentIframe.getBoundingClientRect() // Keep getBoundingClientRect for iframe offset\n\t\t\t\tiframeOffset.x = iframeRect.left\n\t\t\t\tiframeOffset.y = iframeRect.top\n\t\t\t}\n\n\t\t\t// Create fragment to hold overlay elements\n\t\t\tconst fragment = document.createDocumentFragment()\n\n\t\t\t// Create highlight overlays for each client rect\n\t\t\tfor (const rect of rects) {\n\t\t\t\tif (rect.width === 0 || rect.height === 0) continue // Skip empty rects\n\n\t\t\t\tconst overlay = document.createElement('div')\n\t\t\t\toverlay.style.position = 'fixed'\n\t\t\t\toverlay.style.border = `2px solid ${baseColor}`\n\t\t\t\toverlay.style.backgroundColor = backgroundColor\n\t\t\t\toverlay.style.pointerEvents = 'none'\n\t\t\t\toverlay.style.boxSizing = 'border-box'\n\n\t\t\t\tconst top = rect.top + iframeOffset.y\n\t\t\t\tconst left = rect.left + iframeOffset.x\n\n\t\t\t\toverlay.style.top = `${top}px`\n\t\t\t\toverlay.style.left = `${left}px`\n\t\t\t\toverlay.style.width = `${rect.width}px`\n\t\t\t\toverlay.style.height = `${rect.height}px`\n\n\t\t\t\tfragment.appendChild(overlay)\n\t\t\t\toverlays.push({ element: overlay, initialRect: rect }) // Store overlay and its rect\n\t\t\t}\n\n\t\t\t// Create and position a single label relative to the first rect\n\t\t\tconst firstRect = rects[0]\n\t\t\tlabel = document.createElement('div')\n\t\t\tlabel.className = 'playwright-highlight-label'\n\t\t\tlabel.style.position = 'fixed'\n\t\t\tlabel.style.background = baseColor\n\t\t\tlabel.style.color = 'white'\n\t\t\tlabel.style.padding = '1px 4px'\n\t\t\tlabel.style.borderRadius = '4px'\n\t\t\tlabel.style.fontSize = `${Math.min(12, Math.max(8, firstRect.height / 2))}px`\n\t\t\tlabel.textContent = index.toString()\n\n\t\t\tlabelWidth = label.offsetWidth > 0 ? label.offsetWidth : labelWidth // Update actual width if possible\n\t\t\tlabelHeight = label.offsetHeight > 0 ? label.offsetHeight : labelHeight // Update actual height if possible\n\n\t\t\tconst firstRectTop = firstRect.top + iframeOffset.y\n\t\t\tconst firstRectLeft = firstRect.left + iframeOffset.x\n\n\t\t\tlet labelTop = firstRectTop + 2\n\t\t\tlet labelLeft = firstRectLeft + firstRect.width - labelWidth - 2\n\n\t\t\t// Adjust label position if first rect is too small\n\t\t\tif (firstRect.width < labelWidth + 4 || firstRect.height < labelHeight + 4) {\n\t\t\t\tlabelTop = firstRectTop - labelHeight - 2\n\t\t\t\tlabelLeft = firstRectLeft + firstRect.width - labelWidth // Align with right edge\n\t\t\t\tif (labelLeft < iframeOffset.x) labelLeft = firstRectLeft // Prevent going off-left\n\t\t\t}\n\n\t\t\t// Ensure label stays within viewport bounds slightly better\n\t\t\tlabelTop = Math.max(0, Math.min(labelTop, window.innerHeight - labelHeight))\n\t\t\tlabelLeft = Math.max(0, Math.min(labelLeft, window.innerWidth - labelWidth))\n\n\t\t\tlabel.style.top = `${labelTop}px`\n\t\t\tlabel.style.left = `${labelLeft}px`\n\n\t\t\tfragment.appendChild(label)\n\n\t\t\t// Update positions on scroll/resize\n\t\t\tconst updatePositions = () => {\n\t\t\t\tconst newRects = element.getClientRects() // Get fresh rects\n\t\t\t\tlet newIframeOffset = { x: 0, y: 0 }\n\n\t\t\t\tif (parentIframe) {\n\t\t\t\t\tconst iframeRect = parentIframe.getBoundingClientRect() // Keep getBoundingClientRect for iframe\n\t\t\t\t\tnewIframeOffset.x = iframeRect.left\n\t\t\t\t\tnewIframeOffset.y = iframeRect.top\n\t\t\t\t}\n\n\t\t\t\t// Update each overlay\n\t\t\t\toverlays.forEach((overlayData, i) => {\n\t\t\t\t\tif (i < newRects.length) {\n\t\t\t\t\t\t// Check if rect still exists\n\t\t\t\t\t\tconst newRect = newRects[i]\n\t\t\t\t\t\tconst newTop = newRect.top + newIframeOffset.y\n\t\t\t\t\t\tconst newLeft = newRect.left + newIframeOffset.x\n\n\t\t\t\t\t\toverlayData.element.style.top = `${newTop}px`\n\t\t\t\t\t\toverlayData.element.style.left = `${newLeft}px`\n\t\t\t\t\t\toverlayData.element.style.width = `${newRect.width}px`\n\t\t\t\t\t\toverlayData.element.style.height = `${newRect.height}px`\n\t\t\t\t\t\toverlayData.element.style.display =\n\t\t\t\t\t\t\tnewRect.width === 0 || newRect.height === 0 ? 'none' : 'block'\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If fewer rects now, hide extra overlays\n\t\t\t\t\t\toverlayData.element.style.display = 'none'\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\t// If there are fewer new rects than overlays, hide the extras\n\t\t\t\tif (newRects.length < overlays.length) {\n\t\t\t\t\tfor (let i = newRects.length; i < overlays.length; i++) {\n\t\t\t\t\t\toverlays[i].element.style.display = 'none'\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Update label position based on the first new rect\n\t\t\t\tif (label && newRects.length > 0) {\n\t\t\t\t\tconst firstNewRect = newRects[0]\n\t\t\t\t\tconst firstNewRectTop = firstNewRect.top + newIframeOffset.y\n\t\t\t\t\tconst firstNewRectLeft = firstNewRect.left + newIframeOffset.x\n\n\t\t\t\t\tlet newLabelTop = firstNewRectTop + 2\n\t\t\t\t\tlet newLabelLeft = firstNewRectLeft + firstNewRect.width - labelWidth - 2\n\n\t\t\t\t\tif (firstNewRect.width < labelWidth + 4 || firstNewRect.height < labelHeight + 4) {\n\t\t\t\t\t\tnewLabelTop = firstNewRectTop - labelHeight - 2\n\t\t\t\t\t\tnewLabelLeft = firstNewRectLeft + firstNewRect.width - labelWidth\n\t\t\t\t\t\tif (newLabelLeft < newIframeOffset.x) newLabelLeft = firstNewRectLeft\n\t\t\t\t\t}\n\n\t\t\t\t\t// Ensure label stays within viewport bounds\n\t\t\t\t\tnewLabelTop = Math.max(0, Math.min(newLabelTop, window.innerHeight - labelHeight))\n\t\t\t\t\tnewLabelLeft = Math.max(0, Math.min(newLabelLeft, window.innerWidth - labelWidth))\n\n\t\t\t\t\tlabel.style.top = `${newLabelTop}px`\n\t\t\t\t\tlabel.style.left = `${newLabelLeft}px`\n\t\t\t\t\tlabel.style.display = 'block'\n\t\t\t\t} else if (label) {\n\t\t\t\t\t// Hide label if element has no rects anymore\n\t\t\t\t\tlabel.style.display = 'none'\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst throttleFunction = (func, delay) => {\n\t\t\t\tlet lastCall = 0\n\t\t\t\treturn (...args) => {\n\t\t\t\t\tconst now = performance.now()\n\t\t\t\t\tif (now - lastCall < delay) return\n\t\t\t\t\tlastCall = now\n\t\t\t\t\treturn func(...args)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst throttledUpdatePositions = throttleFunction(updatePositions, 16) // ~60fps\n\t\t\twindow.addEventListener('scroll', throttledUpdatePositions, true)\n\t\t\twindow.addEventListener('resize', throttledUpdatePositions)\n\n\t\t\t// Add cleanup function\n\t\t\tcleanupFn = () => {\n\t\t\t\twindow.removeEventListener('scroll', throttledUpdatePositions, true)\n\t\t\t\twindow.removeEventListener('resize', throttledUpdatePositions)\n\t\t\t\t// Remove overlay elements if needed\n\t\t\t\toverlays.forEach((overlay) => overlay.element.remove())\n\t\t\t\tif (label) label.remove()\n\t\t\t}\n\n\t\t\t// Then add fragment to container in one operation\n\t\t\tcontainer.appendChild(fragment)\n\n\t\t\treturn index + 1\n\t\t} finally {\n\t\t\t// Store cleanup function for later use\n\t\t\tif (cleanupFn) {\n\t\t\t\t// Keep a reference to cleanup functions in a global array\n\t\t\t\t;(window._highlightCleanupFunctions = window._highlightCleanupFunctions || []).push(\n\t\t\t\t\tcleanupFn\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t// // Add this function to perform cleanup when needed\n\t// function cleanupHighlights() {\n\t// if (window._highlightCleanupFunctions && window._highlightCleanupFunctions.length) {\n\t// window._highlightCleanupFunctions.forEach(fn => fn());\n\t// window._highlightCleanupFunctions = [];\n\t// }\n\n\t// // Also remove the container\n\t// const container = document.getElementById(HIGHLIGHT_CONTAINER_ID);\n\t// if (container) container.remove();\n\t// }\n\n\t/**\n\t * Gets the position of an element in its parent.\n\t *\n\t * @param {HTMLElement} currentElement - The element to get the position for.\n\t * @returns {number} The position of the element in its parent.\n\t */\n\tfunction getElementPosition(currentElement) {\n\t\tif (!currentElement.parentElement) {\n\t\t\treturn 0 // No parent means no siblings\n\t\t}\n\n\t\tconst tagName = currentElement.nodeName.toLowerCase()\n\n\t\tconst siblings = Array.from(currentElement.parentElement.children).filter(\n\t\t\t(sib) => sib.nodeName.toLowerCase() === tagName\n\t\t)\n\n\t\tif (siblings.length === 1) {\n\t\t\treturn 0 // Only element of its type\n\t\t}\n\n\t\tconst index = siblings.indexOf(currentElement) + 1 // 1-based index\n\t\treturn index\n\t}\n\n\tfunction getXPathTree(element, stopAtBoundary = true) {\n\t\tif (xpathCache.has(element)) return xpathCache.get(element)\n\n\t\tconst segments = []\n\t\tlet currentElement = element\n\n\t\twhile (currentElement && currentElement.nodeType === Node.ELEMENT_NODE) {\n\t\t\t// Stop if we hit a shadow root or iframe\n\t\t\tif (\n\t\t\t\tstopAtBoundary &&\n\t\t\t\t(currentElement.parentNode instanceof ShadowRoot ||\n\t\t\t\t\tcurrentElement.parentNode instanceof HTMLIFrameElement)\n\t\t\t) {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tconst position = getElementPosition(currentElement)\n\t\t\tconst tagName = currentElement.nodeName.toLowerCase()\n\t\t\tconst xpathIndex = position > 0 ? `[${position}]` : ''\n\t\t\tsegments.unshift(`${tagName}${xpathIndex}`)\n\n\t\t\tcurrentElement = currentElement.parentNode\n\t\t}\n\n\t\tconst result = segments.join('/')\n\t\txpathCache.set(element, result)\n\t\treturn result\n\t}\n\n\t/**\n\t * @edit scrollable element detection\n\t * Checks if an element is scrollable. if so, return the scrollable distance on each direction (left right top bottom). if not return null.\n\t * @note distance smaller than 4 will be considered as not scrollable.\n\t * @note only check block elements, not inline elements.\n\t */\n\tfunction isScrollableElement(element) {\n\t\tif (!element || element.nodeType !== Node.ELEMENT_NODE) {\n\t\t\treturn null // Not a valid element\n\t\t}\n\n\t\tconst style = getCachedComputedStyle(element)\n\t\tif (!style) return null\n\n\t\t// Check if the element is a block-level element\n\t\tconst display = style.display\n\t\tif (display === 'inline' || display === 'inline-block') {\n\t\t\treturn null // Not a block-level element\n\t\t}\n\n\t\t// Check overflow properties\n\t\tconst overflowX = style.overflowX\n\t\tconst overflowY = style.overflowY\n\n\t\t// scrollbar-width/scrollbar-gutter are only set on elements designed to scroll;\n\t\t// their presence signals scroll intent even when overflow is hidden (e.g. overflow: auto on :hover)\n\t\tconst hasScrollbarSignal =\n\t\t\t(style.scrollbarWidth && style.scrollbarWidth !== 'auto') ||\n\t\t\t(style.scrollbarGutter && style.scrollbarGutter !== 'auto')\n\n\t\tconst scrollableX = overflowX === 'auto' || overflowX === 'scroll'\n\t\tconst scrollableY = overflowY === 'auto' || overflowY === 'scroll'\n\n\t\tif (!scrollableX && !scrollableY && !hasScrollbarSignal) {\n\t\t\treturn null // Not scrollable in any direction\n\t\t}\n\n\t\tconst scrollWidth = element.scrollWidth - element.clientWidth\n\t\tconst scrollHeight = element.scrollHeight - element.clientHeight\n\n\t\t// Consider small distances as not scrollable\n\t\tconst threshold = 4\n\n\t\tif (scrollWidth < threshold && scrollHeight < threshold) {\n\t\t\treturn null // Not scrollable\n\t\t}\n\n\t\tif (!scrollableY && !hasScrollbarSignal && scrollWidth < threshold) {\n\t\t\treturn null // Not scrollable horizontally\n\t\t}\n\n\t\tif (!scrollableX && !hasScrollbarSignal && scrollHeight < threshold) {\n\t\t\treturn null // Not scrollable vertically\n\t\t}\n\n\t\tconst distanceToTop = element.scrollTop\n\t\tconst distanceToLeft = element.scrollLeft\n\t\tconst distanceToRight = element.scrollWidth - element.clientWidth - element.scrollLeft\n\t\tconst distanceToBottom = element.scrollHeight - element.clientHeight - element.scrollTop\n\n\t\tconst scrollData = {\n\t\t\ttop: distanceToTop,\n\t\t\tright: distanceToRight,\n\t\t\tbottom: distanceToBottom,\n\t\t\tleft: distanceToLeft,\n\t\t}\n\n\t\t// Store extra data for the element\n\t\taddExtraData(element, {\n\t\t\tscrollable: true,\n\t\t\tscrollData: scrollData,\n\t\t})\n\n\t\treturn scrollData\n\t}\n\n\t/**\n\t * Checks if a text node is visible.\n\t *\n\t * @param {Text} textNode - The text node to check.\n\t * @returns {boolean} Whether the text node is visible.\n\t */\n\tfunction isTextNodeVisible(textNode) {\n\t\ttry {\n\t\t\t// Special case: when viewportExpansion is -1, consider all text nodes as visible\n\t\t\tif (viewportExpansion === -1) {\n\t\t\t\t// Still check parent visibility for basic filtering\n\t\t\t\tconst parentElement = textNode.parentElement\n\t\t\t\tif (!parentElement) return false\n\n\t\t\t\ttry {\n\t\t\t\t\treturn parentElement.checkVisibility({\n\t\t\t\t\t\tcheckOpacity: true,\n\t\t\t\t\t\tcheckVisibilityCSS: true,\n\t\t\t\t\t})\n\t\t\t\t} catch (e) {\n\t\t\t\t\t// Fallback if checkVisibility is not supported\n\t\t\t\t\tconst style = window.getComputedStyle(parentElement)\n\t\t\t\t\treturn style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0'\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst range = document.createRange()\n\t\t\trange.selectNodeContents(textNode)\n\t\t\tconst rects = range.getClientRects() // Use getClientRects for Range\n\n\t\t\tif (!rects || rects.length === 0) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tlet isAnyRectVisible = false\n\t\t\tlet isAnyRectInViewport = false\n\n\t\t\tfor (const rect of rects) {\n\t\t\t\t// Check size\n\t\t\t\tif (rect.width > 0 && rect.height > 0) {\n\t\t\t\t\tisAnyRectVisible = true\n\n\t\t\t\t\t// Viewport check for this rect\n\t\t\t\t\tif (!(\n\t\t\t\t\t\trect.bottom < -viewportExpansion ||\n\t\t\t\t\t\trect.top > window.innerHeight + viewportExpansion ||\n\t\t\t\t\t\trect.right < -viewportExpansion ||\n\t\t\t\t\t\trect.left > window.innerWidth + viewportExpansion\n\t\t\t\t\t)) {\n\t\t\t\t\t\tisAnyRectInViewport = true\n\t\t\t\t\t\tbreak // Found a visible rect in viewport, no need to check others\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!isAnyRectVisible || !isAnyRectInViewport) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check parent visibility\n\t\t\tconst parentElement = textNode.parentElement\n\t\t\tif (!parentElement) return false\n\n\t\t\ttry {\n\t\t\t\treturn parentElement.checkVisibility({\n\t\t\t\t\tcheckOpacity: true,\n\t\t\t\t\tcheckVisibilityCSS: true,\n\t\t\t\t})\n\t\t\t} catch (e) {\n\t\t\t\t// Fallback if checkVisibility is not supported\n\t\t\t\tconst style = window.getComputedStyle(parentElement)\n\t\t\t\treturn style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0'\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.warn('Error checking text node visibility:', e)\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Checks if an element is accepted.\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t * @returns {boolean} Whether the element is accepted.\n\t */\n\tfunction isElementAccepted(element) {\n\t\tif (!element || !element.tagName) return false\n\n\t\t// Always accept body and common container elements\n\t\tconst alwaysAccept = new Set([\n\t\t\t'body',\n\t\t\t'div',\n\t\t\t'main',\n\t\t\t'article',\n\t\t\t'section',\n\t\t\t'nav',\n\t\t\t'header',\n\t\t\t'footer',\n\t\t])\n\t\tconst tagName = element.tagName.toLowerCase()\n\n\t\tif (alwaysAccept.has(tagName)) return true\n\n\t\tconst leafElementDenyList = new Set([\n\t\t\t'svg',\n\t\t\t'script',\n\t\t\t'style',\n\t\t\t'link',\n\t\t\t'meta',\n\t\t\t'noscript',\n\t\t\t'template',\n\t\t])\n\n\t\treturn !leafElementDenyList.has(tagName)\n\t}\n\n\t/**\n\t * Checks if an element is visible.\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t * @returns {boolean} Whether the element is visible.\n\t */\n\tfunction isElementVisible(element) {\n\t\tconst style = getCachedComputedStyle(element)\n\t\treturn (\n\t\t\telement.offsetWidth > 0 &&\n\t\t\telement.offsetHeight > 0 &&\n\t\t\tstyle?.visibility !== 'hidden' &&\n\t\t\tstyle?.display !== 'none'\n\t\t)\n\t}\n\n\t/**\n\t * Checks if an element is interactive.\n\t *\n\t * lots of comments, and uncommented code - to show the logic of what we already tried\n\t *\n\t * One of the things we tried at the beginning was also to use event listeners, and other fancy class, style stuff -> what actually worked best was just combining most things with computed cursor style :)\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t */\n\tfunction isInteractiveElement(element) {\n\t\tif (!element || element.nodeType !== Node.ELEMENT_NODE) {\n\t\t\treturn false\n\t\t}\n\n\t\t/**\n\t\t * @edit add interactiveBlacklist interactiveWhitelist\n\t\t */\n\t\tif (interactiveBlacklist.includes(element)) {\n\t\t\treturn false // Skip blacklisted elements\n\t\t}\n\t\tif (interactiveWhitelist.includes(element)) {\n\t\t\treturn true // Skip whitelisted elements\n\t\t}\n\n\t\t// Cache the tagName and style lookups\n\t\tconst tagName = element.tagName.toLowerCase()\n\t\tconst style = getCachedComputedStyle(element)\n\n\t\t// Define interactive cursors\n\t\tconst interactiveCursors = new Set([\n\t\t\t'pointer', // Link/clickable elements\n\t\t\t'move', // Movable elements\n\t\t\t'text', // Text selection\n\t\t\t'grab', // Grabbable elements\n\t\t\t'grabbing', // Currently grabbing\n\t\t\t'cell', // Table cell selection\n\t\t\t'copy', // Copy operation\n\t\t\t'alias', // Alias creation\n\t\t\t'all-scroll', // Scrollable content\n\t\t\t'col-resize', // Column resize\n\t\t\t'context-menu', // Context menu available\n\t\t\t'crosshair', // Precise selection\n\t\t\t'e-resize', // East resize\n\t\t\t'ew-resize', // East-west resize\n\t\t\t'help', // Help available\n\t\t\t'n-resize', // North resize\n\t\t\t'ne-resize', // Northeast resize\n\t\t\t'nesw-resize', // Northeast-southwest resize\n\t\t\t'ns-resize', // North-south resize\n\t\t\t'nw-resize', // Northwest resize\n\t\t\t'nwse-resize', // Northwest-southeast resize\n\t\t\t'row-resize', // Row resize\n\t\t\t's-resize', // South resize\n\t\t\t'se-resize', // Southeast resize\n\t\t\t'sw-resize', // Southwest resize\n\t\t\t'vertical-text', // Vertical text selection\n\t\t\t'w-resize', // West resize\n\t\t\t'zoom-in', // Zoom in\n\t\t\t'zoom-out', // Zoom out\n\t\t])\n\n\t\t// Define non-interactive cursors\n\t\tconst nonInteractiveCursors = new Set([\n\t\t\t'not-allowed', // Action not allowed\n\t\t\t'no-drop', // Drop not allowed\n\t\t\t'wait', // Processing\n\t\t\t'progress', // In progress\n\t\t\t'initial', // Initial value\n\t\t\t'inherit', // Inherited value\n\t\t\t//? Let's just include all potentially clickable elements that are not specifically blocked\n\t\t\t// 'none', // No cursor\n\t\t\t// 'default', // Default cursor\n\t\t\t// 'auto', // Browser default\n\t\t])\n\n\t\t/**\n\t\t * Checks if an element has an interactive pointer.\n\t\t *\n\t\t * @param {HTMLElement} element - The element to check.\n\t\t * @returns {boolean} Whether the element has an interactive pointer.\n\t\t */\n\t\tfunction doesElementHaveInteractivePointer(element) {\n\t\t\tif (element.tagName.toLowerCase() === 'html') return false\n\n\t\t\tif (style?.cursor && interactiveCursors.has(style.cursor)) return true\n\n\t\t\treturn false\n\t\t}\n\n\t\tlet isInteractiveCursor = doesElementHaveInteractivePointer(element)\n\n\t\t// Genius fix for almost all interactive elements\n\t\tif (isInteractiveCursor) {\n\t\t\treturn true\n\t\t}\n\n\t\tconst interactiveElements = new Set([\n\t\t\t'a', // Links\n\t\t\t'button', // Buttons\n\t\t\t'input', // All input types (text, checkbox, radio, etc.)\n\t\t\t'select', // Dropdown menus\n\t\t\t'textarea', // Text areas\n\t\t\t'details', // Expandable details\n\t\t\t'summary', // Summary element (clickable part of details)\n\t\t\t'label', // Form labels (often clickable)\n\t\t\t'option', // Select options\n\t\t\t'optgroup', // Option groups\n\t\t\t'fieldset', // Form fieldsets (can be interactive with legend)\n\t\t\t'legend', // Fieldset legends\n\t\t])\n\n\t\t// Define explicit disable attributes and properties\n\t\tconst explicitDisableTags = new Set([\n\t\t\t'disabled', // Standard disabled attribute\n\t\t\t// 'aria-disabled', // ARIA disabled state\n\t\t\t'readonly', // Read-only state\n\t\t\t// 'aria-readonly', // ARIA read-only state\n\t\t\t// 'aria-hidden', // Hidden from accessibility\n\t\t\t// 'hidden', // Hidden attribute\n\t\t\t// 'inert', // Inert attribute\n\t\t\t// 'aria-inert', // ARIA inert state\n\t\t\t// 'tabindex=\"-1\"', // Removed from tab order\n\t\t\t// 'aria-hidden=\"true\"' // Hidden from screen readers\n\t\t])\n\n\t\t// handle inputs, select, checkbox, radio, textarea, button and make sure they are not cursor style disabled/not-allowed\n\t\tif (interactiveElements.has(tagName)) {\n\t\t\t// Check for non-interactive cursor\n\t\t\tif (style?.cursor && nonInteractiveCursors.has(style.cursor)) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check for explicit disable attributes\n\t\t\tfor (const disableTag of explicitDisableTags) {\n\t\t\t\tif (\n\t\t\t\t\telement.hasAttribute(disableTag) ||\n\t\t\t\t\telement.getAttribute(disableTag) === 'true' ||\n\t\t\t\t\telement.getAttribute(disableTag) === ''\n\t\t\t\t) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check for disabled property on form elements\n\t\t\tif (element.disabled) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check for readonly property on form elements\n\t\t\tif (element.readOnly) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check for inert property\n\t\t\tif (element.inert) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\treturn true\n\t\t}\n\n\t\tconst role = element.getAttribute('role')\n\t\tconst ariaRole = element.getAttribute('aria-role')\n\n\t\t// Check for contenteditable attribute\n\t\tif (element.getAttribute('contenteditable') === 'true' || element.isContentEditable) {\n\t\t\treturn true\n\t\t}\n\n\t\t// Added enhancement to capture dropdown interactive elements\n\t\tif (\n\t\t\telement.classList &&\n\t\t\t(element.classList.contains('button') ||\n\t\t\t\telement.classList.contains('dropdown-toggle') ||\n\t\t\t\telement.getAttribute('data-index') ||\n\t\t\t\telement.getAttribute('data-toggle') === 'dropdown' ||\n\t\t\t\telement.getAttribute('aria-haspopup') === 'true')\n\t\t) {\n\t\t\treturn true\n\t\t}\n\n\t\tconst interactiveRoles = new Set([\n\t\t\t'button', // Directly clickable element\n\t\t\t// 'link', // Clickable link\n\t\t\t'menu', // Menu container (ARIA menus)\n\t\t\t'menubar', // Menu bar container\n\t\t\t'menuitem', // Clickable menu item\n\t\t\t'menuitemradio', // Radio-style menu item (selectable)\n\t\t\t'menuitemcheckbox', // Checkbox-style menu item (toggleable)\n\t\t\t'radio', // Radio button (selectable)\n\t\t\t'checkbox', // Checkbox (toggleable)\n\t\t\t'tab', // Tab (clickable to switch content)\n\t\t\t'switch', // Toggle switch (clickable to change state)\n\t\t\t'slider', // Slider control (draggable)\n\t\t\t'spinbutton', // Number input with up/down controls\n\t\t\t'combobox', // Dropdown with text input\n\t\t\t'searchbox', // Search input field\n\t\t\t'textbox', // Text input field\n\t\t\t'listbox', // Selectable list\n\t\t\t'option', // Selectable option in a list\n\t\t\t'scrollbar', // Scrollable control\n\t\t])\n\n\t\t// Basic role/attribute checks\n\t\tconst hasInteractiveRole =\n\t\t\tinteractiveElements.has(tagName) ||\n\t\t\t(role && interactiveRoles.has(role)) ||\n\t\t\t(ariaRole && interactiveRoles.has(ariaRole))\n\n\t\tif (hasInteractiveRole) return true\n\n\t\t// check whether element has event listeners by window.getEventListeners\n\t\ttry {\n\t\t\tif (typeof getEventListeners === 'function') {\n\t\t\t\tconst listeners = getEventListeners(element)\n\t\t\t\tconst mouseEvents = ['click', 'mousedown', 'mouseup', 'dblclick']\n\t\t\t\tfor (const eventType of mouseEvents) {\n\t\t\t\t\tif (listeners[eventType] && listeners[eventType].length > 0) {\n\t\t\t\t\t\treturn true // Found a mouse interaction listener\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst getEventListenersForNode =\n\t\t\t\telement?.ownerDocument?.defaultView?.getEventListenersForNode ||\n\t\t\t\twindow.getEventListenersForNode\n\t\t\tif (typeof getEventListenersForNode === 'function') {\n\t\t\t\tconst listeners = getEventListenersForNode(element)\n\t\t\t\tconst interactionEvents = [\n\t\t\t\t\t'click',\n\t\t\t\t\t'mousedown',\n\t\t\t\t\t'mouseup',\n\t\t\t\t\t'keydown',\n\t\t\t\t\t'keyup',\n\t\t\t\t\t'submit',\n\t\t\t\t\t'change',\n\t\t\t\t\t'input',\n\t\t\t\t\t'focus',\n\t\t\t\t\t'blur',\n\t\t\t\t]\n\t\t\t\tfor (const eventType of interactionEvents) {\n\t\t\t\t\tfor (const listener of listeners) {\n\t\t\t\t\t\tif (listener.type === eventType) {\n\t\t\t\t\t\t\treturn true // Found a common interaction listener\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Fallback: Check common event attributes if getEventListeners is not available (getEventListeners doesn't work in page.evaluate context)\n\t\t\tconst commonMouseAttrs = ['onclick', 'onmousedown', 'onmouseup', 'ondblclick']\n\t\t\tfor (const attr of commonMouseAttrs) {\n\t\t\t\tif (element.hasAttribute(attr) || typeof element[attr] === 'function') {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\t// console.warn(`Could not check event listeners for ${element.tagName}:`, e);\n\t\t\t// If checking listeners fails, rely on other checks\n\t\t}\n\n\t\t/**\n\t\t * @edit scrollable element detection\n\t\t */\n\t\tif (isScrollableElement(element)) {\n\t\t\treturn true\n\t\t}\n\n\t\treturn false\n\t}\n\n\t/**\n\t * Checks if an element is the topmost element at its position.\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t * @returns {boolean} Whether the element is the topmost element at its position.\n\t */\n\tfunction isTopElement(element) {\n\t\t// Special case: when viewportExpansion is -1, consider all elements as \"top\" elements\n\t\tif (viewportExpansion === -1) {\n\t\t\treturn true\n\t\t}\n\n\t\tconst rects = getCachedClientRects(element) // Replace element.getClientRects()\n\n\t\tif (!rects || rects.length === 0) {\n\t\t\treturn false // No geometry, cannot be top\n\t\t}\n\n\t\tlet isAnyRectInViewport = false\n\t\tfor (const rect of rects) {\n\t\t\t// Use the same logic as isInExpandedViewport check\n\t\t\tif (\n\t\t\t\trect.width > 0 &&\n\t\t\t\trect.height > 0 &&\n\t\t\t\t!(\n\t\t\t\t\t// Only check non-empty rects\n\t\t\t\t\trect.bottom < -viewportExpansion ||\n\t\t\t\t\trect.top > window.innerHeight + viewportExpansion ||\n\t\t\t\t\trect.right < -viewportExpansion ||\n\t\t\t\t\trect.left > window.innerWidth + viewportExpansion\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tisAnyRectInViewport = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif (!isAnyRectInViewport) {\n\t\t\treturn false // All rects are outside the viewport area\n\t\t}\n\n\t\t// Find the correct document context and root element\n\t\tlet doc = element.ownerDocument\n\n\t\t// If we're in an iframe, elements are considered top by default\n\t\tif (doc !== window.document) {\n\t\t\treturn true\n\t\t}\n\n\t\t/**\n\t\t * @edit improve `sampleRect`, filter out rects with 0 area\n\t\t */\n\t\t// find a rect that has width and height as sample\n\t\tlet rect = Array.from(rects).find((r) => r.width > 0 && r.height > 0)\n\t\tif (!rect) {\n\t\t\treturn false // No valid rect found\n\t\t}\n\n\t\t// For shadow DOM, we need to check within its own root context\n\t\tconst shadowRoot = element.getRootNode()\n\t\tif (shadowRoot instanceof ShadowRoot) {\n\t\t\tconst centerX = rect.left + rect.width / 2\n\t\t\tconst centerY = rect.top + rect.height / 2\n\n\t\t\ttry {\n\t\t\t\tconst topEl = shadowRoot.elementFromPoint(centerX, centerY)\n\t\t\t\tif (!topEl) return false\n\n\t\t\t\tlet current = topEl\n\t\t\t\twhile (current && current !== shadowRoot) {\n\t\t\t\t\tif (current === element) return true\n\t\t\t\t\tcurrent = current.parentElement\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t} catch (e) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\tconst margin = 5\n\n\t\t// For elements in viewport, check if they're topmost. Do the check in the\n\t\t// center of the element and at the corners to ensure we catch more cases.\n\t\tconst checkPoints = [\n\t\t\t// Initially only this was used, but it was not enough\n\t\t\t{ x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 },\n\t\t\t{ x: rect.left + margin, y: rect.top + margin }, // top left\n\t\t\t// { x: rect.right - margin, y: rect.top + margin }, // top right\n\t\t\t// { x: rect.left + margin, y: rect.bottom - margin }, // bottom left\n\t\t\t{ x: rect.right - margin, y: rect.bottom - margin }, // bottom right\n\t\t]\n\n\t\treturn checkPoints.some(({ x, y }) => {\n\t\t\ttry {\n\t\t\t\tconst topEl = document.elementFromPoint(x, y)\n\t\t\t\tif (!topEl) return false\n\n\t\t\t\tlet current = topEl\n\t\t\t\twhile (current && current !== document.documentElement) {\n\t\t\t\t\tif (current === element) return true\n\t\t\t\t\tcurrent = current.parentElement\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t} catch (e) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Checks if an element is within the expanded viewport.\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t * @param {number} viewportExpansion - The viewport expansion.\n\t * @returns {boolean} Whether the element is within the expanded viewport.\n\t */\n\tfunction isInExpandedViewport(element, viewportExpansion) {\n\t\tif (viewportExpansion === -1) {\n\t\t\treturn true\n\t\t}\n\n\t\tconst rects = element.getClientRects() // Use getClientRects\n\n\t\tif (!rects || rects.length === 0) {\n\t\t\t// Fallback to getBoundingClientRect if getClientRects is empty,\n\t\t\t// useful for elements like <svg> that might not have client rects but have a bounding box.\n\t\t\tconst boundingRect = getCachedBoundingRect(element)\n\t\t\tif (!boundingRect || boundingRect.width === 0 || boundingRect.height === 0) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn !(\n\t\t\t\tboundingRect.bottom < -viewportExpansion ||\n\t\t\t\tboundingRect.top > window.innerHeight + viewportExpansion ||\n\t\t\t\tboundingRect.right < -viewportExpansion ||\n\t\t\t\tboundingRect.left > window.innerWidth + viewportExpansion\n\t\t\t)\n\t\t}\n\n\t\t// Check if *any* client rect is within the viewport\n\t\tfor (const rect of rects) {\n\t\t\tif (rect.width === 0 || rect.height === 0) continue // Skip empty rects\n\n\t\t\tif (!(\n\t\t\t\trect.bottom < -viewportExpansion ||\n\t\t\t\trect.top > window.innerHeight + viewportExpansion ||\n\t\t\t\trect.right < -viewportExpansion ||\n\t\t\t\trect.left > window.innerWidth + viewportExpansion\n\t\t\t)) {\n\t\t\t\treturn true // Found at least one rect in the viewport\n\t\t\t}\n\t\t}\n\n\t\treturn false // No rects were found in the viewport\n\t}\n\n\t// /**\n\t// * Gets the effective scroll of an element.\n\t// *\n\t// * @param {HTMLElement} element - The element to get the effective scroll for.\n\t// * @returns {Object} The effective scroll of the element.\n\t// */\n\t// function getEffectiveScroll(element) {\n\t// let currentEl = element;\n\t// let scrollX = 0;\n\t// let scrollY = 0;\n\n\t// while (currentEl && currentEl !== document.documentElement) {\n\t// if (currentEl.scrollLeft || currentEl.scrollTop) {\n\t// scrollX += currentEl.scrollLeft;\n\t// scrollY += currentEl.scrollTop;\n\t// }\n\t// currentEl = currentEl.parentElement;\n\t// }\n\n\t// scrollX += window.scrollX;\n\t// scrollY += window.scrollY;\n\n\t// return { scrollX, scrollY };\n\t// }\n\n\t/**\n\t * Checks if an element is an interactive candidate.\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t * @returns {boolean} Whether the element is an interactive candidate.\n\t */\n\n\t// @edit fix \"aria-*\" attributes check\n\tconst INTERACTIVE_ARIA_ATTRS = [\n\t\t'aria-expanded',\n\t\t'aria-checked',\n\t\t'aria-selected',\n\t\t'aria-pressed',\n\t\t'aria-haspopup',\n\t\t'aria-controls',\n\t\t'aria-owns',\n\t\t'aria-activedescendant',\n\t\t'aria-valuenow',\n\t\t'aria-valuetext',\n\t\t'aria-valuemax',\n\t\t'aria-valuemin',\n\t\t'aria-autocomplete',\n\t]\n\n\tfunction hasInteractiveAria(el) {\n\t\tfor (let i = 0; i < INTERACTIVE_ARIA_ATTRS.length; i++) {\n\t\t\tif (el.hasAttribute(INTERACTIVE_ARIA_ATTRS[i])) return true\n\t\t}\n\t\treturn false\n\t}\n\n\tfunction isInteractiveCandidate(element) {\n\t\tif (!element || element.nodeType !== Node.ELEMENT_NODE) return false\n\n\t\tconst tagName = element.tagName.toLowerCase()\n\n\t\t// Fast-path for common interactive elements\n\t\tconst interactiveElements = new Set([\n\t\t\t'a',\n\t\t\t'button',\n\t\t\t'input',\n\t\t\t'select',\n\t\t\t'textarea',\n\t\t\t'details',\n\t\t\t'summary',\n\t\t\t'label',\n\t\t])\n\n\t\tif (interactiveElements.has(tagName)) return true\n\n\t\t// Quick attribute checks without getting full lists\n\t\tconst hasQuickInteractiveAttr =\n\t\t\telement.hasAttribute('onclick') ||\n\t\t\telement.hasAttribute('role') ||\n\t\t\telement.hasAttribute('tabindex') ||\n\t\t\thasInteractiveAria(element) ||\n\t\t\telement.hasAttribute('data-action') ||\n\t\t\telement.getAttribute('contenteditable') === 'true'\n\n\t\treturn hasQuickInteractiveAttr\n\t}\n\n\t// --- Define constants for distinct interaction check ---\n\tconst DISTINCT_INTERACTIVE_TAGS = new Set([\n\t\t'a',\n\t\t'button',\n\t\t'input',\n\t\t'select',\n\t\t'textarea',\n\t\t'summary',\n\t\t'details',\n\t\t'label',\n\t\t'option',\n\t\t'li',\n\t])\n\tconst DISTINCT_INTERACTIVE_ROLES = new Set([\n\t\t'button',\n\t\t'link',\n\t\t'menuitem',\n\t\t'menuitemradio',\n\t\t'menuitemcheckbox',\n\t\t'radio',\n\t\t'checkbox',\n\t\t'tab',\n\t\t'switch',\n\t\t'slider',\n\t\t'spinbutton',\n\t\t'combobox',\n\t\t'searchbox',\n\t\t'textbox',\n\t\t'listbox',\n\t\t'listitem',\n\t\t'treeitem',\n\t\t'row',\n\t\t'option',\n\t\t'scrollbar',\n\t])\n\n\t/**\n\t * Heuristically determines if an element should be considered as independently interactive,\n\t * even if it's nested inside another interactive container.\n\t *\n\t * This function helps detect deeply nested actionable elements (e.g., menu items within a button)\n\t * that may not be picked up by strict interactivity checks.\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t * @returns {boolean} Whether the element is heuristically interactive.\n\t */\n\tfunction isHeuristicallyInteractive(element) {\n\t\tif (!element || element.nodeType !== Node.ELEMENT_NODE) return false\n\n\t\t// Skip non-visible elements early for performance\n\t\tif (!isElementVisible(element)) return false\n\n\t\t// Check for common attributes that often indicate interactivity\n\t\tconst hasInteractiveAttributes =\n\t\t\telement.hasAttribute('role') ||\n\t\t\telement.hasAttribute('tabindex') ||\n\t\t\telement.hasAttribute('onclick') ||\n\t\t\ttypeof element.onclick === 'function'\n\n\t\t// Check for semantic class names suggesting interactivity\n\t\tconst hasInteractiveClass = /\\b(btn|clickable|menu|item|entry|link)\\b/i.test(\n\t\t\telement.className || ''\n\t\t)\n\n\t\t// Determine whether the element is inside a known interactive container\n\t\tconst isInKnownContainer = Boolean(\n\t\t\telement.closest('button,a,[role=\"button\"],.menu,.dropdown,.list,.toolbar')\n\t\t)\n\n\t\t// Ensure the element has at least one visible child (to avoid marking empty wrappers)\n\t\tconst hasVisibleChildren = [...element.children].some(isElementVisible)\n\n\t\t// Avoid highlighting elements whose parent is <body> (top-level wrappers)\n\t\tconst isParentBody = element.parentElement && element.parentElement.isSameNode(document.body)\n\n\t\treturn (\n\t\t\t(isInteractiveElement(element) || hasInteractiveAttributes || hasInteractiveClass) &&\n\t\t\thasVisibleChildren &&\n\t\t\tisInKnownContainer &&\n\t\t\t!isParentBody\n\t\t)\n\t}\n\n\t/**\n\t * Checks if an element likely represents a distinct interaction\n\t * separate from its parent (if the parent is also interactive).\n\t *\n\t * @param {HTMLElement} element - The element to check.\n\t * @returns {boolean} Whether the element is a distinct interaction.\n\t */\n\tfunction isElementDistinctInteraction(element) {\n\t\tif (!element || element.nodeType !== Node.ELEMENT_NODE) {\n\t\t\treturn false\n\t\t}\n\n\t\tconst tagName = element.tagName.toLowerCase()\n\t\tconst role = element.getAttribute('role')\n\n\t\t// Check if it's an iframe - always distinct boundary\n\t\tif (tagName === 'iframe') {\n\t\t\treturn true\n\t\t}\n\n\t\t// Check tag name\n\t\tif (DISTINCT_INTERACTIVE_TAGS.has(tagName)) {\n\t\t\treturn true\n\t\t}\n\t\t// Check interactive roles\n\t\tif (role && DISTINCT_INTERACTIVE_ROLES.has(role)) {\n\t\t\treturn true\n\t\t}\n\t\t// Check contenteditable\n\t\tif (element.isContentEditable || element.getAttribute('contenteditable') === 'true') {\n\t\t\treturn true\n\t\t}\n\t\t// Check for common testing/automation attributes\n\t\tif (\n\t\t\telement.hasAttribute('data-testid') ||\n\t\t\telement.hasAttribute('data-cy') ||\n\t\t\telement.hasAttribute('data-test')\n\t\t) {\n\t\t\treturn true\n\t\t}\n\t\t// Check for explicit onclick handler (attribute or property)\n\t\tif (element.hasAttribute('onclick') || typeof element.onclick === 'function') {\n\t\t\treturn true\n\t\t}\n\t\t// ARIA state attributes imply the element manages its own interaction state\n\t\tif (hasInteractiveAria(element)) {\n\t\t\treturn true\n\t\t}\n\n\t\t// return false\n\n\t\t// Check for other common interaction event listeners\n\t\ttry {\n\t\t\tconst getEventListenersForNode =\n\t\t\t\telement?.ownerDocument?.defaultView?.getEventListenersForNode ||\n\t\t\t\twindow.getEventListenersForNode\n\t\t\tif (typeof getEventListenersForNode === 'function') {\n\t\t\t\tconst listeners = getEventListenersForNode(element)\n\t\t\t\tconst interactionEvents = [\n\t\t\t\t\t'click',\n\t\t\t\t\t'mousedown',\n\t\t\t\t\t'mouseup',\n\t\t\t\t\t'keydown',\n\t\t\t\t\t'keyup',\n\t\t\t\t\t'submit',\n\t\t\t\t\t'change',\n\t\t\t\t\t'input',\n\t\t\t\t\t'focus',\n\t\t\t\t\t'blur',\n\t\t\t\t]\n\t\t\t\tfor (const eventType of interactionEvents) {\n\t\t\t\t\tfor (const listener of listeners) {\n\t\t\t\t\t\tif (listener.type === eventType) {\n\t\t\t\t\t\t\treturn true // Found a common interaction listener\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Fallback: Check common event attributes if getEventListeners is not available (getEventListenersForNode doesn't work in page.evaluate context)\n\t\t\tconst commonEventAttrs = [\n\t\t\t\t'onmousedown',\n\t\t\t\t'onmouseup',\n\t\t\t\t'onkeydown',\n\t\t\t\t'onkeyup',\n\t\t\t\t'onsubmit',\n\t\t\t\t'onchange',\n\t\t\t\t'oninput',\n\t\t\t\t'onfocus',\n\t\t\t\t'onblur',\n\t\t\t]\n\t\t\tif (commonEventAttrs.some((attr) => element.hasAttribute(attr))) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t} catch (e) {\n\t\t\t// console.warn(`Could not check event listeners for ${element.tagName}:`, e);\n\t\t\t// If checking listeners fails, rely on other checks\n\t\t}\n\n\t\t// if the element is not strictly interactive but appears clickable based on heuristic signals\n\t\tif (isHeuristicallyInteractive(element)) {\n\t\t\treturn true\n\t\t}\n\n\t\t// Scrollable containers are always distinct — the LLM needs their index for targeted scrolling.\n\t\t// Check extraData (already set by isScrollableElement in isInteractiveElement) to avoid redundant layout reads.\n\t\tif (extraData.get(element)?.scrollable) {\n\t\t\treturn true\n\t\t}\n\n\t\t// Default to false: if it's interactive but doesn't match above,\n\t\t// assume it triggers the same action as the parent.\n\t\treturn false\n\t}\n\t// --- End distinct interaction check ---\n\n\t/**\n * Handles the logic for deciding whether to highlight an element and performing the highlight.\n * @param {\n {\n tagName: string;\n attributes: Record<string, string>;\n xpath: any;\n children: never[];\n isVisible?: boolean;\n isTopElement?: boolean;\n isInteractive?: boolean;\n isInViewport?: boolean;\n highlightIndex?: number;\n shadowRoot?: boolean;\n }} nodeData - The node data object.\n * @param {HTMLElement} node - The node to highlight.\n * @param {HTMLElement | null} parentIframe - The parent iframe node.\n * @param {boolean} isParentHighlighted - Whether the parent node is highlighted.\n * @returns {boolean} Whether the element was highlighted.\n */\n\tfunction handleHighlighting(nodeData, node, parentIframe, isParentHighlighted) {\n\t\tif (!nodeData.isInteractive) return false // Not interactive, definitely don't highlight\n\n\t\tlet shouldHighlight = false\n\t\tif (!isParentHighlighted) {\n\t\t\t// Parent wasn't highlighted, this interactive node can be highlighted.\n\t\t\tshouldHighlight = true\n\t\t} else {\n\t\t\t// Parent *was* highlighted. Only highlight this node if it represents a distinct interaction.\n\t\t\tif (isElementDistinctInteraction(node)) {\n\t\t\t\tshouldHighlight = true\n\t\t\t} else {\n\t\t\t\t// console.log(`Skipping highlight for ${nodeData.tagName} (parent highlighted)`);\n\t\t\t\tshouldHighlight = false\n\t\t\t}\n\t\t}\n\n\t\tif (shouldHighlight) {\n\t\t\t// Check viewport status before assigning index and highlighting\n\t\t\tnodeData.isInViewport = isInExpandedViewport(node, viewportExpansion)\n\n\t\t\t// When viewportExpansion is -1, all interactive elements should get a highlight index\n\t\t\t// regardless of viewport status\n\t\t\tif (nodeData.isInViewport || viewportExpansion === -1) {\n\t\t\t\tnodeData.highlightIndex = highlightIndex++\n\n\t\t\t\tif (doHighlightElements) {\n\t\t\t\t\tif (focusHighlightIndex >= 0) {\n\t\t\t\t\t\tif (focusHighlightIndex === nodeData.highlightIndex) {\n\t\t\t\t\t\t\thighlightElement(node, nodeData.highlightIndex, parentIframe)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\thighlightElement(node, nodeData.highlightIndex, parentIframe)\n\t\t\t\t\t}\n\t\t\t\t\treturn true // Successfully highlighted\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// console.log(`Skipping highlight for ${nodeData.tagName} (outside viewport)`);\n\t\t\t}\n\t\t}\n\n\t\treturn false // Did not highlight\n\t}\n\n\t/**\n\t * Creates a node data object for a given node and its descendants.\n\t *\n\t * @param {HTMLElement} node - The node to process.\n\t * @param {HTMLElement | null} parentIframe - The parent iframe node.\n\t * @param {boolean} isParentHighlighted - Whether the parent node is highlighted.\n\t * @returns {string | null} The ID of the node data object, or null if the node is not processed.\n\t */\n\tfunction buildDomTree(node, parentIframe = null, isParentHighlighted = false) {\n\t\t// Fast rejection checks first\n\t\tif (\n\t\t\t!node ||\n\t\t\tnode.id === HIGHLIGHT_CONTAINER_ID ||\n\t\t\t(node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE)\n\t\t) {\n\t\t\treturn null\n\t\t}\n\n\t\tif (!node || node.id === HIGHLIGHT_CONTAINER_ID) {\n\t\t\treturn null\n\t\t}\n\n\t\t/**\n\t\t * @edit add `data-browser-use-ignore` attribute\n\t\t */\n\t\tif (node.dataset?.browserUseIgnore === 'true' || node.dataset?.pageAgentIgnore === 'true') {\n\t\t\treturn null // Skip this node and its children\n\t\t}\n\n\t\t/**\n\t\t * @edit exclude aria-hidden elements\n\t\t */\n\t\tif (node.getAttribute && node.getAttribute('aria-hidden') === 'true') {\n\t\t\treturn null // Skip this node and its children\n\t\t}\n\n\t\t// Special handling for root node (body)\n\t\tif (node === document.body) {\n\t\t\tconst nodeData = {\n\t\t\t\ttagName: 'body',\n\t\t\t\tattributes: {},\n\t\t\t\txpath: '/body',\n\t\t\t\tchildren: [],\n\t\t\t}\n\n\t\t\t// Process children of body\n\t\t\tfor (const child of node.childNodes) {\n\t\t\t\tconst domElement = buildDomTree(child, parentIframe, false) // Body's children have no highlighted parent initially\n\t\t\t\tif (domElement) nodeData.children.push(domElement)\n\t\t\t}\n\n\t\t\tconst id = `${ID.current++}`\n\t\t\tDOM_HASH_MAP[id] = nodeData\n\t\t\treturn id\n\t\t}\n\n\t\t// Early bailout for non-element nodes except text\n\t\tif (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE) {\n\t\t\treturn null\n\t\t}\n\n\t\t// Process text nodes\n\t\tif (node.nodeType === Node.TEXT_NODE) {\n\t\t\tconst textContent = node.textContent?.trim()\n\t\t\tif (!textContent) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\t// Only check visibility for text nodes that might be visible\n\t\t\tconst parentElement = node.parentElement\n\t\t\tif (!parentElement || parentElement.tagName.toLowerCase() === 'script') {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tconst id = `${ID.current++}`\n\t\t\tDOM_HASH_MAP[id] = {\n\t\t\t\ttype: 'TEXT_NODE',\n\t\t\t\ttext: textContent,\n\t\t\t\tisVisible: isTextNodeVisible(node),\n\t\t\t}\n\t\t\treturn id\n\t\t}\n\n\t\t// Quick checks for element nodes\n\t\tif (node.nodeType === Node.ELEMENT_NODE && !isElementAccepted(node)) {\n\t\t\treturn null\n\t\t}\n\n\t\t// Early viewport check - only filter out elements clearly outside viewport\n\t\t// The getBoundingClientRect() of the Shadow DOM host element may return width/height = 0\n\t\tif (viewportExpansion !== -1 && !node.shadowRoot) {\n\t\t\tconst rect = getCachedBoundingRect(node) // Keep for initial quick check\n\t\t\tconst style = getCachedComputedStyle(node)\n\n\t\t\t// Skip viewport check for fixed/sticky elements as they may appear anywhere\n\t\t\tconst isFixedOrSticky = style && (style.position === 'fixed' || style.position === 'sticky')\n\n\t\t\t// Check if element has actual dimensions using offsetWidth/Height (quick check)\n\t\t\tconst hasSize = node.offsetWidth > 0 || node.offsetHeight > 0\n\n\t\t\t// Use getBoundingClientRect for the quick OUTSIDE check.\n\t\t\t// isInExpandedViewport will do the more accurate check later if needed.\n\t\t\tif (\n\t\t\t\t!rect ||\n\t\t\t\t(!isFixedOrSticky &&\n\t\t\t\t\t!hasSize &&\n\t\t\t\t\t(rect.bottom < -viewportExpansion ||\n\t\t\t\t\t\trect.top > window.innerHeight + viewportExpansion ||\n\t\t\t\t\t\trect.right < -viewportExpansion ||\n\t\t\t\t\t\trect.left > window.innerWidth + viewportExpansion))\n\t\t\t) {\n\t\t\t\t// console.log(\"Skipping node outside viewport (quick check):\", node.tagName, rect);\n\t\t\t\treturn null\n\t\t\t}\n\t\t}\n\n\t\t/**\n * @type {\n {\n tagName: string;\n attributes: Record<string, string | null>;\n xpath: any;\n children: never[];\n isVisible?: boolean;\n isTopElement?: boolean;\n isInteractive?: boolean;\n isInViewport?: boolean;\n highlightIndex?: number;\n shadowRoot?: boolean;\n }\n } nodeData - The node data object.\n */\n\t\tconst nodeData = {\n\t\t\ttagName: node.tagName.toLowerCase(),\n\t\t\tattributes: {},\n\n\t\t\t/**\n\t\t\t * @edit no need for xpath\n\t\t\t */\n\t\t\t// xpath: getXPathTree(node, true),\n\n\t\t\tchildren: [],\n\t\t}\n\n\t\t// Get attributes for interactive elements or potential text containers\n\t\tif (\n\t\t\tisInteractiveCandidate(node) ||\n\t\t\tnode.tagName.toLowerCase() === 'iframe' ||\n\t\t\tnode.tagName.toLowerCase() === 'body'\n\t\t) {\n\t\t\tconst attributeNames = node.getAttributeNames?.() || []\n\t\t\tfor (const name of attributeNames) {\n\t\t\t\tconst value = node.getAttribute(name)\n\t\t\t\tnodeData.attributes[name] = value\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @edit @workaround input.checked\n\t\t\t */\n\t\t\tif (\n\t\t\t\tnode.tagName.toLowerCase() === 'input' &&\n\t\t\t\t(node.type === 'checkbox' || node.type === 'radio')\n\t\t\t) {\n\t\t\t\tnodeData.attributes.checked = node.checked ? 'true' : 'false' // Store as string for consistency\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @edit @workaround select.value\n\t\t\t * select 的选中状态是 DOM 属性(selectedIndex/value),不会反映到 HTML attribute 上,\n\t\t\t * 若不同步,选择后重新快照时 LLM 看不到变化,会误判操作未生效而反复执行 select 操作。\n\t\t\t */\n\t\t\tif (node.tagName.toLowerCase() === 'select') {\n\t\t\t\tconst selectedOption = node.options[node.selectedIndex]\n\t\t\t\tnodeData.attributes.value = selectedOption ? selectedOption.text : node.value\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @edit @workaround label-wrapped checkbox/radio checked\n\t\t\t * 常见组件库(如 antd)把 checkbox/radio 包在 <label> 里,视觉上的选中框覆盖在原生\n\t\t\t * input 上面,导致 input 不是 isTopElement,不会被收进可交互树、也就打印不出 checked\n\t\t\t * 状态。真正可点击并带 highlightIndex 的是这个 <label>,所以把关联 input 的 checked\n\t\t\t * 状态同步到 label 自己的 attributes 上,否则 LLM 看不到勾选反馈会反复点击。\n\t\t\t */\n\t\t\tif (node.tagName.toLowerCase() === 'label') {\n\t\t\t\tconst forId = node.getAttribute('for')\n\t\t\t\tconst input =\n\t\t\t\t\tnode.querySelector('input[type=\"checkbox\"], input[type=\"radio\"]') ||\n\t\t\t\t\t(forId && document.getElementById(forId))\n\t\t\t\tif (input && (input.type === 'checkbox' || input.type === 'radio')) {\n\t\t\t\t\tnodeData.attributes.checked = input.checked ? 'true' : 'false'\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet nodeWasHighlighted = false\n\t\t// Perform visibility, interactivity, and highlighting checks\n\t\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\t\tnodeData.isVisible = isElementVisible(node) // isElementVisible uses offsetWidth/Height, which is fine\n\t\t\tif (nodeData.isVisible) {\n\t\t\t\tnodeData.isTopElement = isTopElement(node)\n\n\t\t\t\t// Special handling for ARIA menu containers - check interactivity even if not top element\n\t\t\t\tconst role = node.getAttribute('role')\n\t\t\t\tconst isMenuContainer = role === 'menu' || role === 'menubar' || role === 'listbox'\n\n\t\t\t\tif (nodeData.isTopElement || isMenuContainer) {\n\t\t\t\t\tnodeData.isInteractive = isInteractiveElement(node)\n\t\t\t\t\t// Call the dedicated highlighting function\n\t\t\t\t\tnodeWasHighlighted = handleHighlighting(nodeData, node, parentIframe, isParentHighlighted)\n\n\t\t\t\t\t/**\n\t\t\t\t\t * @edit direct dom ref\n\t\t\t\t\t */\n\t\t\t\t\tnodeData.ref = node\n\n\t\t\t\t\t/**\n\t\t\t\t\t * @edit make sure attributes exist for interactive candidates.\n\t\t\t\t\t * @note if the element failed the isInteractiveCandidate, attributes would be empty.\n\t\t\t\t\t */\n\t\t\t\t\tif (nodeData.isInteractive && Object.keys(nodeData.attributes).length === 0) {\n\t\t\t\t\t\tconst attributeNames = node.getAttributeNames?.() || []\n\t\t\t\t\t\tfor (const name of attributeNames) {\n\t\t\t\t\t\t\tconst value = node.getAttribute(name)\n\t\t\t\t\t\t\tnodeData.attributes[name] = value\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Process children, with special handling for iframes and rich text editors\n\t\tif (node.tagName) {\n\t\t\tconst tagName = node.tagName.toLowerCase()\n\n\t\t\t// Handle iframes\n\t\t\tif (tagName === 'iframe') {\n\t\t\t\ttry {\n\t\t\t\t\tconst iframeDoc = node.contentDocument || node.contentWindow?.document\n\t\t\t\t\tif (iframeDoc) {\n\t\t\t\t\t\tfor (const child of iframeDoc.childNodes) {\n\t\t\t\t\t\t\tconst domElement = buildDomTree(child, node, false)\n\t\t\t\t\t\t\tif (domElement) nodeData.children.push(domElement)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconsole.warn('Unable to access iframe:', e)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Handle rich text editors and contenteditable elements\n\t\t\telse if (\n\t\t\t\tnode.isContentEditable ||\n\t\t\t\tnode.getAttribute('contenteditable') === 'true' ||\n\t\t\t\tnode.id === 'tinymce' ||\n\t\t\t\tnode.classList.contains('mce-content-body') ||\n\t\t\t\t(tagName === 'body' && node.getAttribute('data-id')?.startsWith('mce_'))\n\t\t\t) {\n\t\t\t\t// Process all child nodes to capture formatted text\n\t\t\t\tfor (const child of node.childNodes) {\n\t\t\t\t\tconst domElement = buildDomTree(child, parentIframe, nodeWasHighlighted)\n\t\t\t\t\tif (domElement) nodeData.children.push(domElement)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Handle shadow DOM\n\t\t\t\tif (node.shadowRoot) {\n\t\t\t\t\tnodeData.shadowRoot = true\n\t\t\t\t\tfor (const child of node.shadowRoot.childNodes) {\n\t\t\t\t\t\tconst domElement = buildDomTree(child, parentIframe, nodeWasHighlighted)\n\t\t\t\t\t\tif (domElement) nodeData.children.push(domElement)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Handle regular elements\n\t\t\t\tfor (const child of node.childNodes) {\n\t\t\t\t\t// Pass the highlighted status of the *current* node to its children\n\t\t\t\t\tconst passHighlightStatusToChild = nodeWasHighlighted || isParentHighlighted\n\t\t\t\t\tconst domElement = buildDomTree(child, parentIframe, passHighlightStatusToChild)\n\t\t\t\t\tif (domElement) nodeData.children.push(domElement)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Skip empty anchor tags only if they have no dimensions and no children\n\t\tif (nodeData.tagName === 'a' && nodeData.children.length === 0 && !nodeData.attributes.href) {\n\t\t\t// Check if the anchor has actual dimensions\n\t\t\tconst rect = getCachedBoundingRect(node)\n\t\t\tconst hasSize =\n\t\t\t\t(rect && rect.width > 0 && rect.height > 0) || node.offsetWidth > 0 || node.offsetHeight > 0\n\n\t\t\tif (!hasSize) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * @edit add `extra` field for extra data\n\t\t */\n\t\tnodeData.extra = extraData.get(node) || null\n\n\t\tconst id = `${ID.current++}`\n\t\tDOM_HASH_MAP[id] = nodeData\n\t\treturn id\n\t}\n\n\tconst rootId = buildDomTree(document.body)\n\n\t// Clear the cache before starting\n\tDOM_CACHE.clearCache()\n\n\treturn { rootId, map: DOM_HASH_MAP }\n}\n","import domTree from './dom_tree/index.js'\nimport {\n\tElementDomNode,\n\tFlatDomTree,\n\tInteractiveElementDomNode,\n\tTextDomNode,\n} from './dom_tree/type'\n\n/**\n * 供 LLM 使用的结构化浏览器状态\n */\nexport interface BrowserState {\n\turl: string\n\ttitle: string\n\t/** 页面信息 + 滚动位置提示(例如 \"Page info: 1920x1080px...\\n[Start of page]\") */\n\theader: string\n\t/** 可交互元素的简化 HTML */\n\tcontent: string\n\t/** 页面底部提示(例如 \"... 300 pixels below ...\" 或 \"[End of page]\") */\n\tfooter: string\n}\n\n/**\n * DOM 树提取时的视口扩展。\n * -1 表示整页(不受视口限制)\n * 0 表示仅视口\n * 正数表示在视口基础上额外扩展的像素数\n *\n * @note 由于 isTopElement 依赖 elementFromPoint,\n * 当元素在视口外时会返回 null,该特性实际用处不大,仅在 -1 和 0 之间有区别\n */\nconst DEFAULT_VIEWPORT_EXPANSION = -1\n\n// 解析视口扩展参数,未传入时使用默认值(-1 表示整页)\nexport function resolveViewportExpansion(viewportExpansion?: number): number {\n\treturn viewportExpansion ?? DEFAULT_VIEWPORT_EXPANSION\n}\n\nexport interface DomConfig {\n\tviewportExpansion?: number // 视口扩展像素数,-1 表示整页\n\tinteractiveBlacklist?: (Element | (() => Element))[] // 不视为可交互的元素黑名单\n\tinteractiveWhitelist?: (Element | (() => Element))[] // 强制视为可交互的元素白名单\n\tincludeAttributes?: string[] // 输出文本时需要包含的额外属性名\n\thighlightOpacity?: number // 高亮边框透明度\n\thighlightLabelOpacity?: number // 高亮序号标签透明度\n\n\t/**\n\t * 即使不可交互,也保留脱水输出中的语义化地标标签\n\t * @note 结合页面滚动时可能让 LLM 感到困惑,请谨慎使用\n\t **/\n\tkeepSemanticTags?: boolean\n}\n\n// TODO: 对应的角色\n// 语义化标签集合:即使不可交互,输出时也会保留这些标签以提供上下文\nconst SEMANTIC_TAGS = new Set([\n\t'nav',\n\t'menu',\n\t// 'main',\n\t'header',\n\t'footer',\n\t'aside',\n\t// 'article',\n\t// 'form',\n\t'dialog',\n])\n\n/**\n * 用于检测可交互元素是否是新出现的。\n */\nconst newElementsCache = new WeakMap<HTMLElement, string>()\n\n// 生成扁平化 DOM 树(FlatDomTree),包含可交互元素标记、高亮编号及\"新出现\"标记\nexport function getFlatTree(config: DomConfig): FlatDomTree {\n\t// 解析视口扩展参数\n\tconst viewportExpansion = resolveViewportExpansion(config.viewportExpansion)\n\n\t// 解析黑名单:函数形式会在调用时取回实际元素\n\tconst interactiveBlacklist = [] as Element[]\n\tfor (const item of config.interactiveBlacklist || []) {\n\t\tif (typeof item === 'function') {\n\t\t\tinteractiveBlacklist.push(item())\n\t\t} else {\n\t\t\tinteractiveBlacklist.push(item)\n\t\t}\n\t}\n\n\t// 解析白名单:函数形式会在调用时取回实际元素\n\tconst interactiveWhitelist = [] as Element[]\n\tfor (const item of config.interactiveWhitelist || []) {\n\t\tif (typeof item === 'function') {\n\t\t\tinteractiveWhitelist.push(item())\n\t\t} else {\n\t\t\tinteractiveWhitelist.push(item)\n\t\t}\n\t}\n\n\tconst elements = domTree({\n\t\tdoHighlightElements: true, // 是否绘制高亮边框\n\t\tdebugMode: true, // 是否开启调试模式\n\t\tfocusHighlightIndex: -1, // 聚焦的高亮序号,-1 表示不聚焦\n\t\tviewportExpansion, // 视口扩展像素数\n\t\tinteractiveBlacklist, // 可交互元素黑名单\n\t\tinteractiveWhitelist, // 可交互元素白名单\n\t\thighlightOpacity: config.highlightOpacity ?? 0.0, // 高亮边框透明度\n\t\thighlightLabelOpacity: config.highlightLabelOpacity ?? 0.1, // 高亮序号标签透明度\n\t}) as FlatDomTree\n\n\t// 当前页面 URL,用于标记新出现元素\n\tconst currentUrl = window.location.href\n\n\t/**\n\t * 标记新出现的元素\n\t * @todo browser-use 使用 hash(位置,属性等信息) 来判断是否同一个元素,\n\t * 能够解决 1. 元素被删除后重新添加 2. 页面卸载 等问题。\n\t * 这里先简单做.\n\t */\n\tfor (const nodeId in elements.map) {\n\t\tconst node = elements.map[nodeId]\n\t\tif (node.isInteractive && node.ref) {\n\t\t\tconst ref = node.ref as HTMLElement\n\t\t\t// @note 这样太严格,元素是可以跨页面存在的\n\t\t\t// if (newElementsCache.get(ref) !== currentUrl) {\n\t\t\tif (!newElementsCache.has(ref)) {\n\t\t\t\tnewElementsCache.set(ref, currentUrl)\n\t\t\t\tnode.isNew = true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn elements\n}\n\n// glob 模式(含 * 通配符)到正则表达式的缓存,避免重复编译\nconst globRegexCache = new Map<string, RegExp>()\n\n// 将 glob 模式(* 通配符)转换为正则表达式,并缓存结果\nfunction globToRegex(pattern: string): RegExp {\n\tlet regex = globRegexCache.get(pattern)\n\tif (!regex) {\n\t\tconst escaped = pattern.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n\t\tregex = new RegExp(`^${escaped.replace(/\\*/g, '.*')}$`)\n\t\tglobRegexCache.set(pattern, regex)\n\t}\n\treturn regex\n}\n\n// 根据属性名模式列表,从元素属性中筛选出需要保留的属性(支持 * 通配符,并去掉空值)\nfunction matchAttributes(\n\tattrs: Record<string, string>,\n\tpatterns: string[]\n): Record<string, string> {\n\tconst result: Record<string, string> = {}\n\n\tfor (const pattern of patterns) {\n\t\tif (pattern.includes('*')) {\n\t\t\t// 含通配符:对每个属性名做正则匹配\n\t\t\tconst regex = globToRegex(pattern)\n\t\t\tfor (const key of Object.keys(attrs)) {\n\t\t\t\tif (regex.test(key) && attrs[key].trim()) {\n\t\t\t\t\tresult[key] = attrs[key].trim()\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// 精确属性名匹配\n\t\t\tconst value = attrs[pattern]\n\t\t\tif (value && value.trim()) {\n\t\t\t\tresult[pattern] = value.trim()\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result\n}\n\n/**\n * elementsToString 内部使用的类型\n */\ninterface TreeNode {\n\ttype: 'text' | 'element' // 节点类型:文本节点或元素节点\n\tparent: TreeNode | null // 父节点\n\tchildren: TreeNode[] // 子节点列表\n\tisVisible: boolean // 是否可见\n\t// 文本节点属性\n\ttext?: string // 文本节点内容\n\t// 元素节点属性\n\ttagName?: string // 元素标签名\n\tattributes?: Record<string, string> // 元素属性集合\n\tisInteractive?: boolean // 是否可交互\n\tisTopElement?: boolean // 是否为最上层元素\n\tisNew?: boolean // 是否为本次新出现的元素\n\thighlightIndex?: number // 高亮序号(可交互元素)\n\textra?: Record<string, any> // 附加信息(如滚动数据)\n}\n\n/**\n * 对应 python 中的 views::clickable_elements_to_string,\n * 将 dom 信息处理成适合 llm 阅读的文本格式\n * @形如\n * ``` text\n * [0]<a aria-label=page-agent.js 首页 />\n * [1]<div >P />\n * [2]<div >page-agent.js\n * UI Agent in your webpage />\n * [3]<a >文档 />\n * [4]<a aria-label=查看源码(在新窗口打开)>源码 />\n * UI Agent in your webpage\n * 用户输入需求,AI 理解页面并自动操作。\n * [5]<a role=button>快速开始 />\n * [6]<a role=button>查看文档 />\n * 无需后端\n * ```\n * 其中可交互元素用序号标出,提示llm可以用序号操作。\n * 缩进代表父子关系。\n * 普通文本则直接列出来。\n *\n * @todo 数据脱敏过滤器\n */\nexport function flatTreeToString(\n\tflatTree: FlatDomTree,\n\tincludeAttributes: string[] = [],\n\tkeepSemanticTags = false\n): string {\n\t// 默认需要在输出中保留的属性名列表\n\tconst DEFAULT_INCLUDE_ATTRIBUTES = [\n\t\t'title',\n\t\t'type',\n\t\t'checked',\n\t\t'name',\n\t\t'role',\n\t\t'value',\n\t\t'placeholder',\n\t\t'data-date-format',\n\t\t'alt',\n\t\t'aria-label',\n\t\t'aria-expanded',\n\t\t'data-state',\n\t\t'aria-checked',\n\n\t\t// @edit 为更好地处理表单而新增\n\t\t'id',\n\t\t'for',\n\n\t\t// 用于跳转检查\n\t\t'target',\n\n\t\t// 绝对定位的下拉菜单\n\t\t'aria-haspopup',\n\t\t'aria-controls',\n\t\t'aria-owns',\n\n\t\t// 内容可编辑\n\t\t'contenteditable',\n\t]\n\n\t// 合并用户传入的属性与默认属性列表\n\tconst includeAttrs = [...includeAttributes, ...DEFAULT_INCLUDE_ATTRIBUTES]\n\n\t// 辅助函数:截断文本长度\n\tconst capTextLength = (text: string, maxLength: number): string => {\n\t\tif (text.length > maxLength) {\n\t\t\treturn text.substring(0, maxLength) + '...'\n\t\t}\n\t\treturn text\n\t}\n\n\t// 根据扁平 map 构建树结构\n\tconst buildTreeNode = (nodeId: string): TreeNode | null => {\n\t\tconst node = flatTree.map[nodeId]\n\t\tif (!node) return null\n\n\t\tif (node.type === 'TEXT_NODE') {\n\t\t\tconst textNode = node as TextDomNode\n\t\t\treturn {\n\t\t\t\ttype: 'text',\n\t\t\t\ttext: textNode.text,\n\t\t\t\tisVisible: textNode.isVisible,\n\t\t\t\tparent: null,\n\t\t\t\tchildren: [],\n\t\t\t}\n\t\t} else {\n\t\t\tconst elementNode = node as ElementDomNode\n\t\t\tconst children: TreeNode[] = []\n\n\t\t\tif (elementNode.children) {\n\t\t\t\tfor (const childId of elementNode.children) {\n\t\t\t\t\tconst child = buildTreeNode(childId)\n\t\t\t\t\tif (child) {\n\t\t\t\t\t\tchild.parent = null // 稍后设置\n\t\t\t\t\t\tchildren.push(child)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttype: 'element',\n\t\t\t\ttagName: elementNode.tagName,\n\t\t\t\tattributes: elementNode.attributes ?? {},\n\t\t\t\tisVisible: elementNode.isVisible ?? false,\n\t\t\t\tisInteractive: elementNode.isInteractive ?? false,\n\t\t\t\tisTopElement: elementNode.isTopElement ?? false,\n\t\t\t\tisNew: elementNode.isNew ?? false,\n\t\t\t\thighlightIndex: elementNode.highlightIndex,\n\t\t\t\tparent: null,\n\t\t\t\tchildren,\n\t\t\t\textra: elementNode.extra ?? {},\n\t\t\t}\n\t\t}\n\t}\n\n\t// 设置父节点引用\n\tconst setParentReferences = (node: TreeNode, parent: TreeNode | null = null) => {\n\t\tnode.parent = parent\n\t\tfor (const child of node.children) {\n\t\t\tsetParentReferences(child, node)\n\t\t}\n\t}\n\n\t// 构建根节点\n\tconst rootNode = buildTreeNode(flatTree.rootId)\n\tif (!rootNode) return ''\n\n\tsetParentReferences(rootNode)\n\n\t// 辅助函数:检查文本节点的父节点是否带高亮序号\n\tconst hasParentWithHighlightIndex = (node: TreeNode): boolean => {\n\t\tlet current = node.parent\n\t\twhile (current) {\n\t\t\tif (current.type === 'element' && current.highlightIndex !== undefined) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tcurrent = current.parent\n\t\t}\n\t\treturn false\n\t}\n\n\t// 辅助函数:检查父节点是否为最上层元素\n\t// const isParentTopElement = (node: TreeNode): boolean => {\n\t// \treturn node.parent?.type === 'element' && node.parent.isTopElement === true\n\t// }\n\n\t// 主处理函数\n\tconst processNode = (node: TreeNode, depth: number, result: string[]): void => {\n\t\tlet nextDepth = depth\n\t\tconst depthStr = '\\t'.repeat(depth)\n\n\t\tif (node.type === 'element') {\n\t\t\tconst isSemantic = keepSemanticTags && node.tagName && SEMANTIC_TAGS.has(node.tagName)\n\n\t\t\t// 带高亮序号的元素\n\t\t\tif (node.highlightIndex !== undefined) {\n\t\t\t\tnextDepth += 1\n\n\t\t\t\tconst text = getAllTextTillNextClickableElement(node)\n\t\t\t\tlet attributesHtmlStr = ''\n\n\t\t\t\tif (includeAttrs.length > 0 && node.attributes) {\n\t\t\t\t\tconst attributesToInclude = matchAttributes(node.attributes, includeAttrs)\n\n\t\t\t\t\t// 去除重复值(针对长度超过 5 个字符的属性)\n\t\t\t\t\tconst keys = Object.keys(attributesToInclude)\n\t\t\t\t\tif (keys.length > 1) {\n\t\t\t\t\t\tconst keysToRemove = new Set<string>()\n\t\t\t\t\t\tconst seenValues: Record<string, string> = {}\n\n\t\t\t\t\t\tfor (const key of keys) {\n\t\t\t\t\t\t\tconst value = attributesToInclude[key]\n\t\t\t\t\t\t\tif (value.length > 5) {\n\t\t\t\t\t\t\t\tif (value in seenValues) {\n\t\t\t\t\t\t\t\t\tkeysToRemove.add(key)\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tseenValues[value] = key\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (const key of keysToRemove) {\n\t\t\t\t\t\t\tdelete attributesToInclude[key]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// 如果 role 与标签名一致则移除\n\t\t\t\t\tif (attributesToInclude.role === node.tagName) {\n\t\t\t\t\t\tdelete attributesToInclude.role\n\t\t\t\t\t}\n\n\t\t\t\t\t// 移除与文本内容重复的属性\n\t\t\t\t\tconst attrsToRemoveIfTextMatches = ['aria-label', 'placeholder', 'title']\n\t\t\t\t\tfor (const attr of attrsToRemoveIfTextMatches) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tattributesToInclude[attr] &&\n\t\t\t\t\t\t\tattributesToInclude[attr].toLowerCase().trim() === text.toLowerCase().trim()\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tdelete attributesToInclude[attr]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Object.keys(attributesToInclude).length > 0) {\n\t\t\t\t\t\tattributesHtmlStr = Object.entries(attributesToInclude)\n\t\t\t\t\t\t\t.map(([key, value]) => `${key}=${capTextLength(value, 20)}`)\n\t\t\t\t\t\t\t.join(' ')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// 构建该行\n\t\t\t\tconst highlightIndicator = node.isNew\n\t\t\t\t\t? `*[${node.highlightIndex}]`\n\t\t\t\t\t: `[${node.highlightIndex}]`\n\t\t\t\tlet line = `${depthStr}${highlightIndicator}<${node.tagName ?? ''}`\n\n\t\t\t\tif (attributesHtmlStr) {\n\t\t\t\t\tline += ` ${attributesHtmlStr}`\n\t\t\t\t}\n\n\t\t\t\t/**\n\t\t\t\t * @edit scrollable 数据\n\t\t\t\t */\n\t\t\t\tif (node.extra) {\n\t\t\t\t\tif (node.extra.scrollable) {\n\t\t\t\t\t\tlet scrollDataText = ''\n\t\t\t\t\t\tif (node.extra.scrollData?.left)\n\t\t\t\t\t\t\tscrollDataText += `left=${node.extra.scrollData.left}, `\n\t\t\t\t\t\tif (node.extra.scrollData?.top) scrollDataText += `top=${node.extra.scrollData.top}, `\n\t\t\t\t\t\tif (node.extra.scrollData?.right)\n\t\t\t\t\t\t\tscrollDataText += `right=${node.extra.scrollData.right}, `\n\t\t\t\t\t\tif (node.extra.scrollData?.bottom)\n\t\t\t\t\t\t\tscrollDataText += `bottom=${node.extra.scrollData.bottom}`\n\n\t\t\t\t\t\tline += ` data-scrollable=\"${scrollDataText}\"`\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (text) {\n\t\t\t\t\tconst trimmedText = text.trim()\n\t\t\t\t\tif (!attributesHtmlStr) {\n\t\t\t\t\t\tline += ' '\n\t\t\t\t\t}\n\t\t\t\t\tline += `>${trimmedText}`\n\t\t\t\t} else if (!attributesHtmlStr) {\n\t\t\t\t\tline += ' '\n\t\t\t\t}\n\n\t\t\t\tline += ' />'\n\t\t\t\tresult.push(line)\n\t\t\t}\n\n\t\t\t// 语义化标签的特殊处理:\n\t\t\t// 即使不可交互,也可以保留它们以提供清晰的上下文\n\n\t\t\tconst emitSemantic = isSemantic && node.highlightIndex === undefined\n\t\t\t// 用于检查该标签是否为空\n\t\t\tconst mark = emitSemantic ? result.length : -1\n\n\t\t\tif (emitSemantic) {\n\t\t\t\tresult.push(`${depthStr}<${node.tagName}>`)\n\t\t\t\tnextDepth += 1\n\t\t\t}\n\n\t\t\tfor (const child of node.children) {\n\t\t\t\tprocessNode(child, nextDepth, result)\n\t\t\t}\n\n\t\t\tif (emitSemantic) {\n\t\t\t\t// 空标签应被移除\n\t\t\t\tif (result.length === mark + 1) {\n\t\t\t\t\tresult.pop()\n\t\t\t\t} else {\n\t\t\t\t\tresult.push(`${depthStr}</${node.tagName}>`)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (node.type === 'text') {\n\t\t\t// 仅当文本没有带高亮序号的父节点时才输出\n\t\t\tif (hasParentWithHighlightIndex(node)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tnode.parent &&\n\t\t\t\tnode.parent.type === 'element' &&\n\t\t\t\tnode.parent.isVisible &&\n\t\t\t\tnode.parent.isTopElement\n\t\t\t) {\n\t\t\t\tresult.push(`${depthStr}${node.text ?? ''}`)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst result: string[] = []\n\tprocessNode(rootNode, 0, result)\n\treturn result.join('\\n')\n}\n\n// 获取到下一个可点击元素为止的所有文本\nexport const getAllTextTillNextClickableElement = (node: TreeNode, maxDepth = -1): string => {\n\tconst textParts: string[] = []\n\n\t// 递归收集当前节点及其子树的文本内容,遇到其他高亮元素则跳过该分支\n\tconst collectText = (currentNode: TreeNode, currentDepth: number) => {\n\t\tif (maxDepth !== -1 && currentDepth > maxDepth) {\n\t\t\treturn\n\t\t}\n\n\t\t// 遇到其他高亮元素则跳过该分支(当前节点除外)\n\t\tif (\n\t\t\tcurrentNode.type === 'element' &&\n\t\t\tcurrentNode !== node &&\n\t\t\tcurrentNode.highlightIndex !== undefined\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\tif (currentNode.type === 'text' && currentNode.text) {\n\t\t\ttextParts.push(currentNode.text)\n\t\t} else if (currentNode.type === 'element') {\n\t\t\tfor (const child of currentNode.children) {\n\t\t\t\tcollectText(child, currentDepth + 1)\n\t\t\t}\n\t\t}\n\t}\n\n\tcollectText(node, 0)\n\treturn textParts.join('\\n').trim()\n}\n\n// 构建 高亮序号 -> 可交互元素节点 的映射表,供按序号定位/操作页面元素\nexport function getSelectorMap(flatTree: FlatDomTree): Map<number, InteractiveElementDomNode> {\n\tconst selectorMap = new Map<number, InteractiveElementDomNode>()\n\n\tconst keys = Object.keys(flatTree.map)\n\tfor (const key of keys) {\n\t\tconst node = flatTree.map[key]\n\t\tif (node.isInteractive && typeof node.highlightIndex === 'number') {\n\t\t\tselectorMap.set(node.highlightIndex, node as InteractiveElementDomNode)\n\t\t}\n\t}\n\n\treturn selectorMap\n}\n\n// 解析简化 HTML 文本,提取每行的序号与原文,构建 序号 -> 文本行 的映射\nexport function getElementTextMap(simplifiedHTML: string) {\n\tconst lines = simplifiedHTML\n\t\t.split('\\n')\n\t\t.map((line) => line.trim())\n\t\t.filter((line) => line.length > 0)\n\tconst elementTextMap = new Map<number, string>()\n\tfor (const line of lines) {\n\t\tconst regex = /^\\[(\\d+)\\]<[^>]+>([^<]*)/\n\t\tconst match = regex.exec(line)\n\t\tif (match) {\n\t\t\tconst index = parseInt(match[1], 10)\n\t\t\telementTextMap.set(index, line)\n\t\t}\n\t}\n\n\treturn elementTextMap\n}\n\n// 依次执行并清空全局注册的高亮清理函数\nexport function cleanUpHighlights() {\n\tconst cleanupFunctions = (window as any)._highlightCleanupFunctions || []\n\tfor (const cleanup of cleanupFunctions) {\n\t\tif (typeof cleanup === 'function') {\n\t\t\tcleanup()\n\t\t}\n\t}\n\n\t;(window as any)._highlightCleanupFunctions = []\n}\n\n// 监听 URL 的任何变化,立刻清空 highLights\nwindow.addEventListener('popstate', () => {\n\t// console.log('URL changed (popstate), highlights cleaned up.')\n\tcleanUpHighlights()\n})\nwindow.addEventListener('hashchange', () => {\n\t// console.log('URL changed (hashchange), highlights cleaned up.')\n\tcleanUpHighlights()\n})\nwindow.addEventListener('beforeunload', () => {\n\t// console.log('Page is unloading, highlights cleaned up.')\n\tcleanUpHighlights()\n})\n\n// 使用 Navigation API(如果浏览器支持)监听页面导航,导航时清理高亮\nconst navigation = (window as any).navigation\nif (navigation && typeof navigation.addEventListener === 'function') {\n\tnavigation.addEventListener('navigate', () => {\n\t\t// console.log('Navigation event detected, highlights cleaned up.')\n\t\tcleanUpHighlights()\n\t})\n} else {\n\t// 定时器\n\tlet currentUrl = window.location.href\n\tsetInterval(() => {\n\t\tif (window.location.href !== currentUrl) {\n\t\t\tcurrentUrl = window.location.href\n\t\t\t// console.log('URL changed (interval), highlights cleaned up.')\n\t\t\tcleanUpHighlights()\n\t\t}\n\t}, 500)\n}\n","// 获取当前页面的视口尺寸、页面总尺寸、滚动位置及分页信息,供 LLM 了解页面空间结构\nexport function getPageInfo() {\n\tconst viewport_width = window.innerWidth\n\tconst viewport_height = window.innerHeight\n\n\tconst page_width = Math.max(document.documentElement.scrollWidth, document.body.scrollWidth || 0)\n\tconst page_height = Math.max(\n\t\tdocument.documentElement.scrollHeight,\n\t\tdocument.body.scrollHeight || 0\n\t)\n\n\tconst scroll_x = window.scrollX || window.pageXOffset || document.documentElement.scrollLeft || 0\n\tconst scroll_y = window.scrollY || window.pageYOffset || document.documentElement.scrollTop || 0\n\n\tconst pixels_below = Math.max(0, page_height - (window.innerHeight + scroll_y))\n\tconst pixels_right = Math.max(0, page_width - (window.innerWidth + scroll_x))\n\n\treturn {\n\t\t// 当前视口尺寸\n\t\tviewport_width, // 视口宽度(px)\n\t\tviewport_height, // 视口高度(px)\n\n\t\t// 页面总尺寸\n\t\tpage_width, // 页面总宽度(px)\n\t\tpage_height, // 页面总高度(px)\n\n\t\t// 当前滚动位置\n\t\tscroll_x, // 水平滚动距离(px)\n\t\tscroll_y, // 垂直滚动距离(px)\n\n\t\tpixels_above: scroll_y, // 视口上方已滚过的像素数\n\t\tpixels_below, // 视口下方剩余像素数\n\n\t\tpages_above: viewport_height > 0 ? scroll_y / viewport_height : 0, // 已滚过的页数(以视口高度为单位)\n\t\tpages_below: viewport_height > 0 ? pixels_below / viewport_height : 0, // 下方剩余页数\n\t\ttotal_pages: viewport_height > 0 ? page_height / viewport_height : 0, // 页面总页数\n\n\t\tcurrent_page_position: scroll_y / Math.max(1, page_height - viewport_height), // 当前在页面中的位置(0~1)\n\n\t\tpixels_left: scroll_x, // 视口左侧已滚过的像素数\n\t\tpixels_right, // 视口右侧剩余像素数\n\t}\n}\n","import type { BrowserController } from '../BrowserController'\n\n// 查找常见的 React 根节点,并为其添加 data-page-agent-not-interactive 属性(标记为不可交互)\nexport function patchReact(_browserController: BrowserController) {\n\tconst reactRootElements = document.querySelectorAll(\n\t\t'[data-reactroot], [data-reactid], [data-react-checksum], #root, #app, [id^=\"root-\"], [id^=\"app-\"], #adex-wrapper, #adex-root'\n\t)\n\n\tfor (const element of reactRootElements) {\n\t\telement.setAttribute('data-page-agent-not-interactive', 'true')\n\t}\n}\n\n/**\n * @todo (较重,可能有漏判)交互检测:如果元素宽高等于 body 的 offsetWidth/Height,则视为根元素并标记为不可交互(React 经常把大量事件绑定在根元素上,导致误判为可交互)\n */\n","/**\n * 判断页面当前是否处于深色主题的综合函数。\n * 启发式检测,只覆盖常见模式,默认返回 false。\n */\nexport function isPageDark() {\n\ttry {\n\t\tif (hasDarkModeClass()) return true\n\t\tif (hasDarkModeDataAttribute()) return true\n\t\tif (isColorSchemeDark()) return true\n\t\tif (isBackgroundDark()) return true\n\t\tif (isMainContentBackgroundDark()) return true\n\t\tif (isTextColorLight()) return true\n\n\t\treturn false\n\t} catch (error) {\n\t\tconsole.warn('Error determining if page is dark:', error)\n\t\treturn false\n\t}\n}\n\n/**\n * 检查 html 或 body 元素上常见的深色模式 CSS 类名。\n */\nfunction hasDarkModeClass() {\n\tconst DEFAULT_DARK_MODE_CLASSES = ['dark', 'dark-mode', 'theme-dark', 'night', 'night-mode']\n\n\tconst htmlElement = document.documentElement\n\tconst bodyElement = document.body || document.documentElement // 某些情况下 body 可能为 null\n\n\t// 检查 <html> 和 <body> 上的类名\n\tfor (const className of DEFAULT_DARK_MODE_CLASSES) {\n\t\tif (htmlElement.classList.contains(className) || bodyElement?.classList.contains(className)) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n/**\n * 某些 UI 框架使用 data 属性来表示主题\n */\nfunction hasDarkModeDataAttribute() {\n\tconst htmlElement = document.documentElement\n\tconst bodyElement = document.body || document.documentElement // 某些情况下 body 可能为 null\n\n\tconst dataAttrs = ['data-theme', 'data-color-mode', 'data-bs-theme', 'data-mui-color-scheme']\n\tfor (const attr of dataAttrs) {\n\t\tconst bodyValue = bodyElement?.getAttribute(attr)\n\t\tconst htmlValue = htmlElement.getAttribute(attr)\n\n\t\tif (bodyValue?.toLowerCase() === 'dark' || htmlValue?.toLowerCase() === 'dark') {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n/**\n * 检查 CSS 的 `color-scheme` 属性和 `<meta name=\"color-scheme\">` 标签。\n * 只有 \"dark\"/\"only dark\" 才视为深色;\"light dark\" 存在歧义,忽略。\n */\nfunction isColorSchemeDark() {\n\t// 检查 <meta name=\"color-scheme\" content=\"dark\">\n\tconst meta = document.querySelector<HTMLMetaElement>('meta[name=\"color-scheme\"]')\n\tconst metaContent = meta?.content.toLowerCase()\n\tif (metaContent === 'dark' || metaContent === 'only dark') return true\n\n\t// 检查 :root 上计算出的 color-scheme CSS 属性\n\tconst rootStyle = window.getComputedStyle(document.documentElement)\n\tconst colorScheme = rootStyle.getPropertyValue('color-scheme').trim().toLowerCase()\n\treturn colorScheme === 'dark' || colorScheme === 'only dark'\n}\n\n/**\n * 检查 body 元素的背景色以判断页面是否为深色。\n */\nfunction isBackgroundDark() {\n\t// 同时检查 <html> 和 <body>,因为有些页面把颜色设置在 <html> 上\n\tconst htmlStyle = window.getComputedStyle(document.documentElement)\n\tconst bodyStyle = window.getComputedStyle(document.body || document.documentElement)\n\n\t// 获取背景颜色\n\tconst htmlBgColor = htmlStyle.backgroundColor\n\tconst bodyBgColor = bodyStyle.backgroundColor\n\n\t// body 的背景可能是透明的,此时应回退到 html 元素的背景。\n\tif (isColorDark(bodyBgColor)) {\n\t\treturn true\n\t} else if (bodyBgColor === 'transparent' || bodyBgColor.startsWith('rgba(0, 0, 0, 0)')) {\n\t\treturn isColorDark(htmlBgColor)\n\t}\n\n\treturn false\n}\n\n/**\n * 检查 body 上的文字颜色是否为浅色,浅色文字通常意味着深色背景。\n */\nfunction isTextColorLight() {\n\t/** 亮度(0-255)超过该值时认为 body 文字为浅色 */\n\tconst LIGHT_TEXT_LUMINANCE = 200\n\n\tconst bodyStyle = window.getComputedStyle(document.body || document.documentElement)\n\tconst luminance = getLuminance(bodyStyle.color)\n\n\t// 浅色文字亮度很高(例如深色背景上的白色文字)\n\treturn luminance !== null && luminance > LIGHT_TEXT_LUMINANCE\n}\n\n/**\n * 检查主要布局元素(#app、#root 等)的背景色。\n * 很多 SPA 会把内容渲染到某个容器中,该容器可能有自己的深色背景,\n * 而 <body> 仍然是透明的。\n */\nfunction isMainContentBackgroundDark() {\n\tconst { innerWidth: vw, innerHeight: vh } = window\n\tconst minArea = vw * vh * 0.5\n\n\tconst selectors = ['#app', '#root', '#__next']\n\tfor (const selector of selectors) {\n\t\tconst el = document.querySelector(selector)\n\t\tif (!el) continue\n\n\t\tconst rect = el.getBoundingClientRect()\n\t\tif (rect.width * rect.height < minArea) continue\n\n\t\tif (isColorDark(window.getComputedStyle(el).backgroundColor)) return true\n\t}\n\treturn false\n}\n\n// --- 工具函数 ---\n\n/**\n * 解析 RGB 或 RGBA 颜色字符串,返回包含 r、g、b 属性的对象。\n * @param {string} colorString - 例如 \"rgb(34, 34, 34)\" 或 \"rgba(0, 0, 0, 0.5)\"\n * @returns {{r: number, g: number, b: number}|null}\n */\nfunction parseRgbColor(colorString: string) {\n\tconst rgbMatch = /rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/.exec(colorString)\n\tif (!rgbMatch) {\n\t\treturn null // 不是合法的 rgb/rgba 字符串\n\t}\n\treturn {\n\t\tr: parseInt(rgbMatch[1]), // 红色分量 (0-255)\n\t\tg: parseInt(rgbMatch[2]), // 绿色分量 (0-255)\n\t\tb: parseInt(rgbMatch[3]), // 蓝色分量 (0-255)\n\t}\n}\n\n/**\n * 计算 CSS 颜色字符串的感知亮度(0-255)。\n * @param {string} colorString - 例如 \"rgb(50, 50, 50)\" 或 \"rgba(0, 0, 0, 0.5)\"\n * @returns {number|null} - 亮度值,若颜色为透明或无法解析则返回 null。\n */\nfunction getLuminance(colorString: string): number | null {\n\tif (!colorString || colorString === 'transparent' || colorString.startsWith('rgba(0, 0, 0, 0)')) {\n\t\treturn null // 透明色没有有意义的亮度\n\t}\n\n\tconst rgb = parseRgbColor(colorString)\n\tif (!rgb) {\n\t\treturn null // 无法解析该颜色\n\t}\n\n\t// 标准感知亮度公式\n\treturn 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b\n}\n\n/**\n * 根据计算出的亮度判断颜色是否为“深色”。\n * @param {string} colorString - CSS 颜色字符串(例如 \"rgb(50, 50, 50)\")。\n * @param {number} threshold - 0 到 255 之间的值。亮度低于该值的颜色视为深色。默认为 128。\n */\nfunction isColorDark(colorString: string, threshold = 128) {\n\tconst luminance = getLuminance(colorString)\n\treturn luminance !== null && luminance < threshold\n}\n",".wrapper {\n\tposition: fixed;\n\tinset: 0;\n\tz-index: 10000; /* 层级:本层(10000) < Agent UI(20000) < 图片预览(99999),上限 99999 */\n\tcursor: wait;\n\toverflow: hidden;\n\n\tdisplay: none;\n}\n\n.wrapper.visible {\n\tdisplay: block;\n}\n","/* AI 光标样式 */\n.cursor {\n\tposition: absolute;\n\twidth: var(--cursor-size, 48px);\n\theight: var(--cursor-size, 48px);\n\tpointer-events: none;\n\tz-index: 10000;\n}\n\n.cursorBorder {\n\tposition: absolute;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: linear-gradient(45deg, rgb(57, 182, 255), rgb(189, 69, 251));\n\tmask-image: url(./cursor-border.svg);\n\tmask-size: 100% 100%;\n\tmask-repeat: no-repeat;\n\tmargin-left: -16px;\n\tmargin-top: -7px;\n}\n\n.cursorFilling {\n\tposition: absolute;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: url(./cursor-fill.svg);\n\tbackground-size: 100% 100%;\n\tbackground-repeat: no-repeat;\n\tmargin-left: -16px;\n\tmargin-top: -7px;\n}\n\n.cursorRipple {\n\tposition: absolute;\n\twidth: 100%;\n\theight: 100%;\n\tpointer-events: none;\n\tmargin-left: -50%;\n\tmargin-top: -50%;\n\n\t&::after {\n\t\tcontent: '';\n\t\topacity: 0;\n\t\tposition: absolute;\n\t\tinset: 0;\n\t\tborder: 4px solid rgba(57, 182, 255, 1);\n\t\tborder-radius: 50%;\n\t}\n}\n\n.cursor.clicking .cursorRipple::after {\n\tanimation: cursor-ripple 300ms ease-out forwards;\n}\n\n@keyframes cursor-ripple {\n\t0% {\n\t\ttransform: scale(0);\n\t\topacity: 1;\n\t}\n\t100% {\n\t\ttransform: scale(2);\n\t\topacity: 0;\n\t}\n}\n","import { Motion } from 'ai-motion'\n\nimport { isPageDark } from './checkDarkMode'\n\nimport styles from './SimulatorMask.module.css'\nimport cursorStyles from './cursor.module.css'\n\nexport class SimulatorMask extends EventTarget {\n\tshown: boolean = false // 是否已显示遮罩\n\twrapper = document.createElement('div') // 遮罩的根容器元素\n\tmotion: Motion | null = null // ai-motion 的动效实例,可能为 null(创建失败时)\n\n\t#disposed = false // 是否已销毁(dispose 后为 true)\n\n\t#cursor = document.createElement('div') // 模拟光标元素\n\n\t#currentCursorX = 0 // 光标当前 X 坐标\n\t#currentCursorY = 0 // 光标当前 Y 坐标\n\n\t#targetCursorX = 0 // 光标目标 X 坐标\n\t#targetCursorY = 0 // 光标目标 Y 坐标\n\n\t/**\n\t * 构造函数:初始化遮罩 DOM、创建 ai-motion 动效、拦截底层事件,并注册全局事件监听。\n\t */\n\tconstructor() {\n\t\tsuper()\n\n\t\tthis.wrapper.id = 'page-agent-runtime_simulator-mask'\n\t\tthis.wrapper.className = styles.wrapper\n\t\tthis.wrapper.setAttribute('data-browser-use-ignore', 'true')\n\t\tthis.wrapper.setAttribute('data-page-agent-ignore', 'true')\n\n\t\ttry {\n\t\t\tconst motion = new Motion({\n\t\t\t\tmode: isPageDark() ? 'dark' : 'light',\n\t\t\t\tborderWidth: 2,\n\t\t\t\tglowWidth: 50,\n\t\t\t\tstyles: { position: 'absolute', inset: '0' },\n\t\t\t\tskipGreeting: true, // 关闭 ai-motion 库控制台打印\n\t\t\t})\n\t\t\tthis.motion = motion\n\t\t\tthis.wrapper.appendChild(motion.element)\n\t\t\tmotion.autoResize(this.wrapper)\n\t\t} catch (e) {\n\t\t\tconsole.warn('[SimulatorMask] Motion overlay unavailable:', e)\n\t\t}\n\n\t\t// 拦截所有鼠标、键盘和滚轮事件,防止它们穿透到下层页面\n\t\t// 拦截所有鼠标、键盘和滚轮事件,防止它们穿透到下层页面\n\t\tthis.wrapper.addEventListener('click', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('mousedown', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('mouseup', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('mousemove', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('wheel', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('keydown', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('keyup', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\n\t\t// 创建 AI 光标\n\t\tthis.#createCursor()\n\t\t// this.show()\n\n\t\tdocument.body.appendChild(this.wrapper)\n\n\t\tthis.#moveCursorToTarget()\n\n\t\t// 全局事件\n\t\t// @note 遮罩应与代码的其他部分隔离。\n\t\t// 全局事件更容易管理和清理。\n\n\t\tconst movePointerToListener = (event: Event) => {\n\t\t\tconst { x, y } = (event as CustomEvent).detail // 目标坐标 { x, y }\n\t\t\tthis.setCursorPosition(x, y)\n\t\t}\n\t\tconst clickPointerListener = () => {\n\t\t\tthis.triggerClickAnimation()\n\t\t}\n\t\tconst enablePassThroughListener = () => {\n\t\t\tthis.wrapper.style.pointerEvents = 'none'\n\t\t}\n\t\tconst disablePassThroughListener = () => {\n\t\t\tthis.wrapper.style.pointerEvents = 'auto'\n\t\t}\n\n\t\twindow.addEventListener('PageAgent::MovePointerTo', movePointerToListener)\n\t\twindow.addEventListener('PageAgent::ClickPointer', clickPointerListener)\n\t\twindow.addEventListener('PageAgent::EnablePassThrough', enablePassThroughListener)\n\t\twindow.addEventListener('PageAgent::DisablePassThrough', disablePassThroughListener)\n\n\t\tthis.addEventListener('dispose', () => {\n\t\t\twindow.removeEventListener('PageAgent::MovePointerTo', movePointerToListener)\n\t\t\twindow.removeEventListener('PageAgent::ClickPointer', clickPointerListener)\n\t\t\twindow.removeEventListener('PageAgent::EnablePassThrough', enablePassThroughListener)\n\t\t\twindow.removeEventListener('PageAgent::DisablePassThrough', disablePassThroughListener)\n\t\t})\n\t}\n\n\t/** 创建模拟光标的 DOM 结构(包含波纹、填充层、边框层)。 */\n\t#createCursor() {\n\t\tthis.#cursor.className = cursorStyles.cursor\n\n\t\t// 创建波纹效果容器\n\t\t// 波纹效果容器\n\t\tconst rippleContainer = document.createElement('div')\n\t\trippleContainer.className = cursorStyles.cursorRipple\n\t\tthis.#cursor.appendChild(rippleContainer)\n\n\t\t// 创建填充层\n\t\t// 填充层\n\t\tconst fillingLayer = document.createElement('div')\n\t\tfillingLayer.className = cursorStyles.cursorFilling\n\t\tthis.#cursor.appendChild(fillingLayer)\n\n\t\t// 创建边框层\n\t\t// 边框层\n\t\tconst borderLayer = document.createElement('div')\n\t\tborderLayer.className = cursorStyles.cursorBorder\n\t\tthis.#cursor.appendChild(borderLayer)\n\n\t\tthis.wrapper.appendChild(this.#cursor)\n\t}\n\n\t/** 每帧将光标向目标位置移动 20% 距离,实现平滑跟随动画。 */\n\t#moveCursorToTarget() {\n\t\tif (this.#disposed) return\n\n\t\tconst newX = this.#currentCursorX + (this.#targetCursorX - this.#currentCursorX) * 0.2\n\t\tconst newY = this.#currentCursorY + (this.#targetCursorY - this.#currentCursorY) * 0.2\n\n\t\tconst xDistance = Math.abs(newX - this.#targetCursorX)\n\t\tif (xDistance > 0) {\n\t\t\tif (xDistance < 2) {\n\t\t\t\tthis.#currentCursorX = this.#targetCursorX\n\t\t\t} else {\n\t\t\t\tthis.#currentCursorX = newX\n\t\t\t}\n\t\t\tthis.#cursor.style.left = `${this.#currentCursorX}px`\n\t\t}\n\n\t\tconst yDistance = Math.abs(newY - this.#targetCursorY)\n\t\tif (yDistance > 0) {\n\t\t\tif (yDistance < 2) {\n\t\t\t\tthis.#currentCursorY = this.#targetCursorY\n\t\t\t} else {\n\t\t\t\tthis.#currentCursorY = newY\n\t\t\t}\n\t\t\tthis.#cursor.style.top = `${this.#currentCursorY}px`\n\t\t}\n\n\t\trequestAnimationFrame(() => this.#moveCursorToTarget())\n\t}\n\n\t/**\n\t * 设置光标的目标坐标(由外部通过事件触发调用)。\n\t * @param x - 目标 X 坐标\n\t * @param y - 目标 Y 坐标\n\t */\n\tsetCursorPosition(x: number, y: number) {\n\t\tif (this.#disposed) return\n\n\t\tthis.#targetCursorX = x\n\t\tthis.#targetCursorY = y\n\t}\n\n\t/** 触发一次光标点击动画(通过重放 clicking class 实现)。 */\n\ttriggerClickAnimation() {\n\t\tif (this.#disposed) return\n\n\t\tthis.#cursor.classList.remove(cursorStyles.clicking)\n\t\t// 强制触发重排以重新开始动画\n\t\tvoid this.#cursor.offsetHeight\n\t\tthis.#cursor.classList.add(cursorStyles.clicking)\n\t}\n\n\t/** 显示遮罩及模拟光标。 */\n\tshow() {\n\t\tif (this.shown || this.#disposed) return\n\n\t\tthis.shown = true\n\t\tthis.motion?.start()\n\t\tthis.motion?.fadeIn()\n\n\t\tthis.wrapper.classList.add(styles.visible)\n\n\t\t// 初始化光标位置\n\t\tthis.#currentCursorX = window.innerWidth / 2\n\t\tthis.#currentCursorY = window.innerHeight / 2\n\t\tthis.#targetCursorX = this.#currentCursorX\n\t\tthis.#targetCursorY = this.#currentCursorY\n\t\tthis.#cursor.style.left = `${this.#currentCursorX}px`\n\t\tthis.#cursor.style.top = `${this.#currentCursorY}px`\n\t}\n\n\t/** 隐藏遮罩及模拟光标。 */\n\thide() {\n\t\tif (!this.shown || this.#disposed) return\n\n\t\tthis.shown = false\n\t\tthis.motion?.fadeOut()\n\t\tthis.motion?.pause()\n\n\t\tthis.#cursor.classList.remove(cursorStyles.clicking)\n\n\t\tsetTimeout(() => {\n\t\t\tthis.wrapper.classList.remove(styles.visible)\n\t\t}, 800) // 匹配动画的持续时间\n\t}\n\n\t/** 销毁遮罩:释放动效、移除 DOM 并触发 dispose 事件。 */\n\tdispose() {\n\t\tthis.#disposed = true\n\t\tthis.motion?.dispose()\n\t\tthis.wrapper.remove()\n\t\tthis.dispatchEvent(new Event('dispose'))\n\t}\n}\n","/**\n * 版权所有 (C) 2025 Alibaba Group Holding Limited\n * 保留所有权利。\n *\n * BrowserController - 管理 DOM 操作和元素交互。\n * 设计上独立于 LLM,可在单元测试中直接测试。\n * 所有公开方法均为异步,以支持潜在的远程调用。\n */\nimport {\n\tclickElement,\n\tgetElementByIndex,\n\tinputTextElement,\n\tscrollHorizontally,\n\tscrollVertically,\n\tselectOptionElement,\n} from './actions'\nimport * as dom from './dom'\nimport type { BrowserState } from './dom'\nimport type { FlatDomTree, InteractiveElementDomNode } from './dom/dom_tree/type'\nimport { getPageInfo } from './dom/getPageInfo'\nimport { patchReact } from './patches/react'\nimport { isAnchorElement } from './utils'\n\nexport type { BrowserState } from './dom'\n\n/**\n * BrowserController 的配置\n */\nexport interface BrowserControllerConfig extends dom.DomConfig {\n\t/** 操作期间启用视觉遮罩层(默认:false) */\n\tenableMask?: boolean\n}\n\ninterface ActionResult {\n\tsuccess: boolean // 操作是否成功\n\tmessage: string // 操作结果描述消息\n}\n\n/**\n * BrowserController 管理 DOM 状态和元素交互。\n * 它为所有 DOM 操作提供异步方法,并保持状态隔离。\n *\n * @lifecycle\n * - beforeUpdate: 在 DOM 树更新之前触发。\n * - afterUpdate: 在 DOM 树更新之后触发。\n */\nexport class BrowserController extends EventTarget {\n\tprivate config: BrowserControllerConfig // 控制器配置(含 DOM 相关配置及遮罩开关)\n\n\t/** 对应 browser-use 中的 eval_page */\n\tprivate flatTree: FlatDomTree | null = null\n\n\t/**\n\t * 所有已索引的高亮交互元素\n\t * 对应 browser-use 中的 DOMState.selector_map\n\t */\n\tprivate selectorMap = new Map<number, InteractiveElementDomNode>()\n\n\t/** 索引 -> 元素文本描述映射 */\n\tprivate elementTextMap = new Map<number, string>()\n\n\t/**\n\t * 供 LLM 消费的简化 HTML。\n\t * 对应 browser-use 中的 clickable_elements_to_string\n\t */\n\tprivate simplifiedHTML = '<EMPTY>'\n\n\t/** 树最后一次更新的时间 */\n\tprivate lastTimeUpdate = 0\n\n\t/** 树是否至少被索引过一次 */\n\tprivate isIndexed = false\n\n\t/** 用于在自动化期间阻止用户交互的视觉遮罩层 */\n\tprivate mask: InstanceType<typeof import('./visual/SimulatorMask').SimulatorMask> | null = null\n\t/** mask 异步初始化完成的 Promise,用于在调用前等待初始化完成 */\n\tprivate maskReady: Promise<void> | null = null\n\t/** 是否已释放资源 */\n\tprivate disposed = false\n\n\tconstructor(config: BrowserControllerConfig = {}) {\n\t\tsuper()\n\n\t\t// 保存控制器配置\n\t\tthis.config = config\n\n\t\tpatchReact(this)\n\n\t\t// 若配置开启遮罩,则初始化遮罩层\n\t\tif (config.enableMask) this.initMask()\n\t}\n\n\t/**\n\t * 异步初始化遮罩层(动态导入以避免在 Node 中加载 CSS)\n\t */\n\tinitMask() {\n\t\tif (this.disposed || this.maskReady !== null) return\n\t\tthis.maskReady = (async () => {\n\t\t\tconst { SimulatorMask } = await import('./visual/SimulatorMask')\n\t\t\tif (this.disposed) return\n\n\t\t\tconst mask = new SimulatorMask()\n\t\t\tif (this.disposed) {\n\t\t\t\tmask.dispose()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.mask = mask\n\t\t})()\n\t}\n\t// ======= 状态查询 =======\n\n\t/**\n\t * 获取当前页面 URL\n\t */\n\tasync getCurrentUrl(): Promise<string> {\n\t\treturn window.location.href\n\t}\n\n\t/**\n\t * 获取树最后一次更新的时间戳\n\t */\n\tasync getLastUpdateTime(): Promise<number> {\n\t\treturn this.lastTimeUpdate\n\t}\n\n\t/**\n\t * 获取供 LLM 消费的结构化浏览器状态。\n\t * 自动调用 updateTree() 刷新 DOM 状态。\n\t */\n\tasync getBrowserState(): Promise<BrowserState> {\n\t\tconst url = window.location.href // 当前页面 URL\n\t\tconst title = document.title // 当前页面标题\n\t\tconst pi = getPageInfo() // 页面尺寸与滚动位置等信息\n\t\tconst viewportExpansion = dom.resolveViewportExpansion(this.config.viewportExpansion) // 解析视口扩展配置\n\n\t\tawait this.updateTree()\n\n\t\tconst content = this.simplifiedHTML // 供 LLM 使用的简化 HTML\n\n\t\t// 构建页头:页面信息 + 滚动位置提示\n\t\tconst titleLine = `Current Page: [${title}](${url})`\n\n\t\tconst pageInfoLine = `Page info: ${pi.viewport_width}x${pi.viewport_height}px viewport, ${pi.page_width}x${pi.page_height}px total page size, ${pi.pages_above.toFixed(1)} pages above, ${pi.pages_below.toFixed(1)} pages below, ${pi.total_pages.toFixed(1)} total pages, at ${(pi.current_page_position * 100).toFixed(0)}% of page`\n\n\t\tconst elementsLabel =\n\t\t\tviewportExpansion === -1\n\t\t\t\t? 'Interactive elements from top layer of the current page (full page):'\n\t\t\t\t: 'Interactive elements from top layer of the current page inside the viewport:'\n\n\t\tconst hasContentAbove = pi.pixels_above > 4\n\t\tconst scrollHintAbove =\n\t\t\thasContentAbove && viewportExpansion !== -1\n\t\t\t\t? `... ${pi.pixels_above} pixels above (${pi.pages_above.toFixed(1)} pages) - scroll to see more ...`\n\t\t\t\t: '[Start of page]'\n\n\t\tconst header = `${titleLine}\\n${pageInfoLine}\\n\\n${elementsLabel}\\n\\n${scrollHintAbove}`\n\n\t\t// 构建页脚:滚动位置提示\n\t\tconst hasContentBelow = pi.pixels_below > 4\n\t\tconst footer =\n\t\t\thasContentBelow && viewportExpansion !== -1\n\t\t\t\t? `... ${pi.pixels_below} pixels below (${pi.pages_below.toFixed(1)} pages) - scroll to see more ...`\n\t\t\t\t: '[End of page]'\n\n\t\treturn {\n\t\t\turl, // 当前页面 URL\n\t\t\ttitle, // 当前页面标题\n\t\t\theader, // 页头信息(页面信息 + 上方滚动提示)\n\t\t\tcontent, // 供 LLM 使用的简化 HTML\n\t\t\tfooter, // 页脚信息(下方滚动提示)\n\t\t}\n\t}\n\n\t// ======= DOM 树操作 =======\n\n\t/**\n\t * 更新 DOM 树,返回供 LLM 使用的简化 HTML。\n\t * 这是刷新页面状态的主要方法。\n\t * 若已启用遮罩,DOM 提取期间会自动绕过遮罩。\n\t */\n\tasync updateTree(): Promise<string> {\n\t\t// 更新前派发事件,供外部监听\n\t\tthis.dispatchEvent(new Event('beforeUpdate'))\n\n\t\t// 记录本次更新树的时间戳\n\t\tthis.lastTimeUpdate = Date.now()\n\n\t\t// 临时绕过遮罩以允许进行 DOM 提取\n\t\tif (this.mask) {\n\t\t\tthis.mask.wrapper.style.pointerEvents = 'none'\n\t\t}\n\n\t\tdom.cleanUpHighlights()\n\n\t\tconst blacklist = [\n\t\t\t...(this.config.interactiveBlacklist || []), // 配置中指定的交互黑名单\n\t\t\t...Array.from(document.querySelectorAll('[data-page-agent-not-interactive]')), // 页面中标记为不可交互的元素\n\t\t]\n\n\t\t// 生成扁平 DOM 树\n\t\tthis.flatTree = dom.getFlatTree({\n\t\t\t...this.config,\n\t\t\tinteractiveBlacklist: blacklist,\n\t\t})\n\n\t\t// 将扁平 DOM 树转换为供 LLM 使用的简化 HTML\n\t\tthis.simplifiedHTML = dom.flatTreeToString(\n\t\t\tthis.flatTree,\n\t\t\tthis.config.includeAttributes,\n\t\t\tthis.config.keepSemanticTags\n\t\t)\n\n\t\tthis.selectorMap.clear()\n\t\tthis.selectorMap = dom.getSelectorMap(this.flatTree) // 生成索引 -> 交互元素节点的映射\n\n\t\tthis.elementTextMap.clear()\n\t\tthis.elementTextMap = dom.getElementTextMap(this.simplifiedHTML) // 生成索引 -> 元素文本描述的映射\n\n\t\t// 标记为已索引 — 现在允许对元素执行操作\n\t\tthis.isIndexed = true\n\n\t\t// 恢复遮罩拦截\n\t\tif (this.mask) {\n\t\t\tthis.mask.wrapper.style.pointerEvents = 'auto'\n\t\t}\n\n\t\t// 更新完成后派发事件,供外部监听\n\t\tthis.dispatchEvent(new Event('afterUpdate'))\n\n\t\treturn this.simplifiedHTML\n\t}\n\n\t/**\n\t * 清理所有元素高亮\n\t */\n\tasync cleanUpHighlights(): Promise<void> {\n\t\tconsole.log('[BrowserController] cleanUpHighlights')\n\t\tdom.cleanUpHighlights()\n\t}\n\n\t// ======= 元素操作 =======\n\n\t/**\n\t * 在任何基于索引的操作之前确保树已被索引。\n\t * 若尚未调用 updateTree() 则抛出异常。\n\t */\n\tprivate assertIndexed(): void {\n\t\tif (!this.isIndexed) {\n\t\t\tthrow new Error('DOM tree not indexed yet. Can not perform actions on elements.')\n\t\t}\n\t}\n\n\t/**\n\t * 根据索引点击元素\n\t */\n\tasync clickElement(index: number): Promise<ActionResult> {\n\t\ttry {\n\t\t\tthis.assertIndexed()\n\t\t\tconst element = getElementByIndex(this.selectorMap, index) // 根据索引获取目标元素\n\t\t\tconst elemText = this.elementTextMap.get(index) // 获取元素文本描述(用于消息展示)\n\n\t\t\tawait clickElement(element)\n\n\t\t\t// 处理在新标签页打开的链接\n\t\t\t// 处理 _blank 新标签页链接\n\t\t\tif (isAnchorElement(element) && element.target === '_blank') {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true, // 操作成功\n\t\t\t\t\tmessage: `✅ Clicked element (${elemText ?? index}). ⚠️ Link opened in a new tab.`, // 成功消息\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsuccess: true, // 操作成功\n\t\t\t\tmessage: `✅ Clicked element (${elemText ?? index}).`, // 成功消息\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false, // 操作失败\n\t\t\t\tmessage: `❌ Failed to click element: ${error}`, // 失败消息\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 根据索引向元素输入文本\n\t */\n\tasync inputText(index: number, text: string): Promise<ActionResult> {\n\t\ttry {\n\t\t\tthis.assertIndexed()\n\t\t\tconst element = getElementByIndex(this.selectorMap, index) // 根据索引获取目标元素\n\t\t\tconst elemText = this.elementTextMap.get(index) // 获取元素文本描述(用于消息展示)\n\t\t\tawait inputTextElement(element, text)\n\n\t\t\treturn {\n\t\t\t\tsuccess: true, // 操作成功\n\t\t\t\tmessage: `✅ Input text (${text}) into element (${elemText ?? index}).`, // 成功消息\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false, // 操作失败\n\t\t\t\tmessage: `❌ Failed to input text: ${error}`, // 失败消息\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 根据索引和选项文本在下拉框中选中选项\n\t */\n\tasync selectOption(index: number, optionText: string): Promise<ActionResult> {\n\t\ttry {\n\t\t\tthis.assertIndexed()\n\t\t\tconst element = getElementByIndex(this.selectorMap, index) // 根据索引获取目标元素\n\t\t\tconst elemText = this.elementTextMap.get(index) // 获取元素文本描述(用于消息展示)\n\t\t\tawait selectOptionElement(element as HTMLSelectElement, optionText)\n\n\t\t\treturn {\n\t\t\t\tsuccess: true, // 操作成功\n\t\t\t\tmessage: `✅ Selected option (${optionText}) in element (${elemText ?? index}).`, // 成功消息\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false, // 操作失败\n\t\t\t\tmessage: `❌ Failed to select option: ${error}`, // 失败消息\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 垂直滚动\n\t */\n\tasync scroll(options: {\n\t\tdown: boolean // 是否向下滚动\n\t\tnumPages: number // 滚动的页数(在未指定 pixels 时作为滚动量基准)\n\t\tpixels?: number // 可选的滚动像素数,优先于 numPages\n\t\tindex?: number // 可选的元素索引;指定时滚动该元素容器而非整页\n\t}): Promise<ActionResult> {\n\t\ttry {\n\t\t\tconst { down, numPages, pixels, index } = options\n\n\t\t\tthis.assertIndexed()\n\n\t\t\tconst scrollAmount = (pixels ?? numPages * window.innerHeight) * (down ? 1 : -1)\n\n\t\t\tconst element = index !== undefined ? getElementByIndex(this.selectorMap, index) : null\n\n\t\t\tconst message = await scrollVertically(scrollAmount, element)\n\n\t\t\treturn {\n\t\t\t\tsuccess: true, // 操作成功\n\t\t\t\tmessage, // 滚动结果描述消息\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false, // 操作失败\n\t\t\t\tmessage: `❌ Failed to scroll: ${error}`, // 失败消息\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 水平滚动\n\t */\n\tasync scrollHorizontally(options: {\n\t\tright: boolean // 是否向右滚动\n\t\tpixels: number // 滚动的像素数\n\t\tindex?: number // 可选的元素索引;指定时滚动该元素容器而非整页\n\t}): Promise<ActionResult> {\n\t\ttry {\n\t\t\tconst { right, pixels, index } = options\n\n\t\t\tthis.assertIndexed()\n\n\t\t\tconst scrollAmount = pixels * (right ? 1 : -1)\n\n\t\t\tconst element = index !== undefined ? getElementByIndex(this.selectorMap, index) : null\n\n\t\t\tconst message = await scrollHorizontally(scrollAmount, element)\n\n\t\t\treturn {\n\t\t\t\tsuccess: true, // 操作成功\n\t\t\t\tmessage, // 滚动结果描述消息\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false, // 操作失败\n\t\t\t\tmessage: `❌ Failed to scroll horizontally: ${error}`, // 失败消息\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 在页面上执行任意 JavaScript。\n\t * 可选的 `signal` 会暴露到脚本作用域中,使协作代码能在任务被中止时及时退出。\n\t */\n\tasync executeJavascript(script: string, signal?: AbortSignal): Promise<ActionResult> {\n\t\ttry {\n\t\t\t// 将脚本包进 async 函数以支持 await,并把 signal 暴露到脚本作用域\n\t\t\tconst asyncFunction = (0, eval)(`(async (signal) => { ${script} })`)\n\t\t\tconst result = await asyncFunction(signal)\n\t\t\treturn {\n\t\t\t\tsuccess: true, // 操作成功\n\t\t\t\tmessage: `✅ Executed JavaScript. Result: ${result}`, // 成功消息\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false, // 操作失败\n\t\t\t\tmessage: `❌ Error executing JavaScript: ${error}`, // 失败消息\n\t\t\t}\n\t\t}\n\t}\n\n\t// ======= 遮罩操作 =======\n\n\t/**\n\t * 显示视觉遮罩层。\n\t * 仅在遮罩初始化完成后可用。\n\t */\n\tasync showMask(): Promise<void> {\n\t\tif (this.disposed) return\n\t\tawait this.maskReady\n\t\tif (this.disposed) return\n\t\tthis.mask?.show()\n\t}\n\n\t/**\n\t * 隐藏视觉遮罩层。\n\t * 仅在遮罩初始化完成后可用。\n\t */\n\tasync hideMask(): Promise<void> {\n\t\tif (this.disposed) return\n\t\tawait this.maskReady\n\t\tif (this.disposed) return\n\t\tthis.mask?.hide()\n\t}\n\n\t/**\n\t * 释放资源并清理\n\t */\n\tdispose(): void {\n\t\tif (this.disposed) return\n\t\tthis.disposed = true\n\n\t\tconst errors: unknown[] = [] // 收集清理过程中出现的错误\n\t\tconst cleanUp = (callback: () => void) => {\n\t\t\ttry {\n\t\t\t\tcallback()\n\t\t\t} catch (error: unknown) {\n\t\t\t\terrors.push(error)\n\t\t\t}\n\t\t}\n\n\t\tcleanUp(() => dom.cleanUpHighlights()) // 清理元素高亮\n\t\tcleanUp(() => {\n\t\t\tthis.flatTree = null\n\t\t\tthis.selectorMap.clear()\n\t\t\tthis.elementTextMap.clear()\n\t\t\tthis.simplifiedHTML = '<EMPTY>'\n\t\t\tthis.isIndexed = false\n\t\t})\n\t\tcleanUp(() => this.mask?.dispose()) // 释放遮罩层\n\t\tthis.mask = null\n\n\t\tif (errors.length === 1) throw errors[0]\n\t\tif (errors.length > 1) throw new AggregateError(errors, 'BrowserController disposal failed')\n\t}\n}\n\nexport * from './actions'\n","// 英文翻译(基准/参考语言)\nconst enUS = {\n\tui: {\n\t\tready: 'Ready', // 就绪状态提示\n\t\tthinking: 'Thinking...', // 思考中提示\n\t\ttaskInput: 'Enter new task, describe steps in detail, press Enter to submit', // 任务输入框占位提示\n\t\tuserAnswerPrompt: 'Please answer the question above, press Enter to submit', // 用户回答问题时的输入框提示\n\t\ttaskTerminated: 'Task terminated', // 任务已终止\n\t\ttaskCompleted: 'Task completed', // 任务已完成\n\t\tuserAnswer: 'User answer: {{input}}', // 用户回答展示模板\n\t\tquestion: 'Question: {{question}}', // 问题展示模板\n\t\twaitingPlaceholder: 'Waiting for task to start...', // 等待任务开始的占位文本\n\t\tstop: 'Stop', // 停止按钮文本\n\t\tclose: 'Close', // 关闭按钮文本\n\t\texpand: 'Expand history', // 展开历史提示\n\t\tcollapse: 'Collapse history', // 收起历史提示\n\t\tstep: 'Step {{number}}', // 步骤编号模板\n\t\tattachButtonTitle: 'Attach files', // 附件按钮的 title 提示\n\t\tattachTooMany: 'You can attach up to {{max}} files', // 附件数量超限提示\n\t\tattachTooLarge: '\"{{name}}\" exceeds the {{maxMB}}MB size limit', // 附件体积超限提示\n\t\tattachUnsupportedType: '\"{{name}}\" is not a supported file type', // 附件类型不支持提示\n\t\tattachmentsLabel: '📎 Attachments: {{names}}', // 任务卡片中展示的附件文件名列表\n\t\ttools: {\n\t\t\tclicking: 'Clicking element [{{index}}]...', // 正在点击元素\n\t\t\tinputting: 'Inputting text to element [{{index}}]...', // 正在向元素输入文本\n\t\t\tselecting: 'Selecting option \"{{text}}\"...', // 正在选择选项\n\t\t\tscrolling: 'Scrolling page...', // 正在滚动页面\n\t\t\twaiting: 'Waiting {{seconds}} seconds...', // 正在等待\n\t\t\taskingUser: 'Asking user...', // 正在询问用户\n\t\t\tdone: 'Task done', // 任务结束\n\t\t\tclicked: '🖱️ Clicked element [{{index}}]', // 已点击元素\n\t\t\tinputted: '⌨️ Inputted text \"{{text}}\"', // 已输入文本\n\t\t\tselected: '☑️ Selected option \"{{text}}\"', // 已选择选项\n\t\t\tscrolled: '🛞 Page scrolled', // 页面已滚动\n\t\t\twaited: '⌛️ Wait completed', // 等待完成\n\t\t\texecuting: 'Executing {{toolName}}...', // 正在执行工具\n\t\t\tresultSuccess: 'success', // 工具执行结果:成功\n\t\t\tresultFailure: 'failed', // 工具执行结果:失败\n\t\t\tresultError: 'error', // 工具执行结果:错误\n\t\t},\n\t\terrors: {\n\t\t\telementNotFound: 'No interactive element found at index {{index}}', // 未找到交互元素\n\t\t\ttaskRequired: 'Task description is required', // 任务描述不能为空\n\t\t\texecutionFailed: 'Task execution failed', // 任务执行失败\n\t\t\tnotInputElement: 'Element is not an input or textarea', // 元素不是输入框或文本域\n\t\t\tnotSelectElement: 'Element is not a select element', // 元素不是选择框\n\t\t\toptionNotFound: 'Option \"{{text}}\" not found', // 未找到选项\n\t\t},\n\t},\n} as const\n\n// 中文翻译(必须与 enUS 结构一致)\nconst zhCN = {\n\tui: {\n\t\tready: '准备就绪', // 就绪状态提示\n\t\tthinking: '正在思考...', // 思考中提示\n\t\ttaskInput: '输入新任务,详细描述步骤,回车提交', // 任务输入框占位提示\n\t\tuserAnswerPrompt: '请回答上面问题,回车提交', // 用户回答问题时的输入框提示\n\t\ttaskTerminated: '任务已终止', // 任务已终止\n\t\ttaskCompleted: '任务结束', // 任务已完成\n\t\tuserAnswer: '用户回答: {{input}}', // 用户回答展示模板\n\t\tquestion: '询问: {{question}}', // 问题展示模板\n\t\twaitingPlaceholder: '等待任务开始...', // 等待任务开始的占位文本\n\t\tstop: '终止', // 停止按钮文本\n\t\tclose: '关闭', // 关闭按钮文本\n\t\texpand: '展开历史', // 展开历史提示\n\t\tcollapse: '收起历史', // 收起历史提示\n\t\tstep: '步骤 {{number}}', // 步骤编号模板\n\t\tattachButtonTitle: '添加附件', // 附件按钮的 title 提示\n\t\tattachTooMany: '最多可添加 {{max}} 个文件', // 附件数量超限提示\n\t\tattachTooLarge: '\"{{name}}\" 超过 {{maxMB}}MB 大小限制', // 附件体积超限提示\n\t\tattachUnsupportedType: '\"{{name}}\" 不是支持的文件类型', // 附件类型不支持提示\n\t\tattachmentsLabel: '📎 附件: {{names}}', // 任务卡片中展示的附件文件名列表\n\t\ttools: {\n\t\t\tclicking: '正在点击元素 [{{index}}]...', // 正在点击元素\n\t\t\tinputting: '正在输入文本到元素 [{{index}}]...', // 正在向元素输入文本\n\t\t\tselecting: '正在选择选项 \"{{text}}\"...', // 正在选择选项\n\t\t\tscrolling: '正在滚动页面...', // 正在滚动页面\n\t\t\twaiting: '等待 {{seconds}} 秒...', // 正在等待\n\t\t\taskingUser: '正在询问用户...', // 正在询问用户\n\t\t\tdone: '结束任务', // 任务结束\n\t\t\tclicked: '🖱️ 已点击元素 [{{index}}]', // 已点击元素\n\t\t\tinputted: '⌨️ 已输入文本 \"{{text}}\"', // 已输入文本\n\t\t\tselected: '☑️ 已选择选项 \"{{text}}\"', // 已选择选项\n\t\t\tscrolled: '🛞 页面滚动完成', // 页面已滚动\n\t\t\twaited: '⌛️ 等待完成', // 等待完成\n\t\t\texecuting: '正在执行 {{toolName}}...', // 正在执行工具\n\t\t\tresultSuccess: '成功', // 工具执行结果:成功\n\t\t\tresultFailure: '失败', // 工具执行结果:失败\n\t\t\tresultError: '错误', // 工具执行结果:错误\n\t\t},\n\t\terrors: {\n\t\t\telementNotFound: '未找到索引为 {{index}} 的交互元素', // 未找到交互元素\n\t\t\ttaskRequired: '任务描述不能为空', // 任务描述不能为空\n\t\t\texecutionFailed: '任务执行失败', // 任务执行失败\n\t\t\tnotInputElement: '元素不是输入框或文本域', // 元素不是输入框或文本域\n\t\t\tnotSelectElement: '元素不是选择框', // 元素不是选择框\n\t\t\toptionNotFound: '未找到选项 \"{{text}}\"', // 未找到选项\n\t\t},\n\t},\n} as const\n\n// 类型定义:根据英文基础结构生成(但叶子值为 string 类型)\ntype DeepStringify<T> = {\n\t[K in keyof T]: T[K] extends string ? string : T[K] extends object ? DeepStringify<T[K]> : T[K] // 递归将叶子值展开为 string\n}\n\n/** 翻译数据结构:由英文基础结构推导出的全字符串类型 */\nexport type TranslationSchema = DeepStringify<typeof enUS>\n\n// 工具类型:提取翻译对象的所有嵌套路径\ntype NestedKeyOf<ObjectType extends object> = {\n\t[Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object\n\t\t? `${Key}` | `${Key}.${NestedKeyOf<ObjectType[Key]>}`\n\t\t: `${Key}`\n}[keyof ObjectType & (string | number)]\n\n/** 所有可能的翻译键路径(如 \"ui.tools.done\") */\nexport type TranslationKey = NestedKeyOf<TranslationSchema>\n\n/** 翻译占位符参数类型:键为参数名,值为字符串或数字 */\nexport type TranslationParams = Record<string, string | number>\n\n/** 语言包集合:键为语言代码,值为对应翻译表 */\nexport const locales = {\n\t'en-US': enUS, // 英文翻译\n\t'zh-CN': zhCN, // 中文翻译\n} as const\n\n/** 受支持的语言代码类型 */\nexport type SupportedLanguage = keyof typeof locales\n","import {\n\ttype SupportedLanguage,\n\ttype TranslationKey,\n\ttype TranslationParams,\n\ttype TranslationSchema,\n\tlocales,\n} from './locales'\n\nexport class I18n {\n\tprivate language: SupportedLanguage\n\tprivate translations: TranslationSchema\n\n\tconstructor(language: SupportedLanguage = 'zh-CN') {\n\t\t// language 语言代码,默认 'zh-CN'\n\t\tthis.language = language in locales ? language : 'zh-CN' // 兜底:不支持的语种回退到 zh-CN\n\t\tthis.translations = locales[this.language] // 当前语言的翻译表\n\t}\n\n\t// 类型安全的翻译方法\n\tt(key: TranslationKey, params?: TranslationParams): string {\n\t\tconst value = this.getNestedValue(this.translations, key) // 根据点分路径取出翻译文本\n\t\tif (!value) {\n\t\t\tconsole.warn(`Translation key \"${key}\" not found for language \"${this.language}\"`)\n\t\t\treturn key\n\t\t}\n\n\t\tif (params) {\n\t\t\treturn this.interpolate(value, params)\n\t\t}\n\t\treturn value\n\t}\n\n\t/** 根据点分路径从翻译对象中取值 */\n\tprivate getNestedValue(obj: any, path: string): string | undefined {\n\t\treturn path.split('.').reduce((current, key) => current?.[key], obj)\n\t}\n\n\t/** 将模板中的 {{key}} 占位符替换为参数值 */\n\tprivate interpolate(template: string, params: TranslationParams): string {\n\t\treturn template.replace(/\\{\\{(\\w+)\\}\\}/g, (match, key) => {\n\t\t\t// 使用 != null 同时判断 null 和 undefined,允许空字符串通过\n\t\t\treturn params[key] != null ? params[key].toString() : match\n\t\t})\n\t}\n\n\t/** 获取当前语言代码 */\n\tgetLanguage(): SupportedLanguage {\n\t\treturn this.language\n\t}\n}\n\n// 导出类型和实例创建函数\nexport type { TranslationKey, SupportedLanguage, TranslationParams }\nexport { locales }\n","/** 截断字符串,超过最大长度时以省略号结尾 */\nexport function truncate(text: string, maxLength: number): string {\n\tif (text.length > maxLength) {\n\t\treturn text.substring(0, maxLength) + '...'\n\t}\n\treturn text\n}\n\n/**\n * 转义 HTML 特殊字符,防止 XSS 及渲染问题\n */\nexport function escapeHtml(text: string): string {\n\treturn text\n\t\t.replace(/&/g, '&')\n\t\t.replace(/</g, '<')\n\t\t.replace(/>/g, '>')\n\t\t.replace(/\"/g, '"')\n\t\t.replace(/'/g, ''')\n}\n",".wrapper {\n\tposition: fixed;\n\tbottom: 100px;\n\tleft: 50%;\n\ttransform: translateX(-50%) translateY(20px);\n\topacity: 0;\n\tz-index: 20000; /* 层级:SimulatorMask(10000) < 本层(20000),上限 99999 */\n\tbox-sizing: border-box;\n\n\t/* 普通流布局:历史弹窗、面板依次纵向排列,靠子元素自身尺寸撑开,\n\t * 不再用 position:absolute + 手算偏移量拼位置(这套算法之前连续出过两次 bug:\n\t * 弹窗被状态条盖住、状态条运行态时下方角没跟着圆)。 */\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\n\t* {\n\t\tbox-sizing: border-box;\n\t}\n\n\t/* 宽度取窗口 40%,用 clamp 防止超窄/超宽窗口下过小或过大 */\n\t--width: clamp(280px, 40vw, 640px);\n\t--height: 40px; /* 状态条高度 */\n\t--input-height: 80px; /* 输入区高度 */\n\t--border-radius: 16px;\n\n\t--side-space: 12px; /* 控制栏两侧的间距 */\n\t--history-width: calc(var(--width) - var(--side-space) * 2);\n\n\t--color-1: rgb(57, 182, 255);\n\t--color-2: rgb(189, 69, 251);\n\t--color-3: rgb(255, 87, 51);\n\t--color-4: rgb(255, 214, 0);\n\n\twidth: var(--width);\n\n\ttransition:\n\t\topacity 0.3s ease-in-out,\n\t\ttransform 0.3s ease-in-out;\n\n\t/* 响应式设计 */\n\t@media (max-width: 480px) {\n\t\twidth: calc(100vw - 40px);\n\t\t--width: calc(100vw - 40px);\n\t}\n}\n\n@keyframes mask-running {\n\tfrom {\n\t\ttransform: translateX(-100%);\n\t}\n\tto {\n\t\ttransform: translateX(100%);\n\t}\n}\n\n/* 状态条 + 输入区的外层容器;.background 光效挂在这一层,.shellInner 负责裁出圆角 */\n.shell {\n\tposition: relative;\n\twidth: 100%;\n}\n\n.background {\n\tposition: absolute;\n\tinset: -2px;\n\tborder-radius: calc(var(--border-radius) + 4px);\n\tfilter: blur(4px);\n\toverflow: hidden;\n\tpointer-events: none;\n\n\t&::before {\n\t\tcontent: '';\n\t\tpointer-events: none;\n\t\tposition: absolute;\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\tleft: 0;\n\t\ttop: 0;\n\n\t\tbackground-image: linear-gradient(\n\t\t\tto bottom left,\n\t\t\tvar(--color-1),\n\t\t\tvar(--color-2),\n\t\t\tvar(--color-1)\n\t\t);\n\t\tanimation: mask-running 2s linear infinite;\n\t}\n\t&::after {\n\t\tcontent: '';\n\t\tpointer-events: none;\n\t\tposition: absolute;\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\tleft: 0;\n\t\ttop: 0;\n\n\t\tbackground-image: linear-gradient(\n\t\t\tto bottom left,\n\t\t\tvar(--color-2),\n\t\t\tvar(--color-1),\n\t\t\tvar(--color-2)\n\t\t);\n\t\tanimation: mask-running 2s linear infinite;\n\t\tanimation-delay: 1s;\n\t}\n}\n\n/* 可视区域:靠 overflow:hidden + border-radius 统一裁角,\n * header 单独存在(运行中输入区隐藏)或 header+输入区一起存在时,四角都自动是圆的,\n * 不用再像以前那样给 header 写一条\"输入区隐藏时补圆角\"的特判规则。 */\n.shellInner {\n\tposition: relative;\n\tdisplay: flex;\n\tflex-direction: column;\n\toverflow: hidden;\n\tborder-radius: var(--border-radius);\n\tborder: 2px solid rgba(255, 255, 255, 0.4);\n\tbox-shadow:\n\t\t0 0 5px 1px rgba(255, 255, 255, 0.3),\n\t\t0 4px 20px rgba(0, 0, 0, 0.5);\n}\n\n/* 控制栏 */\n.header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tpadding: 8px 12px;\n\tuser-select: none;\n\n\theight: var(--height);\n\tflex-shrink: 0; /* 防止 header 被压缩 */\n\n\tcursor: pointer;\n\n\tbackground: rgba(0, 0, 0, 0.5);\n\tbackdrop-filter: blur(10px);\n\tbackground-clip: padding-box;\n\n\t.statusSection {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 8px;\n\t\tflex: 1;\n\t\tmin-height: 24px; /* 确保垂直居中 */\n\n\t\t.indicator {\n\t\t\twidth: 6px;\n\t\t\theight: 6px;\n\t\t\tborder-radius: 50%;\n\t\t\tbackground: rgba(255, 255, 255, 0.5);\n\t\t\tflex-shrink: 0;\n\t\t\tanimation: none; /* 默认无动画 */\n\n\t\t\t/* 运行状态 - 有动画 */\n\t\t\t&.thinking {\n\t\t\t\tbackground: rgb(57, 182, 255);\n\t\t\t\tanimation: pulse 0.8s ease-in-out infinite;\n\t\t\t}\n\n\t\t\t&.tool_executing {\n\t\t\t\tbackground: rgb(189, 69, 251);\n\t\t\t\tanimation: pulse 0.6s ease-in-out infinite;\n\t\t\t}\n\n\t\t\t&.retry {\n\t\t\t\tbackground: rgb(255, 214, 0);\n\t\t\t\tanimation: retryPulse 1s ease-in-out infinite;\n\t\t\t}\n\n\t\t\t/* 静止状态 - 无动画 */\n\t\t\t&.completed,\n\t\t\t&.input,\n\t\t\t&.output {\n\t\t\t\tbackground: rgb(34, 197, 94);\n\t\t\t\tanimation: none;\n\t\t\t}\n\n\t\t\t&.error {\n\t\t\t\tbackground: rgb(239, 68, 68);\n\t\t\t\tanimation: none;\n\t\t\t}\n\t\t}\n\n\t\t.statusText {\n\t\t\tcolor: white;\n\t\t\tfont-size: 12px;\n\t\t\tline-height: 1;\n\t\t\tfont-weight: 500;\n\t\t\ttransition: all 0.3s ease-in-out;\n\t\t\tposition: relative;\n\t\t\toverflow: hidden;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tmin-height: 24px; /* 确保垂直居中 */\n\n\t\t\t&.fadeOut {\n\t\t\t\tanimation: statusTextFadeOut 0.3s ease forwards;\n\t\t\t}\n\n\t\t\t&.fadeIn {\n\t\t\t\tanimation: statusTextFadeIn 0.3s ease forwards;\n\t\t\t}\n\t\t}\n\t}\n\n\t.controls {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 4px;\n\n\t\t.controlButton {\n\t\t\twidth: 24px;\n\t\t\theight: 24px;\n\t\t\tborder: none;\n\t\t\tborder-radius: 4px;\n\t\t\tbackground: rgba(255, 255, 255, 0.1);\n\t\t\tcolor: white;\n\t\t\tcursor: pointer;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tfont-size: 12px;\n\t\t\tline-height: 1;\n\n\t\t\t&:hover {\n\t\t\t\tbackground: rgba(255, 255, 255, 0.2);\n\t\t\t}\n\t\t}\n\n\t\t.stopButton {\n\t\t\tbackground: rgba(239, 68, 68, 0.2);\n\t\t\tcolor: rgb(255, 41, 41);\n\t\t\tfont-weight: 600;\n\n\t\t\t&:hover {\n\t\t\t\tbackground: rgba(239, 68, 68, 0.3);\n\t\t\t}\n\t\t}\n\t}\n}\n\n@keyframes statusTextFadeIn {\n\t0% {\n\t\topacity: 0;\n\t\ttransform: translateY(5px);\n\t}\n\t100% {\n\t\topacity: 1;\n\t\ttransform: translateY(0);\n\t}\n}\n\n@keyframes statusTextFadeOut {\n\t0% {\n\t\topacity: 1;\n\t\ttransform: translateY(0);\n\t}\n\t100% {\n\t\topacity: 0;\n\t\ttransform: translateY(-5px);\n\t}\n}\n\n/* 历史记录弹窗:普通流里排在面板前面(视觉上在上方),默认收起为 0 高度,\n * 展开时靠 JS 量出的真实内容高度做 max-height 动画(不用固定封顶值),\n * 撑开多高、要不要出现,跟面板本身完全无关,不用再算\"面板一共多高、弹窗该挂在哪个 bottom 偏移\"。 */\n.historySectionWrapper {\n\twidth: 100%;\n\toverflow: hidden;\n\tmax-height: 0;\n\tmargin-bottom: 0;\n\n\tbackground: rgba(2, 0, 20, 0.5);\n\tbackdrop-filter: blur(10px);\n\n\ttext-shadow: 0 0 1px rgba(0, 0, 0, 0.2);\n\n\tborder-radius: calc(var(--border-radius) + 4px);\n\tborder: 0 solid rgba(255, 255, 255, 0);\n\tbox-shadow: 0 4px 16px rgba(0, 0, 0, 0);\n\n\ttransition:\n\t\tmax-height 0.2s,\n\t\tmargin-bottom 0.2s,\n\t\tborder-width 0.2s,\n\t\tborder-color 0.2s,\n\t\tbox-shadow 0.2s;\n\n\t.wrapper.expanded & {\n\t\tmargin-bottom: 8px;\n\t\tborder-width: 2px;\n\t\tborder-color: rgba(255, 255, 255, 0.4);\n\t\tbox-shadow: 0 4px 16px rgba(0, 0, 0, 0.6);\n\t}\n\n\t.historySection {\n\t\tmax-height: min(\n\t\t\t500px,\n\t\t\tcalc(100vh - 200px - var(--height))\n\t\t); /* 列表本身的滚动上限,跟展开/收起动画解耦 */\n\t\toverflow-y: auto;\n\t\toverscroll-behavior: contain;\n\t\tscrollbar-width: none;\n\t\t/* 底部留白用 padding 撑,不依赖最后一项 margin-bottom:\n\t\t * overflow:auto 容器会裁掉末尾子元素的 margin,导致最后一条记录贴底 */\n\t\tpadding: 8px;\n\n\t\t.historyItem {\n\t\t\t/* backdrop-filter: blur(10px); */\n\t\t\tpadding: 8px 10px;\n\t\t\tmargin-bottom: 8px;\n\t\t\tbackground: linear-gradient(135deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.03));\n\t\t\tborder-radius: 8px;\n\t\t\tborder-left: 2px solid rgba(57, 182, 255, 0.5);\n\t\t\tfont-size: 12px;\n\t\t\tcolor: white;\n\t\t\t/* color: black; */\n\t\t\tline-height: 1.3;\n\t\t\tposition: relative;\n\t\t\toverflow: hidden;\n\n\t\t\t/* 微妙的内阴影 */\n\t\t\tbox-shadow:\n\t\t\t\t0 1px 0 rgba(255, 255, 255, 0.1),\n\t\t\t\t0 1px 3px rgba(0, 0, 0, 0.1);\n\n\t\t\t&::before {\n\t\t\t\tcontent: '';\n\t\t\t\tposition: absolute;\n\t\t\t\ttop: 0;\n\t\t\t\tleft: 0;\n\t\t\t\tright: 0;\n\t\t\t\theight: 1px;\n\t\t\t\tbackground: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);\n\t\t\t}\n\n\t\t\t&:hover {\n\t\t\t\tbackground: linear-gradient(135deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.06));\n\t\t\t\t/* transform: translateY(-1px); */\n\t\t\t\tbox-shadow:\n\t\t\t\t\tinset 0 1px 0 rgba(255, 255, 255, 0.15),\n\t\t\t\t\t0 2px 4px rgba(0, 0, 0, 0.15);\n\t\t\t}\n\n\t\t\t&.completed,\n\t\t\t&.input,\n\t\t\t&.output {\n\t\t\t\tborder-left-color: rgb(34, 197, 94);\n\t\t\t\tbackground: linear-gradient(135deg, rgba(34, 197, 94, 0.1), rgba(34, 197, 94, 0.05));\n\t\t\t}\n\n\t\t\t&.error {\n\t\t\t\tborder-left-color: rgb(239, 68, 68);\n\t\t\t\tbackground: linear-gradient(135deg, rgba(239, 68, 68, 0.1), rgba(239, 68, 68, 0.05));\n\t\t\t}\n\n\t\t\t&.retry {\n\t\t\t\tborder-left-color: rgb(255, 214, 0);\n\t\t\t\tbackground: linear-gradient(135deg, rgba(255, 214, 0, 0.1), rgba(255, 214, 0, 0.05));\n\t\t\t}\n\n\t\t\t&.observation {\n\t\t\t\tborder-left-color: rgb(147, 51, 234);\n\t\t\t\tbackground: linear-gradient(135deg, rgba(147, 51, 234, 0.1), rgba(147, 51, 234, 0.05));\n\t\t\t}\n\n\t\t\t&.question {\n\t\t\t\tborder-left-color: rgb(255, 159, 67);\n\t\t\t\tbackground: linear-gradient(135deg, rgba(255, 159, 67, 0.15), rgba(255, 159, 67, 0.08));\n\t\t\t}\n\n\t\t\t/* 突出显示 done 成功结果 */\n\t\t\t&.doneSuccess {\n\t\t\t\tbackground: linear-gradient(\n\t\t\t\t\t135deg,\n\t\t\t\t\trgba(34, 197, 94, 0.25),\n\t\t\t\t\trgba(34, 197, 94, 0.15),\n\t\t\t\t\trgba(34, 197, 94, 0.08)\n\t\t\t\t);\n\t\t\t\tborder: none;\n\t\t\t\tborder-left: 2px solid rgb(34, 197, 94);\n\t\t\t\tbox-shadow:\n\t\t\t\t\t0 4px 12px rgba(34, 197, 94, 0.3),\n\t\t\t\t\tinset 0 1px 0 rgba(255, 255, 255, 0.2),\n\t\t\t\t\t0 0 20px rgba(34, 197, 94, 0.1);\n\t\t\t\tfont-weight: 600;\n\t\t\t\tcolor: rgb(220, 252, 231);\n\t\t\t\tpadding: 10px 12px;\n\t\t\t\tmargin-bottom: 8px;\n\t\t\t\tborder-radius: 8px;\n\t\t\t\tposition: relative;\n\t\t\t\toverflow: hidden;\n\n\t\t\t\t&::before {\n\t\t\t\t\tbackground: linear-gradient(90deg, transparent, rgba(34, 197, 94, 0.4), transparent);\n\t\t\t\t}\n\n\t\t\t\t&::after {\n\t\t\t\t\tcontent: '';\n\t\t\t\t\tposition: absolute;\n\t\t\t\t\ttop: 0;\n\t\t\t\t\tleft: -100%;\n\t\t\t\t\twidth: 100%;\n\t\t\t\t\theight: 100%;\n\t\t\t\t\tbackground: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);\n\t\t\t\t\tanimation: shimmer 2s ease-in-out infinite;\n\t\t\t\t}\n\n\t\t\t\t.historyContent {\n\t\t\t\t\t.statusIcon {\n\t\t\t\t\t\tfont-size: 16px;\n\t\t\t\t\t\tanimation: celebrate 0.8s ease-in-out;\n\t\t\t\t\t\tfilter: drop-shadow(0 2px 4px rgba(34, 197, 94, 0.5));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* 突出显示 done 失败结果 */\n\t\t\t&.doneError {\n\t\t\t\tbackground: linear-gradient(\n\t\t\t\t\t135deg,\n\t\t\t\t\trgba(239, 68, 68, 0.25),\n\t\t\t\t\trgba(239, 68, 68, 0.15),\n\t\t\t\t\trgba(239, 68, 68, 0.08)\n\t\t\t\t);\n\t\t\t\tborder: none;\n\t\t\t\tborder-left: 2px solid rgb(239, 68, 68);\n\t\t\t\tbox-shadow:\n\t\t\t\t\t0 4px 12px rgba(239, 68, 68, 0.3),\n\t\t\t\t\tinset 0 1px 0 rgba(255, 255, 255, 0.2),\n\t\t\t\t\t0 0 20px rgba(239, 68, 68, 0.1);\n\t\t\t\tfont-weight: 600;\n\t\t\t\tcolor: rgb(254, 226, 226);\n\t\t\t\tpadding: 10px 12px;\n\t\t\t\tmargin-bottom: 8px;\n\t\t\t\tborder-radius: 8px;\n\t\t\t\tposition: relative;\n\t\t\t\toverflow: hidden;\n\n\t\t\t\t&::before {\n\t\t\t\t\tbackground: linear-gradient(90deg, transparent, rgba(239, 68, 68, 0.4), transparent);\n\t\t\t\t}\n\n\t\t\t\t.historyContent {\n\t\t\t\t\t.statusIcon {\n\t\t\t\t\t\tfont-size: 16px;\n\t\t\t\t\t\tfilter: drop-shadow(0 2px 4px rgba(239, 68, 68, 0.5));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t.historyContent {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tgap: 8px;\n\n\t\t\t\tword-break: break-all;\n\t\t\t\twhite-space: pre-wrap;\n\n\t\t\t\t/* overflow-x: auto; */\n\n\t\t\t\t.statusIcon {\n\t\t\t\t\tfont-size: 12px;\n\t\t\t\t\tflex-shrink: 0;\n\t\t\t\t\tline-height: 1;\n\t\t\t\t\ttransition: all 0.3s ease;\n\t\t\t\t}\n\n\t\t\t\t.reflectionLines {\n\t\t\t\t\tdisplay: flex;\n\t\t\t\t\tflex-direction: column;\n\t\t\t\t\tgap: 4px;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t.historyMeta {\n\t\t\t\tfont-size: 10px;\n\t\t\t\tcolor: rgba(255, 255, 255, 0.6);\n\t\t\t\t/* color: rgb(61, 61, 61); */\n\t\t\t\tmargin-top: 8px;\n\t\t\t\tline-height: 1;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/* 动画关键帧 - 更快的闪烁 */\n@keyframes pulse {\n\t0%,\n\t100% {\n\t\topacity: 1;\n\t\ttransform: scale(1);\n\t}\n\t50% {\n\t\topacity: 0.4;\n\t\ttransform: scale(1.3);\n\t}\n}\n\n/* 重试动画 - 旋转脉冲 */\n@keyframes retryPulse {\n\t0%,\n\t100% {\n\t\topacity: 1;\n\t\ttransform: scale(1) rotate(0deg);\n\t}\n\t25% {\n\t\topacity: 0.6;\n\t\ttransform: scale(1.2) rotate(90deg);\n\t}\n\t50% {\n\t\topacity: 0.8;\n\t\ttransform: scale(1.1) rotate(180deg);\n\t}\n\t75% {\n\t\topacity: 0.6;\n\t\ttransform: scale(1.2) rotate(270deg);\n\t}\n}\n\n/* 庆祝动画 */\n@keyframes celebrate {\n\t0%,\n\t100% {\n\t\ttransform: scale(1);\n\t}\n\t25% {\n\t\ttransform: scale(1.2) rotate(-5deg);\n\t}\n\t75% {\n\t\ttransform: scale(1.2) rotate(5deg);\n\t}\n}\n\n/* done 卡片的光泽效果 */\n@keyframes shimmer {\n\t0% {\n\t\tleft: -100%;\n\t}\n\t100% {\n\t\tleft: 100%;\n\t}\n}\n\n/* 输入区域样式 */\n.inputSectionWrapper {\n\twidth: 100%;\n\tmargin: 0 auto;\n\n\toverflow: hidden;\n\tmax-height: 0; /* 由 JS 同步为真实内容高度(含附件条、错误提示),见 #syncInputHeight */\n\n\ttransition: max-height 0.2s;\n\n\t/* 与 header 保持同色调,让面板看起来是一体的深色玻璃卡片 */\n\tbackground: rgba(0, 0, 0, 0.4);\n\tbackdrop-filter: blur(10px);\n\n\tborder-bottom-left-radius: calc(var(--border-radius) + 4px);\n\tborder-bottom-right-radius: calc(var(--border-radius) + 4px);\n\n\t&.hidden {\n\t\tmax-height: 0;\n\t}\n\n\t.inputSection {\n\t\tposition: relative;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\theight: var(--input-height);\n\t\tpadding: 8px; /* 与上方状态条 padding 保持一致 */\n\n\t\t.attachButton {\n\t\t\tposition: absolute;\n\t\t\tright: 14px;\n\t\t\tbottom: 12px;\n\t\t\tz-index: 1;\n\t\t\twidth: 26px;\n\t\t\theight: 26px;\n\t\t\tborder: none;\n\t\t\tborder-radius: 4px;\n\t\t\tbackground: transparent;\n\t\t\tcolor: rgba(255, 255, 255, 0.85);\n\t\t\tfont-size: 13px;\n\t\t\tline-height: 1;\n\t\t\tcursor: pointer;\n\t\t}\n\n\t\t.taskInput {\n\t\t\tflex: 1;\n\t\t\theight: 100%;\n\t\t\tbackground: rgba(255, 255, 255, 0.08);\n\t\t\tborder: 1px solid rgba(255, 255, 255, 0.15);\n\t\t\tborder-radius: 12px;\n\t\t\tpadding: 8px 40px 8px 14px; /* 右侧留白避免文字与悬浮的附件按钮重叠 */\n\t\t\tcolor: rgb(240, 240, 245);\n\t\t\tfont-family: inherit;\n\t\t\tfont-size: 14px;\n\t\t\tline-height: 1.4;\n\t\t\toutline: none;\n\t\t\tresize: none;\n\t\t\toverflow-y: auto;\n\t\t\ttransition: all 0.2s ease;\n\n\t\t\t&::placeholder {\n\t\t\t\tcolor: rgba(255, 255, 255, 0.45);\n\t\t\t}\n\n\t\t\t&:focus {\n\t\t\t\tbackground: rgba(255, 255, 255, 0.14);\n\t\t\t\tborder-color: rgba(57, 182, 255, 0.6);\n\t\t\t\tbox-shadow: 0 0 0 2px rgba(57, 182, 255, 0.2);\n\t\t\t}\n\t\t}\n\t}\n\n\t.attachmentsBar {\n\t\tdisplay: flex;\n\t\tflex-wrap: wrap;\n\t\tgap: 6px;\n\t\tpadding: 8px 8px 0;\n\n\t\t&:empty {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n\n\t.attachmentChip {\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tgap: 4px;\n\t\tmax-width: 160px;\n\t\tpadding: 3px 4px 3px 8px;\n\t\tborder-radius: 10px;\n\t\tbackground: rgba(255, 255, 255, 0.1);\n\t\tcolor: rgba(255, 255, 255, 0.85);\n\t\tfont-size: 12px;\n\t\twhite-space: nowrap;\n\n\t\tspan {\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t}\n\t}\n\n\t.attachmentChipRemove {\n\t\tflex-shrink: 0;\n\t\tborder: none;\n\t\tbackground: transparent;\n\t\tcolor: rgba(255, 255, 255, 0.6);\n\t\tcursor: pointer;\n\t\tfont-size: 13px;\n\t\tline-height: 1;\n\t\tpadding: 0 4px;\n\n\t\t&:hover {\n\t\t\tcolor: rgba(255, 255, 255, 0.95);\n\t\t}\n\t}\n\n\t.attachError {\n\t\tpadding: 4px 8px 0;\n\t\tcolor: rgb(255, 138, 138);\n\t\tfont-size: 12px;\n\n\t\t&:empty {\n\t\t\tdisplay: none;\n\t\t\tpadding: 0;\n\t\t}\n\t}\n\n\t.fileInput {\n\t\tdisplay: none;\n\t}\n}\n","/**\n * UI 的卡片 HTML 生成工具\n */\nimport { escapeHtml } from './utils'\n\nimport styles from './styles/UI.module.css'\n\n// 卡片类型:default(默认)/ input(输入)/ output(输出)/ question(提问)/ observation(观察)/ doneSuccess(成功)/ doneError(失败)\ntype CardType =\n\t'default' | 'input' | 'output' | 'question' | 'observation' | 'doneSuccess' | 'doneError'\n\n// 卡片渲染选项\ninterface CardOptions {\n\ticon: string // 图标(emoji)\n\tcontent: string | string[] // 卡片内容,字符串或反思行数组\n\tmeta?: string // 元信息(如步骤号)\n\ttype?: CardType // 卡片类型,用于选择对应样式\n}\n\n/** 创建单条历史卡片 */\nexport function createCard({ icon, content, meta, type }: CardOptions): string {\n\tconst typeClass = type ? styles[type] : ''\n\tconst contentHtml = Array.isArray(content)\n\t\t? `<div class=\"${styles.reflectionLines}\">${content.map((line) => `<span>${escapeHtml(line)}</span>`).join('')}</div>`\n\t\t: `<span>${escapeHtml(content)}</span>`\n\n\treturn `\n\t\t<div class=\"${styles.historyItem} ${typeClass}\">\n\t\t\t<div class=\"${styles.historyContent}\">\n\t\t\t\t<span class=\"${styles.statusIcon}\">${icon}</span>\n\t\t\t\t${contentHtml}\n\t\t\t</div>\n\t\t\t${meta ? `<div class=\"${styles.historyMeta}\">${meta}</div>` : ''}\n\t\t</div>\n\t`\n}\n\n/** 从反思对象生成反思行 */\nexport function createReflectionLines(reflection: {\n\tevaluation_previous_goal?: string // 对上一目标的评估\n\tmemory?: string // 记忆信息\n\tnext_goal?: string // 下一步目标\n}): string[] {\n\tconst lines: string[] = []\n\tif (reflection.evaluation_previous_goal) {\n\t\tlines.push(`🔍 ${reflection.evaluation_previous_goal}`)\n\t}\n\tif (reflection.memory) {\n\t\tlines.push(`💾 ${reflection.memory}`)\n\t}\n\tif (reflection.next_goal) {\n\t\tlines.push(`🎯 ${reflection.next_goal}`)\n\t}\n\treturn lines\n}\n","import { I18n, type SupportedLanguage } from './i18n'\nimport { truncate, escapeHtml } from './utils'\nimport { createCard, createReflectionLines } from './cards'\nimport type { AgentActivity, Attachment, UIAdapter } from './types'\n\nimport styles from './styles/UI.module.css'\n\nexport type { Attachment, UIAdapter } from './types'\n\n/** 单个附件最大体积(5MB) */\nconst MAX_ATTACHMENT_SIZE = 5 * 1024 * 1024\n/** 单次任务最多允许携带的附件数量 */\nconst MAX_ATTACHMENTS = 5\n/** 支持的文本类附件扩展名(图片按 MIME 类型单独判断) */\nconst TEXT_ATTACHMENT_EXTENSIONS = ['.txt', '.md', '.json', '.csv']\n\n/**\n * UI 配置\n */\nexport interface UIConfig {\n\tlanguage?: SupportedLanguage // 界面语言,可选\n\t/**\n\t * 任务完成后是否提示输入下一个任务\n\t * @default true\n\t */\n\tpromptForNextTask?: boolean\n}\n\n/**\n * Agent 控制 UI\n *\n * 架构:\n * - 历史列表:直接从 agent.history 渲染(历史事件)\n * - 头部栏:展示活动事件(瞬时状态)和 agent 状态\n *\n * 这种分离保证了数据一致性 - 历史是已完成内容的唯一数据源,\n * 而活动状态展示的是当前正在发生的事情。\n */\nexport class UI {\n\t#wrapper: HTMLElement // 整体容器\n\t#indicator: HTMLElement // 状态指示器(圆点)\n\t#statusText: HTMLElement // 状态文本\n\t#historySectionWrapper: HTMLElement // 历史区域外层容器(控制 max-height 动画)\n\t#historySection: HTMLElement // 历史卡片列表容器\n\t#expandButton: HTMLElement // 展开/收起按钮\n\t#actionButton: HTMLElement // 动作按钮(运行中为停止,空闲为关闭)\n\t#inputSection: HTMLElement // 输入区域容器\n\t#taskInput: HTMLTextAreaElement // 任务输入框\n\t#attachButton: HTMLElement // 附件按钮(📎)\n\t#fileInput: HTMLInputElement // 隐藏的文件选择输入框\n\t#attachmentsBar: HTMLElement // 已选附件的预览条\n\t#attachError: HTMLElement // 附件校验错误提示\n\n\t#agent: UIAdapter // 绑定的 agent 适配器\n\t#config: UIConfig // 用户配置\n\t#isExpanded = false // 历史区是否展开\n\t#i18n: I18n // 国际化实例\n\t#userAnswerResolver: ((input: string | { text: string; attachments?: Attachment[] }) => void) | null = null // 用户回答的 resolve 回调\n\t#userAnswerRejecter: ((reason: DOMException) => void) | null = null // 用户回答的 reject 回调\n\t#userAnswerSignal: AbortSignal | null = null // 用户回答的取消信号\n\t#userAnswerAbortHandler: (() => void) | null = null // 取消信号触发时的处理函数\n\t#isWaitingForUserAnswer: boolean = false // 是否正在等待用户回答\n\t#pendingAttachments: Attachment[] = [] // 当前已选中、待随任务一起提交的附件\n\t#lastSubmission: { task: string; attachments: Attachment[] } | null = null // 上一次成功提交的任务内容,用于 ↑ 键回填\n\t#headerUpdateTimer: ReturnType<typeof setInterval> | null = null // 头部更新循环的定时器\n\t#timers = new Set<ReturnType<typeof setTimeout>>() // 内部延时定时器集合\n\t#pendingHeaderText: string | null = null // 待显示的头部文本\n\t#isAnimating = false // 是否正在执行文本切换动画\n\t#disposed = false // 是否已销毁\n\n\t// 事件处理器(绑定后用于移除)\n\t#onStatusChange = () => this.#handleStatusChange() // 状态变化事件处理\n\t#onHistoryChange = () => this.#handleHistoryChange() // 历史变化事件处理\n\t#onActivity = (e: Event) => this.#handleActivity((e as CustomEvent<AgentActivity>).detail) // 活动事件处理\n\t#onAgentDispose = () => this.dispose() // agent 销毁事件处理\n\t#askUserCallback = (question: string, options?: { signal: AbortSignal }) =>\n\t\t// 转发给内部 askUser 的回调\n\t\tthis.#askUser(question, options?.signal)\n\n\t/** 获取 UI 的根容器元素 */\n\tget wrapper(): HTMLElement {\n\t\treturn this.#wrapper\n\t}\n\n\t/**\n\t * 创建绑定到某个 agent 的 UI\n\t * @param agent - 实现 UIAdapter 的 Agent 实例\n\t * @param config - 可选的 UI 配置\n\t */\n\tconstructor(agent: UIAdapter, config: UIConfig = {}) {\n\t\tthis.#agent = agent\n\t\tthis.#config = config\n\t\tthis.#i18n = new I18n(config.language ?? 'zh-CN')\n\n\t\t// 在 agent 上设置 askUser 回调\n\t\tthis.#agent.onAskUser = this.#askUserCallback\n\n\t\t// 创建 UI 元素\n\t\tthis.#wrapper = this.#createWrapper()\n\t\tthis.#indicator = this.#wrapper.querySelector(`.${styles.indicator}`)!\n\t\tthis.#statusText = this.#wrapper.querySelector(`.${styles.statusText}`)!\n\t\tthis.#historySectionWrapper = this.#wrapper.querySelector(`.${styles.historySectionWrapper}`)!\n\t\tthis.#historySection = this.#wrapper.querySelector(`.${styles.historySection}`)!\n\t\tthis.#expandButton = this.#wrapper.querySelector(`.${styles.expandButton}`)!\n\t\tthis.#actionButton = this.#wrapper.querySelector(`.${styles.stopButton}`)!\n\t\tthis.#inputSection = this.#wrapper.querySelector(`.${styles.inputSectionWrapper}`)!\n\t\tthis.#taskInput = this.#wrapper.querySelector(`.${styles.taskInput}`)!\n\t\tthis.#attachButton = this.#wrapper.querySelector(`.${styles.attachButton}`)!\n\t\tthis.#fileInput = this.#wrapper.querySelector(`.${styles.fileInput}`)!\n\t\tthis.#attachmentsBar = this.#wrapper.querySelector(`.${styles.attachmentsBar}`)!\n\t\tthis.#attachError = this.#wrapper.querySelector(`.${styles.attachError}`)!\n\n\t\t// 监听 agent 事件\n\t\tthis.#agent.addEventListener('statuschange', this.#onStatusChange)\n\t\tthis.#agent.addEventListener('historychange', this.#onHistoryChange)\n\t\tthis.#agent.addEventListener('activity', this.#onActivity)\n\t\tthis.#agent.addEventListener('dispose', this.#onAgentDispose)\n\n\t\tthis.#setupEventListeners()\n\t\tthis.#startHeaderUpdateLoop()\n\n\t\tthis.#showInputArea()\n\n\t\tthis.hide() // 初始状态为隐藏\n\t}\n\n\t// ========== Agent 事件处理 ==========\n\n\t/** 处理 agent 状态变化 */\n\t#handleStatusChange(): void {\n\t\tconst status = this.#agent.status\n\n\t\t// 将 agent 状态映射为 UI 指示器。`completed` 运行但其结果\n\t\t// 上报失败时显示为 error;其他状态映射到各自的指示器。\n\t\tconst failed = status === 'completed' && this.#agent.lastResult?.success === false\n\t\tthis.#updateStatusIndicator(failed ? 'error' : status)\n\n\t\t// 变换动作按钮:运行中 = 停止(■),未运行 = 关闭(X)\n\t\tif (status === 'running') {\n\t\t\tthis.#actionButton.textContent = '■'\n\t\t\tthis.#actionButton.title = this.#i18n.t('ui.stop')\n\t\t} else {\n\t\t\tthis.#actionButton.textContent = 'X'\n\t\t\tthis.#actionButton.title = this.#i18n.t('ui.close')\n\t\t}\n\n\t\t// 根据状态显示/隐藏\n\t\tif (status === 'running') {\n\t\t\tthis.show()\n\t\t\tthis.#hideInputArea() // 运行期间隐藏输入框\n\t\t\tif (!this.#isExpanded) {\n\t\t\t\tthis.#expand() // 任务开始时展开历史,方便观察执行过程\n\t\t\t}\n\t\t}\n\n\t\t// 处理完成状态\n\t\tif (status === 'completed' || status === 'error' || status === 'stopped') {\n\t\t\tif (!this.#isExpanded) {\n\t\t\t\tthis.#expand()\n\t\t\t}\n\t\t\tif (this.#shouldShowInputArea()) {\n\t\t\t\tthis.#showInputArea()\n\t\t\t}\n\t\t}\n\t}\n\n\t/** 处理 agent 历史变化 - 根据 agent.history 重新渲染历史列表 */\n\t#handleHistoryChange(): void {\n\t\tthis.#renderHistory()\n\t}\n\n\t/**\n\t * 处理 agent 活动 - 用于即时 UI 反馈的瞬时状态\n\t * 活动事件不会持久化到历史中,仅用于头部栏更新\n\t */\n\t#handleActivity(activity: AgentActivity): void {\n\t\tswitch (activity.type) {\n\t\t\tcase 'thinking':\n\t\t\t\tthis.#pendingHeaderText = this.#i18n.t('ui.thinking')\n\t\t\t\tthis.#updateStatusIndicator('thinking')\n\t\t\t\tbreak\n\n\t\t\tcase 'executing':\n\t\t\t\tthis.#pendingHeaderText = this.#getToolExecutingText(activity.tool, activity.input)\n\t\t\t\tthis.#updateStatusIndicator('executing')\n\t\t\t\tbreak\n\n\t\t\tcase 'executed':\n\t\t\t\tthis.#pendingHeaderText = truncate(activity.output, 50)\n\t\t\t\tbreak\n\n\t\t\tcase 'retrying':\n\t\t\t\tthis.#pendingHeaderText = `Retrying (${activity.attempt}/${activity.maxAttempts})`\n\t\t\t\tthis.#updateStatusIndicator('retrying')\n\t\t\t\tbreak\n\n\t\t\tcase 'error':\n\t\t\t\tthis.#pendingHeaderText = truncate(activity.message, 50)\n\t\t\t\tthis.#updateStatusIndicator('error')\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t/**\n\t * 请求用户输入(内部方法,由 agent 通过 onAskUser 调用)。\n\t * 当 `signal` 中止(任务被停止或销毁)时拒绝该请求,\n\t * 并清理问题卡片和挂起状态,以便 agent 循环能够收尾。\n\t */\n\t#askUser(question: string, signal?: AbortSignal): Promise<string | { text: string; attachments?: Attachment[] }> {\n\t\tif (this.#disposed) return Promise.reject(this.#createAbortError())\n\t\tthis.#rejectPendingQuestion()\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\t// 设置\"等待用户回答\"状态\n\t\t\tthis.#isWaitingForUserAnswer = true\n\t\t\tthis.#userAnswerResolver = resolve\n\t\t\tthis.#userAnswerRejecter = reject\n\n\t\t\t// 展开历史 UI\n\t\t\tif (!this.#isExpanded) {\n\t\t\t\tthis.#expand()\n\t\t\t}\n\n\t\t\t// 添加临时问题卡片,让用户能看到完整的问题\n\t\t\tconst tempCard = document.createElement('div')\n\t\t\ttempCard.innerHTML = createCard({\n\t\t\t\ticon: '❓',\n\t\t\t\tcontent: `Question: ${question}`,\n\t\t\t\ttype: 'question',\n\t\t\t})\n\t\t\tconst cardElement = tempCard.firstElementChild as HTMLElement\n\t\t\tcardElement.setAttribute('data-temp-card', 'true')\n\t\t\tthis.#historySection.appendChild(cardElement)\n\t\t\tthis.#scrollToBottom()\n\t\t\tthis.#syncHistoryHeight()\n\n\t\t\tthis.#showInputArea(this.#i18n.t('ui.userAnswerPrompt'))\n\n\t\t\tif (signal) {\n\t\t\t\tthis.#userAnswerSignal = signal\n\t\t\t\tthis.#userAnswerAbortHandler = () => this.#rejectPendingQuestion()\n\t\t\t\tif (signal.aborted) {\n\t\t\t\t\tthis.#rejectPendingQuestion()\n\t\t\t\t} else {\n\t\t\t\t\tsignal.addEventListener('abort', this.#userAnswerAbortHandler, { once: true })\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\t/** 创建标准的中止异常(AbortError) */\n\t#createAbortError(): DOMException {\n\t\treturn new DOMException('The operation was aborted.', 'AbortError')\n\t}\n\n\t/** 清空用户回答相关状态并移除临时卡片 */\n\t#clearQuestionState(): void {\n\t\tif (this.#userAnswerSignal && this.#userAnswerAbortHandler) {\n\t\t\tthis.#userAnswerSignal.removeEventListener('abort', this.#userAnswerAbortHandler)\n\t\t}\n\t\tthis.#userAnswerSignal = null\n\t\tthis.#userAnswerAbortHandler = null\n\t\tthis.#userAnswerResolver = null\n\t\tthis.#userAnswerRejecter = null\n\t\tthis.#isWaitingForUserAnswer = false\n\t\tthis.#removeTempCards()\n\t}\n\n\t/** 拒绝挂起的用户问题(存在待回答问题时) */\n\t#rejectPendingQuestion(): void {\n\t\tconst reject = this.#userAnswerRejecter\n\t\tif (!reject) return\n\t\tthis.#clearQuestionState()\n\t\treject(this.#createAbortError())\n\t}\n\n\t/** 移除临时问题卡片(为安全起见仅移除直接子节点) */\n\t#removeTempCards(): void {\n\t\tArray.from(this.#historySection.children).forEach((child) => {\n\t\t\tif (child.getAttribute('data-temp-card') === 'true') {\n\t\t\t\tchild.remove()\n\t\t\t}\n\t\t})\n\t\tthis.#syncHistoryHeight()\n\t}\n\n\t// ========== 公共控制方法 ==========\n\n\t/** 显示 UI(淡入 + 平移效果) */\n\tshow(): void {\n\t\tthis.wrapper.style.display = 'block'\n\t\tvoid this.wrapper.offsetHeight\n\t\tthis.wrapper.style.opacity = '1'\n\t\tthis.wrapper.style.transform = 'translateX(-50%) translateY(0)'\n\t}\n\n\t/** 隐藏 UI(淡出 + 下移效果) */\n\thide(): void {\n\t\tthis.wrapper.style.opacity = '0'\n\t\tthis.wrapper.style.transform = 'translateX(-50%) translateY(20px)'\n\t\tthis.wrapper.style.display = 'none'\n\t}\n\n\t/** 重置 UI 状态(拒绝挂起问题、清空状态文本、收起历史并显示输入框) */\n\treset(): void {\n\t\tthis.#rejectPendingQuestion()\n\t\tthis.#statusText.textContent = this.#i18n.t('ui.ready')\n\t\tthis.#updateStatusIndicator('thinking')\n\t\tthis.#renderHistory()\n\t\tthis.#collapse()\n\t\tthis.#pendingAttachments = []\n\t\tthis.#renderAttachments()\n\t\tthis.#setAttachError('')\n\t\t// 显示输入区域\n\t\tthis.#showInputArea()\n\t}\n\n\t/** 展开历史弹窗(公开方法) */\n\texpand(): void {\n\t\tthis.#expand()\n\t}\n\n\t/** 收起历史弹窗(公开方法) */\n\tcollapse(): void {\n\t\tthis.#collapse()\n\t}\n\n\t/**\n\t * 销毁 UI 并清理事件监听器\n\t */\n\tdispose(): void {\n\t\tif (this.#disposed) return\n\t\tthis.#disposed = true\n\n\t\t// 移除 agent 事件监听器\n\t\tthis.#agent.removeEventListener('statuschange', this.#onStatusChange)\n\t\tthis.#agent.removeEventListener('historychange', this.#onHistoryChange)\n\t\tthis.#agent.removeEventListener('activity', this.#onActivity)\n\t\tthis.#agent.removeEventListener('dispose', this.#onAgentDispose)\n\t\tif (this.#agent.onAskUser === this.#askUserCallback) {\n\t\t\tthis.#agent.onAskUser = undefined\n\t\t}\n\n\t\t// 清理 UI\n\t\tthis.#rejectPendingQuestion()\n\t\tthis.#clearQuestionState()\n\t\tthis.#stopHeaderUpdateLoop()\n\t\tfor (const timer of this.#timers) clearTimeout(timer)\n\t\tthis.#timers.clear()\n\t\tthis.wrapper.remove()\n\t}\n\n\t// ========== 私有方法 ==========\n\n\t/** 根据工具名称和参数生成\"正在执行\"提示文本 */\n\t#getToolExecutingText(toolName: string, args: unknown): string {\n\t\tconst a = args as Record<string, string | number>\n\t\tswitch (toolName) {\n\t\t\tcase 'click_element_by_index':\n\t\t\t\treturn this.#i18n.t('ui.tools.clicking', { index: a.index })\n\t\t\tcase 'input_text':\n\t\t\t\treturn this.#i18n.t('ui.tools.inputting', { index: a.index })\n\t\t\tcase 'select_dropdown_option':\n\t\t\t\treturn this.#i18n.t('ui.tools.selecting', { text: a.text })\n\t\t\tcase 'scroll':\n\t\t\t\treturn this.#i18n.t('ui.tools.scrolling')\n\t\t\tcase 'wait':\n\t\t\t\treturn this.#i18n.t('ui.tools.waiting', { seconds: a.seconds })\n\t\t\tcase 'ask_user':\n\t\t\t\treturn this.#i18n.t('ui.tools.askingUser')\n\t\t\tcase 'done':\n\t\t\t\treturn this.#i18n.t('ui.tools.done')\n\t\t\tdefault:\n\t\t\t\treturn this.#i18n.t('ui.tools.executing', { toolName })\n\t\t}\n\t}\n\n\t/**\n\t * 动作按钮处理:运行中则停止,空闲则关闭(销毁)\n\t */\n\t#handleActionButton(): void {\n\t\tif (this.#agent.status === 'running') {\n\t\t\tthis.#agent.stop()\n\t\t} else {\n\t\t\tthis.#agent.dispose()\n\t\t}\n\t}\n\n\t/**\n\t * 提交任务\n\t */\n\t#submitTask() {\n\t\tconst input = this.#taskInput.value.trim()\n\t\tif (!input) return\n\n\t\t// 隐藏输入区域\n\t\tthis.#hideInputArea()\n\n\t\tif (this.#isWaitingForUserAnswer) {\n\t\t\t// 处理用户输入模式\n\t\t\tthis.#handleUserAnswer(input)\n\t\t\treturn\n\t\t}\n\n\t\tconst attachments = this.#pendingAttachments\n\t\tthis.#lastSubmission = { task: input, attachments } // 记录本次提交,供 ↑ 键回填\n\t\tthis.#pendingAttachments = []\n\t\tthis.#renderAttachments()\n\t\tthis.#setAttachError('')\n\n\t\tconst onExecuteError = (error: unknown) => {\n\t\t\tif ((error as { name?: string })?.name === 'AbortError') return\n\t\t\tconsole.error('[UI] Failed to execute task:', error)\n\t\t\tif (!this.#disposed) this.#showInputArea()\n\t\t}\n\n\t\t// 通过 agent 执行任务(仅当存在附件时才多传一个参数,保持无附件场景的调用签名不变)\n\t\tconst promise =\n\t\t\tattachments.length > 0 ? this.#agent.execute(input, attachments) : this.#agent.execute(input)\n\t\tvoid promise.catch(onExecuteError)\n\t}\n\n\t/** 将上一次提交的文本和附件回填到输入框,供 ↑ 键触发 */\n\t#restoreLastSubmission(): void {\n\t\tconst last = this.#lastSubmission\n\t\tif (!last) return\n\t\tthis.#taskInput.value = last.task\n\t\tthis.#pendingAttachments = [...last.attachments]\n\t\tthis.#renderAttachments()\n\t}\n\n\t/**\n\t * 处理用户回答;若回答时附带了附件,一并传给 resolve 回调\n\t */\n\t#handleUserAnswer(input: string): void {\n\t\tthis.#removeTempCards()\n\n\t\tconst attachments = this.#pendingAttachments\n\t\tthis.#pendingAttachments = []\n\t\tthis.#renderAttachments()\n\n\t\t// 调用 resolve 回调返回用户输入\n\t\tconst resolve = this.#userAnswerResolver\n\t\tthis.#clearQuestionState()\n\t\tresolve?.(attachments.length > 0 ? { text: input, attachments } : input)\n\t}\n\n\t/**\n\t * 显示输入区域\n\t */\n\t#showInputArea(placeholder?: string): void {\n\t\t// 清空输入框\n\t\tthis.#taskInput.value = ''\n\t\tthis.#taskInput.placeholder = placeholder || this.#i18n.t('ui.taskInput')\n\t\tthis.#inputSection.classList.remove(styles.hidden)\n\t\tthis.#syncInputHeight()\n\t\t// 聚焦到输入框\n\t\tthis.#setTimer(() => {\n\t\t\tthis.#taskInput.focus()\n\t\t}, 100)\n\t}\n\n\t/**\n\t * 隐藏输入区域\n\t */\n\t#hideInputArea(): void {\n\t\tthis.#inputSection.classList.add(styles.hidden)\n\t\tthis.#inputSection.style.maxHeight = '0px'\n\t}\n\n\t/** 将输入区域外层容器的 max-height 同步为当前真实内容高度(含附件条、错误提示) */\n\t#syncInputHeight(): void {\n\t\tif (this.#inputSection.classList.contains(styles.hidden)) return\n\t\tthis.#inputSection.style.maxHeight = `${this.#inputSection.scrollHeight}px`\n\t}\n\n\t/** 设置(或清空)附件校验错误提示,并同步输入区域高度 */\n\t#setAttachError(message: string): void {\n\t\tthis.#attachError.textContent = message\n\t\tthis.#syncInputHeight()\n\t}\n\n\t/** 判断文件是否为支持的附件类型:图片,或指定扩展名的文本文件 */\n\t#isSupportedAttachment(file: File): boolean {\n\t\tif (file.type.startsWith('image/')) return true\n\t\tconst name = file.name.toLowerCase()\n\t\treturn TEXT_ATTACHMENT_EXTENSIONS.some((ext) => name.endsWith(ext))\n\t}\n\n\t/** 将文件读取为 base64 data URL */\n\t#readFileAsDataUrl(file: File): Promise<string> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst reader = new FileReader()\n\t\t\treader.onload = () => resolve(reader.result as string)\n\t\t\treader.onerror = () => reject(reader.error)\n\t\t\treader.readAsDataURL(file)\n\t\t})\n\t}\n\n\t/**\n\t * 校验并读取用户选中的文件,追加为待提交附件。\n\t * 任一文件超出数量/大小/类型限制时,拒绝本次整批选择并提示错误。\n\t */\n\tasync #handleFileSelection(files: FileList): Promise<void> {\n\t\tconst incoming = Array.from(files)\n\n\t\tif (this.#pendingAttachments.length + incoming.length > MAX_ATTACHMENTS) {\n\t\t\tthis.#setAttachError(this.#i18n.t('ui.attachTooMany', { max: MAX_ATTACHMENTS }))\n\t\t\treturn\n\t\t}\n\n\t\tfor (const file of incoming) {\n\t\t\tif (file.size > MAX_ATTACHMENT_SIZE) {\n\t\t\t\tthis.#setAttachError(\n\t\t\t\t\tthis.#i18n.t('ui.attachTooLarge', {\n\t\t\t\t\t\tname: file.name,\n\t\t\t\t\t\tmaxMB: MAX_ATTACHMENT_SIZE / (1024 * 1024),\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.#isSupportedAttachment(file)) {\n\t\t\t\tthis.#setAttachError(this.#i18n.t('ui.attachUnsupportedType', { name: file.name }))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.#setAttachError('')\n\t\tconst dataUrls = await Promise.all(incoming.map((file) => this.#readFileAsDataUrl(file)))\n\t\tincoming.forEach((file, index) => {\n\t\t\tthis.#pendingAttachments.push({\n\t\t\t\tname: file.name,\n\t\t\t\tmimeType: file.type,\n\t\t\t\tdataUrl: dataUrls[index],\n\t\t\t})\n\t\t})\n\n\t\tthis.#renderAttachments()\n\t}\n\n\t/** 重新渲染已选附件的预览条 */\n\t#renderAttachments(): void {\n\t\tthis.#attachmentsBar.innerHTML = this.#pendingAttachments\n\t\t\t.map((attachment, index) => {\n\t\t\t\tconst icon = attachment.mimeType.startsWith('image/') ? '🖼️' : '📄'\n\t\t\t\treturn `\n\t\t\t\t\t<span class=\"${styles.attachmentChip}\">\n\t\t\t\t\t\t<span>${icon} ${escapeHtml(truncate(attachment.name, 20))}</span>\n\t\t\t\t\t\t<button type=\"button\" class=\"${styles.attachmentChipRemove}\" data-remove-index=\"${index}\">×</button>\n\t\t\t\t\t</span>\n\t\t\t\t`\n\t\t\t})\n\t\t\t.join('')\n\t\tthis.#syncInputHeight()\n\t}\n\n\t/** 移除一个已选附件 */\n\t#removeAttachment(index: number): void {\n\t\tthis.#pendingAttachments.splice(index, 1)\n\t\tthis.#renderAttachments()\n\t}\n\n\t/**\n\t * 检查是否应显示输入区域\n\t */\n\t#shouldShowInputArea(): boolean {\n\t\t// 等待用户输入时始终显示输入区域\n\t\tif (this.#isWaitingForUserAnswer) return true\n\n\t\tconst history = this.#agent.history\n\t\tif (history.length === 0) {\n\t\t\treturn true // 初始状态\n\t\t}\n\n\t\tconst status = this.#agent.status\n\t\tconst isTaskEnded = status === 'completed' || status === 'error' || status === 'stopped'\n\n\t\t// 仅当配置允许时,才在任务完成后显示输入区域\n\t\tif (isTaskEnded) {\n\t\t\treturn this.#config.promptForNextTask ?? true\n\t\t}\n\n\t\treturn false\n\t}\n\n\t/** 创建 UI 根元素并挂载到 body,返回根容器 */\n\t#createWrapper(): HTMLElement {\n\t\tconst taskInputMaxLength = 1000\n\t\tconst wrapper = document.createElement('div')\n\t\twrapper.id = 'my-page-agent-runtime_agent-ui'\n\t\twrapper.className = styles.wrapper\n\t\twrapper.setAttribute('data-browser-use-ignore', 'true')\n\t\twrapper.setAttribute('data-page-agent-ignore', 'true')\n\n\t\twrapper.innerHTML = `\n\t\t\t<div class=\"${styles.historySectionWrapper}\">\n\t\t\t\t<div class=\"${styles.historySection}\">\n\t\t\t\t\t<div class=\"${styles.historyItem}\">\n\t\t\t\t\t\t<div class=\"${styles.historyContent}\">\n\t\t\t\t\t\t\t<span class=\"${styles.statusIcon}\">🧠</span>\n\t\t\t\t\t\t\t<span>${this.#i18n.t('ui.waitingPlaceholder')}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"${styles.shell}\">\n\t\t\t\t<div class=\"${styles.background}\"></div>\n\t\t\t\t<div class=\"${styles.shellInner}\">\n\t\t\t\t\t<div class=\"${styles.header}\">\n\t\t\t\t\t\t<div class=\"${styles.statusSection}\">\n\t\t\t\t\t\t\t<div class=\"${styles.indicator} ${styles.thinking}\"></div>\n\t\t\t\t\t\t\t<div class=\"${styles.statusText}\">${this.#i18n.t('ui.ready')}</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"${styles.controls}\">\n\t\t\t\t\t\t\t<button class=\"${styles.controlButton} ${styles.expandButton}\" title=\"${this.#i18n.t('ui.expand')}\">\n\t\t\t\t\t\t\t\t▼\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button class=\"${styles.controlButton} ${styles.stopButton}\" title=\"${this.#i18n.t('ui.close')}\">\n\t\t\t\t\t\t\t\tX\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"${styles.inputSectionWrapper} ${styles.hidden}\">\n\t\t\t\t\t\t<div class=\"${styles.attachmentsBar}\"></div>\n\t\t\t\t\t\t<div class=\"${styles.attachError}\"></div>\n\t\t\t\t\t\t<div class=\"${styles.inputSection}\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"${styles.attachButton}\"\n\t\t\t\t\t\t\t\ttitle=\"${this.#i18n.t('ui.attachButtonTitle')}\"\n\t\t\t\t\t\t\t>📎</button>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"file\"\n\t\t\t\t\t\t\t\tclass=\"${styles.fileInput}\"\n\t\t\t\t\t\t\t\taccept=\"image/*,${TEXT_ATTACHMENT_EXTENSIONS.join(',')}\"\n\t\t\t\t\t\t\t\tmultiple\n\t\t\t\t\t\t\t\thidden\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\t\tclass=\"${styles.taskInput}\"\n\t\t\t\t\t\t\t\tmaxlength=\"${taskInputMaxLength}\"\n\t\t\t\t\t\t\t\trows=\"1\"\n\t\t\t\t\t\t\t></textarea>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t`\n\n\t\tdocument.body.appendChild(wrapper)\n\t\treturn wrapper\n\t}\n\n\t/** 绑定所有 DOM 事件监听器(头部点击、展开按钮、动作按钮、回车提交等) */\n\t#setupEventListeners(): void {\n\t\t// 点击头部区域可展开/收起\n\t\tconst header = this.wrapper.querySelector(`.${styles.header}`)!\n\t\theader.addEventListener('click', (e) => {\n\t\t\t// 点击按钮时不触发展开/收起\n\t\t\tif ((e.target as HTMLElement).closest(`.${styles.controlButton}`)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.#toggle()\n\t\t})\n\n\t\t// 展开按钮\n\t\tthis.#expandButton.addEventListener('click', (e) => {\n\t\t\te.stopPropagation()\n\t\t\tthis.#toggle()\n\t\t})\n\n\t\t// 动作按钮(停止 / 关闭)\n\t\tthis.#actionButton.addEventListener('click', (e) => {\n\t\t\te.stopPropagation()\n\t\t\tthis.#handleActionButton()\n\t\t})\n\n\t\t// 回车提交;Shift+Enter 插入换行(多行输入);按 ↑ 用上一次提交内容覆盖输入框(清空后再填充,而非在原内容后追加)\n\t\tthis.#taskInput.addEventListener('keydown', (e) => {\n\t\t\tif (e.isComposing) return // 忽略输入法组合键\n\t\t\tif (e.key === 'Enter' && !e.shiftKey) {\n\t\t\t\te.preventDefault()\n\t\t\t\tthis.#submitTask()\n\t\t\t} else if (e.key === 'ArrowUp' && !this.#isWaitingForUserAnswer && this.#lastSubmission) {\n\t\t\t\te.preventDefault()\n\t\t\t\tthis.#restoreLastSubmission()\n\t\t\t}\n\t\t})\n\n\t\t// 阻止输入区域点击事件冒泡\n\t\tthis.#inputSection.addEventListener('click', (e) => {\n\t\t\te.stopPropagation()\n\t\t})\n\n\t\t// 附件按钮:点击触发隐藏的文件选择框\n\t\tthis.#attachButton.addEventListener('click', () => {\n\t\t\tthis.#fileInput.click()\n\t\t})\n\n\t\t// 文件选择变化:校验并读取选中的文件\n\t\tthis.#fileInput.addEventListener('change', () => {\n\t\t\tconst files = this.#fileInput.files\n\t\t\tif (files?.length) void this.#handleFileSelection(files)\n\t\t\tthis.#fileInput.value = '' // 清空选择,允许重复选择同一文件\n\t\t})\n\n\t\t// 粘贴截图/图片:剪贴板携带文件时作为附件处理,否则走默认文本粘贴逻辑\n\t\tthis.#taskInput.addEventListener('paste', (e) => {\n\t\t\tconst files = e.clipboardData?.files\n\t\t\tif (!files?.length) return\n\t\t\te.preventDefault()\n\t\t\tvoid this.#handleFileSelection(files)\n\t\t})\n\n\t\t// 点击附件预览条上的删除按钮\n\t\tthis.#attachmentsBar.addEventListener('click', (e) => {\n\t\t\tconst removeButton = (e.target as HTMLElement).closest(`.${styles.attachmentChipRemove}`)\n\t\t\tif (!(removeButton instanceof HTMLElement)) return\n\t\t\tthis.#removeAttachment(Number(removeButton.dataset.removeIndex))\n\t\t})\n\t}\n\n\t/** 在展开/收起状态之间切换 */\n\t#toggle(): void {\n\t\tif (this.#isExpanded) {\n\t\t\tthis.#collapse()\n\t\t} else {\n\t\t\tthis.#expand()\n\t\t}\n\t}\n\n\t/**\n\t * 展开历史弹窗。\n\t *\n\t * `max-height` 由 JS 测量(而非动画到固定的 CSS 上限),因此\n\t * 过渡距离始终匹配真实内容高度 — 否则展开/收起会给人\n\t * 速度不一致的感觉(展开会提前达到真实高度并停滞,\n\t * 而收起每次都按完整上限做动画)。\n\t */\n\t#expand(): void {\n\t\tthis.#isExpanded = true\n\t\tthis.wrapper.classList.add(styles.expanded)\n\t\tthis.#expandButton.textContent = '▲'\n\t\tthis.#syncHistoryHeight()\n\t}\n\n\t/** 收起历史弹窗 */\n\t#collapse(): void {\n\t\tthis.#isExpanded = false\n\t\tthis.wrapper.classList.remove(styles.expanded)\n\t\tthis.#expandButton.textContent = '▼'\n\t\tthis.#historySectionWrapper.style.maxHeight = '0px'\n\t}\n\n\t/** 将历史弹窗的 max-height 同步为当前(已封顶的)内容高度 */\n\t#syncHistoryHeight(): void {\n\t\tif (!this.#isExpanded) return\n\t\tthis.#historySectionWrapper.style.maxHeight = `${this.#historySectionWrapper.scrollHeight}px`\n\t}\n\n\t/**\n\t * 启动周期性的头部更新循环\n\t */\n\t#startHeaderUpdateLoop(): void {\n\t\t// 每 450ms 检查一次(与动画总时长一致)\n\t\tthis.#headerUpdateTimer = setInterval(() => {\n\t\t\tthis.#checkAndUpdateHeader()\n\t\t}, 450)\n\t}\n\n\t/**\n\t * 停止周期性的头部更新循环\n\t */\n\t#stopHeaderUpdateLoop(): void {\n\t\tif (this.#headerUpdateTimer) {\n\t\t\tclearInterval(this.#headerUpdateTimer)\n\t\t\tthis.#headerUpdateTimer = null\n\t\t}\n\t}\n\n\t/** 设置一个受控延时定时器(销毁后自动忽略回调) */\n\t#setTimer(callback: () => void, delay: number): void {\n\t\tconst timer = setTimeout(() => {\n\t\t\tthis.#timers.delete(timer)\n\t\t\tif (!this.#disposed) callback()\n\t\t}, delay)\n\t\tthis.#timers.add(timer)\n\t}\n\n\t/**\n\t * 检查头部是否需要更新,若当前未在动画中则触发动画\n\t */\n\t#checkAndUpdateHeader(): void {\n\t\t// 无待处理文本或当前正在动画中,则跳过\n\t\tif (!this.#pendingHeaderText || this.#isAnimating) {\n\t\t\treturn\n\t\t}\n\n\t\t// 若文本已显示,则清空待处理文本并跳过\n\t\tif (this.#statusText.textContent === this.#pendingHeaderText) {\n\t\t\tthis.#pendingHeaderText = null\n\t\t\treturn\n\t\t}\n\n\t\t// 开始动画\n\t\tconst textToShow = this.#pendingHeaderText\n\t\tthis.#pendingHeaderText = null\n\t\tthis.#animateTextChange(textToShow)\n\t}\n\n\t/**\n\t * 以淡出/淡入效果动画切换文本\n\t */\n\t#animateTextChange(newText: string): void {\n\t\tthis.#isAnimating = true\n\n\t\t// 淡出当前文本\n\t\tthis.#statusText.classList.add(styles.fadeOut)\n\n\t\tthis.#setTimer(() => {\n\t\t\t// 更新文本内容\n\t\t\tthis.#statusText.textContent = newText\n\n\t\t\t// 淡入新文本\n\t\t\tthis.#statusText.classList.remove(styles.fadeOut)\n\t\t\tthis.#statusText.classList.add(styles.fadeIn)\n\n\t\t\tthis.#setTimer(() => {\n\t\t\t\tthis.#statusText.classList.remove(styles.fadeIn)\n\t\t\t\tthis.#isAnimating = false\n\t\t\t}, 300)\n\t\t}, 150) // 淡出动画时长的一半\n\t}\n\n\t/** 更新状态指示器的样式类 */\n\t#updateStatusIndicator(\n\t\ttype:\n\t\t\t| 'idle'\n\t\t\t| 'running'\n\t\t\t| 'thinking'\n\t\t\t| 'executing'\n\t\t\t| 'executed'\n\t\t\t| 'retrying'\n\t\t\t| 'completed'\n\t\t\t| 'error'\n\t\t\t| 'stopped'\n\t): void {\n\t\t// `running` 按 thinking 方式动画;`idle`/`stopped` 使用中性基础样式。\n\t\tconst variant =\n\t\t\ttype === 'running'\n\t\t\t\t? 'thinking'\n\t\t\t\t: type === 'executing'\n\t\t\t\t\t? 'tool_executing'\n\t\t\t\t\t: type === 'retrying'\n\t\t\t\t\t\t? 'retry'\n\t\t\t\t\t\t: type\n\t\tthis.#indicator.className = styles.indicator\n\t\tif (variant !== 'idle' && variant !== 'stopped') {\n\t\t\tthis.#indicator.classList.add(styles[variant])\n\t\t}\n\t}\n\n\t/** 将历史区域滚动到底部 */\n\t#scrollToBottom(): void {\n\t\t// 在下一轮事件循环中执行,确保 DOM 更新完成\n\t\tthis.#setTimer(() => {\n\t\t\tthis.#historySection.scrollTop = this.#historySection.scrollHeight\n\t\t}, 0)\n\t}\n\n\t/**\n\t * 直接从 agent.history 渲染历史\n\t *\n\t * 渲染内容:\n\t * 1. 任务卡片(每个 task_start 事件对应一张,history 跨任务持久保留,因此可展示历次任务)\n\t * 2. 反思卡片(评估、记忆、下一步目标)\n\t * 3. 工具执行及输出\n\t * 4. 观察\n\t */\n\t#renderHistory(): void {\n\t\tconst items: string[] = []\n\n\t\tconst history = this.#agent.history\n\n\t\t// 兼容旧状态:若历史中还没有 task_start 事件(例如尚未产生任何事件),\n\t\t// 用当前 agent.task 兜底渲染一张任务卡片\n\t\tif (history.length === 0 && this.#agent.task) {\n\t\t\titems.push(this.#createTaskCard(this.#agent.task))\n\t\t}\n\n\t\tfor (const event of history) {\n\t\t\titems.push(...this.#createHistoryCards(event))\n\t\t}\n\n\t\tthis.#historySection.innerHTML = items.join('')\n\t\tthis.#scrollToBottom()\n\t\tthis.#syncHistoryHeight()\n\t}\n\n\t/** 生成任务卡片 HTML;若任务附带附件,附加一行文件名列表 */\n\t#createTaskCard(task: string, attachmentNames?: string[]): string {\n\t\tif (!attachmentNames?.length) {\n\t\t\treturn createCard({ icon: '🎯', content: task, type: 'input' })\n\t\t}\n\t\treturn createCard({\n\t\t\ticon: '🎯',\n\t\t\tcontent: [task, this.#i18n.t('ui.attachmentsLabel', { names: attachmentNames.join(', ') })],\n\t\t\ttype: 'input',\n\t\t})\n\t}\n\n\t/** 为一条历史事件生成卡片 */\n\t#createHistoryCards(event: UIAdapter['history'][number]): string[] {\n\t\tconst cards: string[] = []\n\t\tconst meta =\n\t\t\tevent.type === 'step' && event.stepIndex !== undefined\n\t\t\t\t? this.#i18n.t('ui.step', {\n\t\t\t\t\t\tnumber: (event.stepIndex + 1).toString(),\n\t\t\t\t\t})\n\t\t\t\t: undefined\n\n\t\tif (event.type === 'task_start') {\n\t\t\tif (event.task) cards.push(this.#createTaskCard(event.task, event.attachmentNames))\n\t\t} else if (event.type === 'step') {\n\t\t\t// 反思卡片\n\t\t\tif (event.reflection) {\n\t\t\t\tconst lines = createReflectionLines(event.reflection)\n\t\t\t\tif (lines.length > 0) {\n\t\t\t\t\tcards.push(createCard({ icon: '🧠', content: lines, meta }))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 动作卡片\n\t\t\tconst action = event.action\n\t\t\tif (action) {\n\t\t\t\tcards.push(...this.#createActionCards(action, meta))\n\t\t\t}\n\t\t} else if (event.type === 'observation') {\n\t\t\tcards.push(\n\t\t\t\tcreateCard({ icon: '👁️', content: event.content || '', meta, type: 'observation' })\n\t\t\t)\n\t\t} else if (event.type === 'user_takeover') {\n\t\t\tcards.push(createCard({ icon: '👤', content: 'User takeover', meta, type: 'input' }))\n\t\t} else if (event.type === 'retry') {\n\t\t\tconst retryInfo = `${event.message || 'Retrying'} (${event.attempt}/${event.maxAttempts})`\n\t\t\tcards.push(createCard({ icon: '🔄', content: retryInfo, meta, type: 'observation' }))\n\t\t} else if (event.type === 'error') {\n\t\t\tcards.push(\n\t\t\t\tcreateCard({ icon: '❌', content: event.message || 'Error', meta, type: 'observation' })\n\t\t\t)\n\t\t}\n\n\t\treturn cards\n\t}\n\n\t/** 为一个动作生成卡片 */\n\t#createActionCards(\n\t\taction: { name: string; input: unknown; output: string },\n\t\tmeta?: string\n\t): string[] {\n\t\tconst cards: string[] = []\n\n\t\tif (action.name === 'done') {\n\t\t\tconst input = action.input as { text?: string; success?: boolean }\n\t\t\tconst text = input.text || action.output || ''\n\t\t\tif (text) {\n\t\t\t\tcards.push(\n\t\t\t\t\tcreateCard({\n\t\t\t\t\t\ticon: '🤖',\n\t\t\t\t\t\tcontent: text,\n\t\t\t\t\t\tmeta,\n\t\t\t\t\t\ttype: input.success === true ? 'doneSuccess' : 'doneError',\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t}\n\t\t} else if (action.name === 'ask_user') {\n\t\t\tconst input = action.input as { question?: string }\n\t\t\tconst answer = action.output.replace(/^User answered:\\s*/i, '')\n\t\t\tcards.push(\n\t\t\t\tcreateCard({\n\t\t\t\t\ticon: '❓',\n\t\t\t\t\tcontent: `Question: ${input.question || ''}`,\n\t\t\t\t\tmeta,\n\t\t\t\t\ttype: 'question',\n\t\t\t\t})\n\t\t\t)\n\t\t\tcards.push(createCard({ icon: '💬', content: `Answer: ${answer}`, meta, type: 'input' }))\n\t\t} else {\n\t\t\tconst toolText = this.#getToolExecutingText(action.name, action.input)\n\t\t\tcards.push(createCard({ icon: '🔨', content: toolText, meta }))\n\t\t\tif (action.output?.length > 0) {\n\t\t\t\tcards.push(createCard({ icon: '🔨', content: action.output, meta, type: 'output' }))\n\t\t\t}\n\t\t}\n\n\t\treturn cards\n\t}\n}\n","/**\n * LLM 调用相关的错误类型与错误处理。\n */\n\nexport const InvokeErrorTypes = {\n\t// 可重试\n\tNETWORK_ERROR: 'network_error', // 网络错误,可重试\n\tRATE_LIMIT: 'rate_limit', // 触发限流,可重试\n\tSERVER_ERROR: 'server_error', // 5xx 服务端错误,可重试\n\tNO_TOOL_CALL: 'no_tool_call', // 模型没有调用工具\n\tINVALID_TOOL_ARGS: 'invalid_tool_args', // 工具参数与 schema 不匹配\n\tTOOL_EXECUTION_ERROR: 'tool_execution_error', // 工具执行出错\n\tINVALID_RESPONSE: 'invalid_response', // 响应体不是合法 JSON\n\tINVALID_SCHEMA: 'invalid_schema', // 响应是合法 JSON,但不符合预期结构\n\n\tUNKNOWN: 'unknown', // 未知错误(默认视为可重试)\n\n\t// 不可重试\n\tCONFIG_ERROR: 'config_error', // 本地配置或 hook 无效\n\tAUTH_ERROR: 'auth_error', // 认证失败\n\tCONTEXT_LENGTH: 'context_length', // 提示词过长\n\tCONTENT_FILTER: 'content_filter', // 内容被安全系统过滤\n} as const\n\ntype InvokeErrorType = (typeof InvokeErrorTypes)[keyof typeof InvokeErrorTypes] // 所有错误类型的联合类型\n\n// 可重试的错误类型列表,命中这些类型的错误可进行重试\nconst RETRYABLE_TYPES: readonly InvokeErrorType[] = [\n\tInvokeErrorTypes.NETWORK_ERROR,\n\tInvokeErrorTypes.RATE_LIMIT,\n\tInvokeErrorTypes.SERVER_ERROR,\n\tInvokeErrorTypes.NO_TOOL_CALL,\n\tInvokeErrorTypes.INVALID_TOOL_ARGS,\n\tInvokeErrorTypes.TOOL_EXECUTION_ERROR,\n\tInvokeErrorTypes.INVALID_RESPONSE,\n\tInvokeErrorTypes.INVALID_SCHEMA,\n\tInvokeErrorTypes.UNKNOWN,\n]\n\nexport class InvokeError extends Error {\n\ttype: InvokeErrorType // 错误类型\n\tretryable: boolean // 是否可重试\n\tstatusCode?: number // HTTP 状态码\n\t/* 原始错误对象(当本错误由其他错误引发时提供) */\n\trawError?: unknown // 原始错误对象(当本错误由其他错误引发时提供)\n\t/* API 原始响应(当本错误由 API 调用引发时提供) */\n\trawResponse?: unknown // API 原始响应(当本错误由 API 调用引发时提供)\n\n\t// 构造函数:根据错误类型创建错误实例,并自动判断是否可重试\n\tconstructor(type: InvokeErrorType, message: string, rawError?: unknown, rawResponse?: unknown) {\n\t\tsuper(message)\n\t\tthis.name = 'InvokeError'\n\t\tthis.type = type\n\t\tthis.retryable = RETRYABLE_TYPES.includes(type)\n\t\tthis.rawError = rawError\n\t\tthis.rawResponse = rawResponse\n\t}\n}\n","// 终端支持的颜色名称,对应 ANSI 标准色\ntype TerminalColor = 'blue' | 'cyan' | 'gray' | 'green' | 'red' | 'yellow'\n\n// 颜色名称到 ANSI 颜色码的映射表\nconst colorCodes: Record<TerminalColor, number> = {\n\tblue: 34, // 蓝色前景色码\n\tcyan: 36, // 青色前景色码\n\tgray: 90, // 灰色前景色码\n\tgreen: 32, // 绿色前景色码\n\tred: 31, // 红色前景色码\n\tyellow: 33, // 黄色前景色码\n}\n\n/**\n * 使用标准 ANSI 终端颜色转义序列格式化文本。\n * 给文本添加标准的 ANSI 终端颜色转义序列,使文本在终端中以指定颜色显示。\n * @param text 需要上色的原始文本\n * @param color 终端颜色名称\n * @param bold 是否加粗显示,默认 false\n * @returns 带 ANSI 颜色转义序列的文本\n */\nexport function formatTerminalText(text: string, color: TerminalColor, bold = false): string {\n\tconst weight = bold ? '\\u001B[1m' : '' // 加粗转义序列,非加粗时为空字符串\n\treturn `${weight}\\u001B[${colorCodes[color]}m${text}\\u001B[0m`\n}\n","/**\n * LLM 集成的工具函数\n */\nimport * as z from 'zod/v4'\n\nimport { formatTerminalText } from '../shared/terminal'\n\nimport type { Tool } from './types'\n\nconst debug = console.debug.bind(console, formatTerminalText('[LLM]', 'gray'))\n\n/**\n * 将 Zod schema 转换为 OpenAI 工具格式\n * 使用 Zod 4 原生 z.toJSONSchema()\n *\n * @param name - 工具名称\n * @param tool - 工具定义\n * @returns OpenAI 格式的工具定义\n */\nexport function zodToOpenAITool(name: string, tool: Tool) {\n\treturn {\n\t\ttype: 'function' as const, // 工具类型,固定为函数调用\n\t\tfunction: {\n\t\t\tname, // 工具名称\n\t\t\tdescription: tool.description, // 工具描述\n\t\t\tparameters: z.toJSONSchema(tool.inputSchema, { target: 'openapi-3.0' }), // 参数 JSON Schema(OpenAPI 3.0 格式)\n\t\t},\n\t}\n}\n\n/**\n * 为特定模型打补丁。仅修补已知的模型。\n *\n * @purpose\n * - 调和各模型所接受的参数 schema 之间的差异。\n * - 禁用思考/推理,或在无法完全禁用时将其降到最低。\n * - 尽量减少返回的 token。\n * - 对已知的较小模型提高 temperature,以提升自动恢复的几率。\n * @note 若用户显式设置了 temperature 则尊重该设置\n *\n * @todo 需要针对不同厂商的补丁。\n * 本地托管和第三方托管的模型可能有不同的 schema。\n *\n * @param body - 请求体对象(会就地修改)\n * @param baseURL - 模型服务的基础 URL,用于识别 provider\n * @returns 修改后的请求体对象\n */\nexport function modelPatch(body: Record<string, any>, baseURL?: string) {\n\tconst model: string = body.model || '' // 模型名称\n\tif (!model) return body\n\n\tconst provider = getProvider(baseURL) // 判断服务提供商\n\n\tconst modelName = normalizeModelName(model) // 归一化后的模型名\n\n\tif (modelName.startsWith('qwen')) {\n\t\tdebug('Patch Qwen: disable thinking')\n\t\tbody.enable_thinking = false\n\t\tif (body.temperature === undefined && !/max|plus/.test(modelName)) {\n\t\t\tdebug('Patch Qwen: raise temperature to 1.0')\n\t\t\tbody.temperature = 1.0\n\t\t}\n\t}\n\n\tif (modelName.startsWith('deepseek')) {\n\t\tdebug('Patch DeepSeek: disable thinking, remove tool_choice')\n\t\tbody.thinking = { type: 'disabled' }\n\t\tdelete body.tool_choice\n\t}\n\n\tif (modelName.startsWith('gpt')) {\n\t\tif (modelName.startsWith('gpt-5')) {\n\t\t\tbody.verbosity = 'low'\n\t\t}\n\n\t\t// 自 gpt-5.4 起,/chat/completions 在存在函数工具时会拒绝任何显式的 reasoning_effort。\n\t\t// 预期更新的模型也会遵循这一点。\n\t\t// - gpt-5.1 / gpt-5.2 可以完全禁用推理\n\t\t// - gpt-5 / -mini / -nano 最低只能到 \"minimal\"\n\t\t// - 其余模型(gpt-4.x、chat-latest、gpt-5.4+)都不能传入该字段\n\t\tif (modelName.includes('chat-latest')) {\n\t\t\tdebug('Patch chat-latest: omit reasoning_effort and temperature')\n\t\t\tdelete body.reasoning_effort\n\t\t\tdelete body.temperature\n\t\t} else if (/^gpt-5[12](-|$)/.test(modelName)) {\n\t\t\tdebug('Patch GPT-5.1/5.2: reasoning_effort=none')\n\t\t\tbody.reasoning_effort = 'none'\n\t\t} else if (/^gpt-5(-|$)/.test(modelName)) {\n\t\t\tdebug('Patch GPT-5: reasoning_effort=minimal')\n\t\t\tbody.reasoning_effort = 'minimal'\n\t\t} else {\n\t\t\tdebug('Patch GPT: omit reasoning_effort')\n\t\t\tdelete body.reasoning_effort\n\t\t}\n\t}\n\n\tif (modelName.startsWith('claude')) {\n\t\tif (/opus|sonnet|haiku/.test(modelName)) {\n\t\t\tdebug('Patch Claude: disable thinking')\n\t\t\tbody.thinking = { type: 'disabled' }\n\n\t\t\tif (provider !== 'openrouter') {\n\t\t\t\t// 将 tool_choice 转换为 Claude 格式\n\t\t\t\tif (body.tool_choice === 'required') {\n\t\t\t\t\t// 'required' -> { type: 'any' }(必须调用某个工具)\n\t\t\t\t\tdebug('Applying Claude patch: convert tool_choice \"required\" to { type: \"any\" }')\n\t\t\t\t\tbody.tool_choice = { type: 'any' }\n\t\t\t\t} else if (body.tool_choice?.function?.name) {\n\t\t\t\t\t// { type: 'function', function: { name: '...' } } -> { type: 'tool', name: '...' }\n\t\t\t\t\tdebug('Applying Claude patch: convert tool_choice format')\n\t\t\t\t\tbody.tool_choice = { type: 'tool', name: body.tool_choice.function.name }\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tdebug('Patch Claude: reasoning_effort=low')\n\t\t\tbody.reasoning_effort = 'low'\n\n\t\t\t// Fable 和 mythos 无法禁用自适应思考。\n\t\t\t// Claude 在扩展思考(extended thinking)下不支持 tool_choice。\n\t\t\t// 这两个概念界限模糊,基本上开启思考就不能用 tool_choice。\n\t\t\tdelete body.tool_choice\n\t\t}\n\t}\n\n\tif (modelName.startsWith('gemini')) {\n\t\tdebug('Patch Gemini: reasoning_effort=low')\n\t\tbody.reasoning_effort = 'low'\n\t\tif (/^gemini-25(?!.*pro)/.test(modelName)) {\n\t\t\tdebug('Patch Gemini 2.5 non-Pro: reasoning_effort=none')\n\t\t\tbody.reasoning_effort = 'none'\n\t\t} else if (\n\t\t\tmodelName.startsWith('gemini-35-flash') ||\n\t\t\tmodelName.startsWith('gemini-31-flash-lite') ||\n\t\t\tmodelName.startsWith('gemini-3-flash')\n\t\t) {\n\t\t\tdebug('Patch Gemini 3.x Flash/Lite: reasoning_effort=minimal')\n\t\t\tbody.reasoning_effort = 'minimal'\n\t\t}\n\t}\n\n\tif (modelName.startsWith('glm')) {\n\t\tdebug('Patch GLM: disable thinking')\n\t\tbody.thinking = { type: 'disabled' }\n\t}\n\n\tif (modelName.startsWith('hy')) {\n\t\tdebug('Patch Hunyuan: disable thinking, reasoning_effort=low')\n\t\tbody.thinking = { type: 'disabled' }\n\t\tbody.reasoning_effort = 'low'\n\t}\n\n\tif (modelName.startsWith('grok')) {\n\t\tif (/^grok-4-?3/.test(modelName)) {\n\t\t\tdebug('Patch Grok 4.3: reasoning_effort=none')\n\t\t\tbody.reasoning_effort = 'none'\n\t\t} else if (modelName.startsWith('grok-3-mini') || modelName.startsWith('grok-code-fast')) {\n\t\t\tdebug('Patch Grok mini/code: reasoning_effort=low')\n\t\t\tbody.reasoning_effort = 'low'\n\t\t}\n\t}\n\n\tif (modelName.startsWith('kimi')) {\n\t\tif (modelName.startsWith('kimi-k3')) {\n\t\t\t// Kimi K3 始终会思考,且在思考时会拒绝指定工具名(named tool choice)。\n\t\t\tdebug('Patch Kimi K3: use required tool choice, remove parallel tool calls')\n\t\t\tdelete body.parallel_tool_calls\n\t\t\tif (body.tool_choice?.function?.name) body.tool_choice = 'required'\n\t\t} else if (!modelName.includes('code')) {\n\t\t\t// kimi-k2.7-code 无法禁用思考\n\t\t\tdebug('Patch Kimi: disable thinking')\n\t\t\tbody.thinking = { type: 'disabled' }\n\t\t}\n\t}\n\n\tif (modelName.startsWith('minimax')) {\n\t\tdebug('Patch MiniMax: remove parallel_tool_calls')\n\t\tdelete body.parallel_tool_calls\n\n\t\tif (modelName.includes('m3')) {\n\t\t\t// 只有 M3 可以禁用思考\n\t\t\tdebug('Patch MiniMax: disable thinking')\n\t\t\tbody.thinking = { type: 'disabled' }\n\t\t}\n\t}\n\n\t// provider 补丁\n\n\tif (provider === 'openrouter') {\n\t\t// openrouter 使用 reasoning 对象而不是 reasoning_effort\n\n\t\t// 读取当前是否禁用推理及推理等级\n\t\tconst reasoningEffort = body.reasoning_effort\n\t\tconst reasoningDisabled =\n\t\t\tbody.thinking?.type === 'disabled' ||\n\t\t\tbody.enable_thinking === false ||\n\t\t\treasoningEffort === 'none'\n\n\t\tif (reasoningDisabled) {\n\t\t\t// 禁用推理\n\t\t\tbody.reasoning = { enabled: false }\n\t\t} else if (reasoningEffort) {\n\t\t\t// 启用推理并指定等级\n\t\t\tbody.reasoning = { enabled: true, effort: reasoningEffort }\n\t\t}\n\t}\n\n\treturn body\n}\n\n/**\n * 检查给定的模型 ID 是否符合某个具体模型名称\n *\n * @note\n * 不同的模型服务商可能对同一模型使用不同的模型 ID。\n * 例如,OpenAI 的 `gpt-5.2` 可能被命名为:\n *\n * - `gpt-5.2-version`\n * - `gpt-5_2-date`\n * - `GPT-52-version-date`\n * - `openai/gpt-5.2-chat`\n *\n * 它们应被视为同一模型。\n * 将其归一化为 `gpt-52`\n */\n/**\n * 归一化模型名称,将不同厂商对同一模型的命名统一为同一形式(如 `gpt-5.2` -> `gpt52`)\n *\n * @param modelName - 原始模型名称\n * @returns 归一化后的模型名称(小写、去掉前缀与特殊字符)\n */\nexport function normalizeModelName(modelName: string): string {\n\tlet normalizedName = modelName.toLowerCase()\n\n\t// 去掉 '/' 之前的前缀\n\tif (normalizedName.includes('/')) {\n\t\tnormalizedName = normalizedName.split('/')[1]\n\t}\n\n\t// 去掉 '_'\n\tnormalizedName = normalizedName.replace(/_/g, '')\n\n\t// 去掉 '.'\n\tnormalizedName = normalizedName.replace(/\\./g, '')\n\n\treturn normalizedName\n}\n\n/**\n * 根据 baseURL 判断服务提供商(目前仅识别 openrouter)\n *\n * @param baseURL - 模型服务的基础 URL\n * @returns 'openrouter' 或 undefined\n */\nexport function getProvider(baseURL?: string): 'openrouter' | undefined {\n\tif (!baseURL) return undefined\n\ttry {\n\t\tconst url = new URL(baseURL)\n\t\tconst hostname = url.hostname\n\t\tif (hostname === 'openrouter.ai') return 'openrouter'\n\t\treturn undefined\n\t} catch {\n\t\treturn undefined\n\t}\n}\n","/**\n * OpenAI 客户端实现\n */\nimport * as z from 'zod/v4'\n\nimport { InvokeError, InvokeErrorTypes } from './errors'\nimport type {\n\tInvokeOptions,\n\tInvokeResult,\n\tLLMClient,\n\tMessage,\n\tResolvedLLMConfig,\n\tTool,\n} from './types'\nimport { modelPatch, zodToOpenAITool } from './utils'\n\n/**\n * 兼容 OpenAI 的 API 客户端\n */\nexport class OpenAICompatibleClient implements LLMClient {\n\tconfig: ResolvedLLMConfig // 解析后的 LLM 配置\n\tprivate fetch: typeof globalThis.fetch // 实际发起请求用的 fetch 函数\n\n\t// 构造函数:保存配置并绑定自定义 fetch 函数\n\tconstructor(config: ResolvedLLMConfig) {\n\t\tthis.config = config\n\t\tthis.fetch = config.customFetch\n\t}\n\n\t/**\n\t * 调用 LLM 接口并执行模型请求的工具\n\t * 流程:转换工具定义 -> 构建并转换请求体 -> 调用 API -> 处理 HTTP 错误 -> 解析并校验响应 -> 执行工具\n\t *\n\t * @param messages - 对话消息列表\n\t * @param tools - 可用工具集合\n\t * @param abortSignal - 中止信号,用于取消请求\n\t * @param options - 可选的调用选项\n\t * @returns 工具调用结果\n\t */\n\tasync invoke(\n\t\tmessages: Message[],\n\t\ttools: Record<string, Tool>,\n\t\tabortSignal?: AbortSignal,\n\t\toptions?: InvokeOptions\n\t): Promise<InvokeResult> {\n\t\tabortSignal?.throwIfAborted()\n\n\t\t// 1. 将工具转换为 OpenAI 格式\n\t\tconst openaiTools = Object.entries(tools).map(([name, t]) => zodToOpenAITool(name, t))\n\n\t\t// 构建请求体\n\n\t\t// 工具选择策略:未指定具体工具且允许命名工具时使用 'required',否则指定具体工具名\n\t\tlet toolChoice: unknown = 'required'\n\t\tif (options?.toolChoiceName && !this.config.disableNamedToolChoice) {\n\t\t\t// 指定要强制调用的工具\n\t\t\ttoolChoice = { type: 'function', function: { name: options.toolChoiceName } }\n\t\t}\n\n\t\tconst requestBody: Record<string, unknown> = {\n\t\t\tmodel: this.config.model, // 模型名称\n\t\t\tmessages, // 对话消息列表\n\t\t\ttools: openaiTools, // OpenAI 格式的工具定义列表\n\t\t\tparallel_tool_calls: false, // 禁用并行工具调用\n\t\t\ttool_choice: toolChoice, // 工具选择策略\n\t\t}\n\t\t// 仅在调用方显式设置了 temperature 时才发送该字段。大多数新模型会在设置了该字段时直接报错。\n\t\tif (this.config.temperature !== undefined) {\n\t\t\trequestBody.temperature = this.config.temperature\n\t\t}\n\n\t\t// 根据模型类型打补丁(调整不同模型的参数差异)\n\t\tmodelPatch(requestBody, this.config.baseURL)\n\n\t\t// 应用 transformRequestBody 进行请求体自定义转换\n\t\tlet transformedBody: Record<string, unknown> | undefined\n\t\ttry {\n\t\t\ttransformedBody = this.config.transformRequestBody(requestBody)\n\t\t} catch (error) {\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.CONFIG_ERROR,\n\t\t\t\t`transformRequestBody failed: ${(error as Error).message}`,\n\t\t\t\terror\n\t\t\t)\n\t\t}\n\t\tconst finalRequestBody = transformedBody ?? requestBody\n\n\t\t// 2. 调用 API\n\t\tlet response: Response\n\t\ttry {\n\t\t\tresponse = await this.fetch(`${this.config.baseURL}/chat/completions`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t...(this.config.apiKey && { Authorization: `Bearer ${this.config.apiKey}` }),\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(finalRequestBody),\n\t\t\t\tsignal: abortSignal,\n\t\t\t})\n\t\t} catch (error: unknown) {\n\t\t\tif ((error as any)?.name === 'AbortError') throw error\n\t\t\tconsole.error(error)\n\t\t\tthrow new InvokeError(InvokeErrorTypes.NETWORK_ERROR, 'Network request failed', error)\n\t\t}\n\n\t\t// 3. 处理 HTTP 错误\n\t\tif (!response.ok) {\n\t\t\tlet errorData: any\n\t\t\ttry {\n\t\t\t\terrorData = await response.json()\n\t\t\t} catch (error) {\n\t\t\t\tif ((error as any)?.name === 'AbortError') throw error\n\t\t\t}\n\t\t\tconst errorMessage = errorData?.error?.message || response.statusText\n\n\t\t\tif (response.status === 401 || response.status === 403) {\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.AUTH_ERROR,\n\t\t\t\t\t`Authentication failed: ${errorMessage}`,\n\t\t\t\t\terrorData\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (response.status === 429) {\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.RATE_LIMIT,\n\t\t\t\t\t`Rate limit exceeded: ${errorMessage}`,\n\t\t\t\t\terrorData\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (response.status >= 500) {\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.SERVER_ERROR,\n\t\t\t\t\t`Server error: ${errorMessage}`,\n\t\t\t\t\terrorData\n\t\t\t\t)\n\t\t\t}\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.UNKNOWN,\n\t\t\t\t`HTTP ${response.status}: ${errorMessage}`,\n\t\t\t\terrorData\n\t\t\t)\n\t\t}\n\n\t\t// 4. 解析并校验响应\n\t\tlet data: any\n\t\ttry {\n\t\t\tdata = await response.json()\n\t\t} catch (error) {\n\t\t\tif ((error as any)?.name === 'AbortError') throw error\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.INVALID_RESPONSE,\n\t\t\t\t'Response body is not valid JSON',\n\t\t\t\terror\n\t\t\t)\n\t\t}\n\n\t\tconst choice = data.choices?.[0]\n\t\tif (!choice) {\n\t\t\tthrow new InvokeError(InvokeErrorTypes.INVALID_SCHEMA, 'No choices in response', data)\n\t\t}\n\n\t\t// 检查 finish_reason\n\t\tswitch (choice.finish_reason) {\n\t\t\tcase 'tool_calls':\n\t\t\tcase 'function_call': // gemini\n\t\t\tcase 'stop': // 部分模型即使带工具调用也会返回 stop\n\t\t\t\tbreak\n\t\t\tcase 'length':\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.CONTEXT_LENGTH,\n\t\t\t\t\t'Response truncated: max tokens reached',\n\t\t\t\t\tundefined,\n\t\t\t\t\tdata\n\t\t\t\t)\n\t\t\tcase 'content_filter':\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.CONTENT_FILTER,\n\t\t\t\t\t'Content filtered by safety system',\n\t\t\t\t\tundefined,\n\t\t\t\t\tdata\n\t\t\t\t)\n\t\t\tdefault:\n\t\t\t\tthrow new InvokeError(\n\t\t\t\t\tInvokeErrorTypes.INVALID_SCHEMA,\n\t\t\t\t\t`Unexpected finish_reason: ${choice.finish_reason}`,\n\t\t\t\t\tundefined,\n\t\t\t\t\tdata\n\t\t\t\t)\n\t\t}\n\n\t\t// 若提供了 normalizeResponse 则应用之(用于自动修复格式问题)\n\t\tconst normalizedData = options?.normalizeResponse ? options.normalizeResponse(data) : data\n\t\tconst normalizedChoice = (normalizedData as any).choices?.[0]\n\n\t\t// 从响应中获取工具名称\n\t\tconst toolCallName = normalizedChoice?.message?.tool_calls?.[0]?.function?.name\n\t\tif (!toolCallName) {\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.NO_TOOL_CALL,\n\t\t\t\t'No tool call found in response',\n\t\t\t\tundefined,\n\t\t\t\tdata\n\t\t\t)\n\t\t}\n\n\t\tconst tool = tools[toolCallName]\n\t\tif (!tool) {\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.UNKNOWN,\n\t\t\t\t`Tool \"${toolCallName}\" not found in tools`,\n\t\t\t\tundefined,\n\t\t\t\tdata\n\t\t\t)\n\t\t}\n\n\t\t// 提取并解析工具参数\n\t\tconst argString = normalizedChoice.message?.tool_calls?.[0]?.function?.arguments\n\t\tif (!argString) {\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.INVALID_TOOL_ARGS,\n\t\t\t\t'No tool call arguments found',\n\t\t\t\tundefined,\n\t\t\t\tdata\n\t\t\t)\n\t\t}\n\n\t\tlet parsedArgs: unknown\n\t\ttry {\n\t\t\tparsedArgs = JSON.parse(argString)\n\t\t} catch (error) {\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.INVALID_TOOL_ARGS,\n\t\t\t\t'Failed to parse tool arguments as JSON',\n\t\t\t\terror,\n\t\t\t\tdata\n\t\t\t)\n\t\t}\n\n\t\t// 用 schema 校验\n\t\tconst validation = tool.inputSchema.safeParse(parsedArgs)\n\t\tif (!validation.success) {\n\t\t\tconsole.error(z.prettifyError(validation.error))\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.INVALID_TOOL_ARGS,\n\t\t\t\t'Tool arguments validation failed',\n\t\t\t\tvalidation.error,\n\t\t\t\tdata\n\t\t\t)\n\t\t}\n\t\tconst toolInput = validation.data\n\n\t\t// 5. 执行工具\n\t\tlet toolResult: unknown\n\t\ttry {\n\t\t\ttoolResult = await tool.execute(toolInput)\n\t\t} catch (error: unknown) {\n\t\t\tif ((error as any)?.name === 'AbortError') throw error\n\t\t\tthrow new InvokeError(\n\t\t\t\tInvokeErrorTypes.TOOL_EXECUTION_ERROR,\n\t\t\t\t`Tool execution failed: ${(error as Error)?.message}`,\n\t\t\t\terror,\n\t\t\t\tdata\n\t\t\t)\n\t\t}\n\n\t\t// 返回结果\n\t\treturn {\n\t\t\ttoolCall: {\n\t\t\t\tname: toolCallName, // 被调用的工具名称\n\t\t\t\targs: toolInput, // 校验后的工具参数\n\t\t\t},\n\t\t\ttoolResult, // 工具执行结果\n\t\t\tusage: {\n\t\t\t\tpromptTokens: data.usage?.prompt_tokens ?? 0, // 输入(提示词)token 数\n\t\t\t\tcompletionTokens: data.usage?.completion_tokens ?? 0, // 输出(生成)token 数\n\t\t\t\ttotalTokens: data.usage?.total_tokens ?? 0, // 总 token 数\n\t\t\t\tcachedTokens: data.usage?.prompt_tokens_details?.cached_tokens, // 缓存命中的 token 数\n\t\t\t\treasoningTokens: data.usage?.completion_tokens_details?.reasoning_tokens, // 推理 token 数\n\t\t\t},\n\t\t\trawResponse: data, // 原始响应数据(用于调试)\n\t\t\trawRequest: finalRequestBody, // 最终发送的请求体(用于调试)\n\t\t}\n\t}\n}\n","import { OpenAICompatibleClient } from './OpenAICompatibleClient'\nimport { InvokeError, InvokeErrorTypes } from './errors'\nimport type {\n\tInvokeOptions,\n\tInvokeResult,\n\tLLMClient,\n\tLLMConfig,\n\tMessage,\n\tResolvedLLMConfig,\n\tTool,\n} from './types'\n\nexport { InvokeError, InvokeErrorTypes }\nexport type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool }\n\n/**\n * LLM 模块\n */\nexport class LLM extends EventTarget {\n\tconfig: ResolvedLLMConfig // 解析后的 LLM 配置\n\tclient: LLMClient // LLM 客户端实例\n\n\t// 构造函数:解析配置并创建默认的 OpenAI 兼容客户端\n\tconstructor(config: LLMConfig) {\n\t\tsuper()\n\t\tthis.config = parseLLMConfig(config)\n\n\t\t// 默认使用 OpenAI 兼容客户端\n\t\tthis.client = new OpenAICompatibleClient(this.config)\n\t}\n\n\t/**\n\t * - 调用一次 LLM API\n\t * - 调用一次工具\n\t * - 返回工具执行结果\n\t */\n\tasync invoke(\n\t\tmessages: Message[],\n\t\ttools: Record<string, Tool>,\n\t\tabortSignal: AbortSignal,\n\t\toptions?: InvokeOptions\n\t): Promise<InvokeResult> {\n\t\treturn await withRetry(async () => this.client.invoke(messages, tools, abortSignal, options), {\n\t\t\tmaxRetries: this.config.maxRetries, // 最大重试次数\n\t\t\tonRetry: (attempt, lastError) => { // 每次重试前的回调,用于派发 retry 事件\n\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\tnew CustomEvent('retry', {\n\t\t\t\t\t\tdetail: { attempt, maxAttempts: this.config.maxRetries, lastError },\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t},\n\t\t})\n\t}\n}\n\n/**\n * 重试某个函数,直到成功或达到最大重试次数。\n */\nasync function withRetry<T>(\n\tfn: () => Promise<T>,\n\tsettings: {\n\t\tmaxRetries: number\n\t\tonRetry: (attempt: number, lastError: Error) => void\n\t}\n): Promise<T> {\n\tlet attempt = 0\n\twhile (true) {\n\t\ttry {\n\t\t\treturn await fn()\n\t\t} catch (error: unknown) {\n\t\t\tif ((error as any)?.name === 'AbortError') throw error\n\t\t\tif (error instanceof InvokeError && !error.retryable) throw error\n\t\t\tattempt++\n\t\t\tif (attempt > settings.maxRetries) throw error\n\n\t\t\tconsole.debug('[LLM] retryable failure, will retry:', error)\n\t\t\tsettings.onRetry(attempt, error as Error)\n\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 100))\n\t\t}\n\t}\n}\n\n/**\n * 解析并校验 LLM 配置,填充默认值后返回完整配置\n *\n * @param config - 用户传入的 LLM 配置\n * @returns 解析后的完整配置\n */\nexport function parseLLMConfig(config: LLMConfig): ResolvedLLMConfig {\n\t// 运行时校验作为防御性编程(类型系统已保证这些条件)\n\tif (!config.baseURL || !config.model) {\n\t\tthrow new Error(\n\t\t\t'[PageAgent] LLM configuration required. Please provide: baseURL, model. ' +\n\t\t\t\t'See: https://alibaba.github.io/page-agent/docs/features/models'\n\t\t)\n\t}\n\n\tif (config.temperature !== undefined) {\n\t\tconsole.warn(\n\t\t\t'[PageAgent] LLMConfig.temperature is deprecated and will be removed in a future version. ' +\n\t\t\t\t'Use transformRequestBody to set it only for models you have verified accept it.'\n\t\t)\n\t}\n\n\treturn {\n\t\tbaseURL: config.baseURL, // 基础 URL\n\t\tmodel: config.model, // 模型名称\n\t\tapiKey: config.apiKey || '', // API 密钥,缺省为空字符串\n\t\ttemperature: config.temperature, // 采样温度(已废弃,透传保持兼容)\n\t\tmaxRetries: config.maxRetries ?? 2, // 最大重试次数,默认 2\n\t\ttransformRequestBody: config.transformRequestBody ?? ((requestBody) => requestBody), // 请求体转换函数,缺省为原样返回\n\t\tdisableNamedToolChoice: config.disableNamedToolChoice ?? false, // 是否禁用指定工具选择,默认 false\n\t\tcustomFetch: (config.customFetch ?? fetch).bind(globalThis), // 不绑定 this 的话,调用 fetch 会报非法调用\n\t}\n}\n","import { InvokeError, InvokeErrorTypes } from '../../llm/errors'\nimport * as z from 'zod/v4'\n\nimport { formatTerminalText } from '../../shared/terminal'\n\nimport type { AgentTool } from '../tools'\n\nconst log = console.log.bind(console, formatTerminalText('[autoFixer]', 'yellow')) // 带 [autoFixer] 前缀的日志函数\n\n/**\n * 规范化 LLM 响应并修复常见的格式问题。\n *\n * 处理:\n * - 没有 tool_calls 但 message.content 中含 JSON(回退)\n * - 模型将动作名作为工具调用返回而非 AgentOutput\n * - 参数被双重 JSON 字符串化\n * - 嵌套的函数调用格式\n * - 缺少 action 字段(回退为 wait)\n * - 单字段工具的原始动作输入(如 `{\"click_element_by_index\": 2}`)\n * - 等等\n */\nexport function normalizeResponse(response: any, tools?: Map<string, AgentTool>): any {\n\tlet resolvedArguments: any\n\n\tconst choice = (response as { choices?: Choice[] }).choices?.[0]\n\tif (!choice) throw new Error('No choices in response')\n\n\tconst message = choice.message\n\tif (!message) throw new Error('No message in choice')\n\n\tconst toolCall = message.tool_calls?.[0]\n\n\t// 修复 arguments 的层级和位置\n\n\tif (toolCall?.function?.arguments) {\n\t\tresolvedArguments = safeJsonParse(toolCall.function.arguments)\n\n\t\t// 情况:有时模型只返回动作层级\n\t\tif (toolCall.function.name && toolCall.function.name !== 'AgentOutput') {\n\t\t\tlog(`#1: fixing tool_call`)\n\t\t\tresolvedArguments = { action: safeJsonParse(resolvedArguments) }\n\t\t}\n\t} else {\n\t\t// 情况:有时模型在 content 中返回 json 而不是 tool_calls\n\t\tif (message.content) {\n\t\t\tconst content = message.content.trim()\n\t\t\tconst jsonInContent = retrieveJsonFromString(content)\n\t\t\tif (jsonInContent) {\n\t\t\t\tresolvedArguments = safeJsonParse(jsonInContent)\n\n\t\t\t\t// 情况:有时 content json 包含外层包装\n\t\t\t\tif (resolvedArguments?.name === 'AgentOutput') {\n\t\t\t\t\tlog(`#2: fixing tool_call`)\n\t\t\t\t\tresolvedArguments = safeJsonParse(resolvedArguments.arguments)\n\t\t\t\t}\n\n\t\t\t\t// 情况:有时甚至有两层包装\n\t\t\t\tif (resolvedArguments?.type === 'function') {\n\t\t\t\t\tlog(`#3: fixing tool_call`)\n\t\t\t\t\tresolvedArguments = safeJsonParse(resolvedArguments.function.arguments)\n\t\t\t\t}\n\n\t\t\t\t// 情况:还有时候只有动作层级\n\t\t\t\t// todo: 需要更好的检测逻辑\n\t\t\t\tif (\n\t\t\t\t\t!resolvedArguments?.action &&\n\t\t\t\t\t!resolvedArguments?.evaluation_previous_goal &&\n\t\t\t\t\t!resolvedArguments?.memory &&\n\t\t\t\t\t!resolvedArguments?.next_goal &&\n\t\t\t\t\t!resolvedArguments?.thinking\n\t\t\t\t) {\n\t\t\t\t\tlog(`#4: fixing tool_call`)\n\t\t\t\t\tresolvedArguments = { action: safeJsonParse(resolvedArguments) }\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new Error('No tool_call and the message content does not contain valid JSON')\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new Error('No tool_call nor message content is present')\n\t\t}\n\t}\n\n\t// 修复双重字符串化的参数\n\tresolvedArguments = safeJsonParse(resolvedArguments)\n\tif (resolvedArguments.action) {\n\t\tresolvedArguments.action = safeJsonParse(resolvedArguments.action)\n\t}\n\n\t// 使用工具 schema 校验并修复动作输入\n\tif (resolvedArguments.action && tools) {\n\t\tresolvedArguments.action = validateAction(resolvedArguments.action, tools)\n\t}\n\n\t// 修复不完整的格式\n\tif (!resolvedArguments.action) {\n\t\tlog(`#5: fixing tool_call`)\n\t\tresolvedArguments.action = { wait: { seconds: 1 } }\n\t}\n\n\t// 重新打包为标准格式\n\treturn {\n\t\t...response,\n\t\tchoices: [\n\t\t\t{\n\t\t\t\t...choice,\n\t\t\t\tmessage: {\n\t\t\t\t\t...message,\n\t\t\t\t\ttool_calls: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t...(toolCall || {}),\n\t\t\t\t\t\t\tfunction: {\n\t\t\t\t\t\t\t\t...(toolCall?.function || {}),\n\t\t\t\t\t\t\t\tname: 'AgentOutput',\n\t\t\t\t\t\t\t\targuments: JSON.stringify(resolvedArguments),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t}\n}\n\n/**\n * 根据工具 schema 校验动作。提供清晰的错误信息,\n * 而不是让联合 schema 产生难以阅读的错误。\n *\n * 同时为单字段工具强制转换原始输入:\n * 如 `{\"click_element_by_index\": 2}` → `{\"click_element_by_index\": {\"index\": 2}}`\n */\nfunction validateAction(action: any, tools: Map<string, AgentTool>): any {\n\tif (typeof action !== 'object' || action === null) return action\n\n\tconst toolName = Object.keys(action)[0]\n\tif (!toolName) return action\n\n\tconst tool = tools.get(toolName)\n\tif (!tool) {\n\t\tconst available = Array.from(tools.keys()).join(', ')\n\t\tthrow new InvokeError(\n\t\t\tInvokeErrorTypes.INVALID_TOOL_ARGS,\n\t\t\t`Unknown action \"${toolName}\". Available: ${available}`\n\t\t)\n\t}\n\n\tlet value = action[toolName]\n\tconst schema = tool.inputSchema\n\n\t// 为单字段工具强制转换原始输入\n\tif (schema instanceof z.ZodObject && value !== null && typeof value !== 'object') {\n\t\tconst requiredKey = Object.keys(schema.shape).find(\n\t\t\t(k) => !(schema.shape as Record<string, z.ZodType>)[k].safeParse(undefined).success\n\t\t)\n\t\tif (requiredKey) {\n\t\t\tlog(`coercing primitive action input for \"${toolName}\"`)\n\t\t\tvalue = { [requiredKey]: value }\n\t\t}\n\t}\n\n\tconst result = schema.safeParse(value)\n\tif (!result.success) {\n\t\tthrow new InvokeError(\n\t\t\tInvokeErrorTypes.INVALID_TOOL_ARGS,\n\t\t\t`Invalid input for action \"${toolName}\": ${z.prettifyError(result.error)}`\n\t\t)\n\t}\n\n\treturn { [toolName]: result.data }\n}\n\n/**\n * 安全解析 JSON,如果不是 JSON 则返回原始输入。\n */\nfunction safeJsonParse(input: any): any {\n\tif (typeof input === 'string') {\n\t\ttry {\n\t\t\treturn JSON.parse(input.trim())\n\t\t} catch {\n\t\t\treturn input\n\t\t}\n\t}\n\treturn input\n}\n\n/**\n * 从字符串中提取并解析 JSON。\n * - 将第一个 `{` 与最后一个 `}` 之间的内容视为 JSON。\n * - 尝试将该内容解析为 JSON 并返回解析后的值(对象/数组/原始值),成功则返回,否则返回 null。\n */\nfunction retrieveJsonFromString(str: string): any {\n\ttry {\n\t\tconst json = /({[\\s\\S]*})/.exec(str) ?? []\n\t\tif (json.length === 0) {\n\t\t\treturn null\n\t\t}\n\t\treturn JSON.parse(json[0]!)\n\t} catch {\n\t\treturn null\n\t}\n}\n\ninterface Choice {\n\tmessage?: {\n\t\trole?: 'assistant' // 消息角色\n\t\tcontent?: string // 消息内容(无 tool_calls 时可能内嵌 JSON)\n\t\ttool_calls?: {\n\t\t\tid?: string // 工具调用 ID\n\t\t\ttype?: 'function' // 工具调用类型\n\t\t\tfunction?: {\n\t\t\t\tname?: string // 函数名\n\t\t\t\targuments?: string // 函数参数的 JSON 字符串\n\t\t\t}\n\t\t}[]\n\t}\n\tindex?: 0 // 选择序号\n\tfinish_reason?: 'tool_calls' // 结束原因\n}\n","import { formatTerminalText } from '../../shared/terminal'\n\nexport * from './autoFixer'\n\n/**\n * 等待 `seconds` 秒。若提供了 `signal`,则等待可被取消:\n * 中止时会以 signal 的 reason(一个 `AbortError`)reject。\n */\nexport async function waitFor(seconds: number, signal?: AbortSignal): Promise<void> {\n\tif (!signal) {\n\t\tawait new Promise((resolve) => setTimeout(resolve, seconds * 1000))\n\t\treturn\n\t}\n\tsignal.throwIfAborted()\n\tawait new Promise<void>((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tsignal.removeEventListener('abort', onAbort)\n\t\t\tresolve()\n\t\t}, seconds * 1000)\n\t\tconst onAbort = () => {\n\t\t\tclearTimeout(timer)\n\t\t\t// reason 是一个 DOMException AbortError。\n\t\t\treject(signal.reason as DOMException)\n\t\t}\n\t\tsignal.addEventListener('abort', onAbort, { once: true })\n\t})\n}\n\n//\n\n// 截断文本到指定最大长度,超出部分以省略号结尾\nexport function truncate(text: string, maxLength: number): string {\n\tif (text.length > maxLength) {\n\t\treturn text.substring(0, maxLength) + '...'\n\t}\n\treturn text\n}\n\n//\n\n// 生成一个随机 ID;若传入 existingIDs,则保证不与已有 ID 重复(最多尝试 1000 次)\nexport function randomID(existingIDs?: string[]): string {\n\tlet id = Math.random().toString(36).substring(2, 11)\n\n\tif (!existingIDs) {\n\t\treturn id\n\t}\n\n\tconst MAX_TRY = 1000\n\tlet tryCount = 0\n\n\twhile (existingIDs.includes(id)) {\n\t\tid = Math.random().toString(36).substring(2, 11)\n\t\ttryCount++\n\t\tif (tryCount > MAX_TRY) {\n\t\t\tthrow new Error('randomID: too many tries')\n\t\t}\n\t}\n\n\treturn id\n}\n\n//\nconst _global = globalThis as any // 全局对象(用于跨模块共享 ID 列表)\n\n// 初始化全局 ID 列表(若尚未存在)\nif (!_global.__PAGE_AGENT_IDS__) {\n\t_global.__PAGE_AGENT_IDS__ = []\n}\n\nconst ids = _global.__PAGE_AGENT_IDS__ // 当前窗口已生成的 ID 列表\n\n/**\n * 生成一个随机 ID。\n * @note 在当前窗口内唯一。\n */\nexport function uid() {\n\tconst id = randomID(ids)\n\tids.push(id)\n\treturn id\n}\n\nconst llmsTxtCache = new Map<string, string | null>()\n\n/** 获取某个 URL 源的 /llms.txt。按源缓存,`null` 表示已尝试过但未找到。 */\nexport async function fetchLlmsTxt(url: string): Promise<string | null> {\n\tlet origin: string\n\ttry {\n\t\torigin = new URL(url).origin\n\t} catch {\n\t\treturn null // URL 无效\n\t}\n\t// about:blank、data:、file:\n\tif (origin === 'null') return null\n\n\tif (llmsTxtCache.has(origin)) return llmsTxtCache.get(origin)!\n\n\tconst endpoint = `${origin}/llms.txt`\n\tlet result: string | null = null\n\ttry {\n\t\tconsole.log(formatTerminalText(`[llms.txt] Fetching ${endpoint}`, 'gray'))\n\t\tconst res = await fetch(endpoint, { signal: AbortSignal.timeout(3000) })\n\t\tif (res.ok) {\n\t\t\tresult = await res.text()\n\t\t\tconsole.log(formatTerminalText(`[llms.txt] Found (${result.length} chars)`, 'green'))\n\t\t\tif (result.length > 1000) {\n\t\t\t\tconsole.log(formatTerminalText(`[llms.txt] Truncating to 1000 chars`, 'yellow'))\n\t\t\t\tresult = truncate(result, 1000)\n\t\t\t}\n\t\t} else {\n\t\t\tconsole.debug(formatTerminalText(`[llms.txt] ${res.status} for ${endpoint}`, 'gray'))\n\t\t}\n\t} catch (e) {\n\t\tconsole.debug(formatTerminalText(`[llms.txt] not found for ${endpoint}`, 'gray'), e)\n\t}\n\tllmsTxtCache.set(origin, result)\n\treturn result\n}\n\n/**\n * 简单的断言函数:条件为假时抛出错误\n * @param condition - 要断言的条件\n * @param message - 可选的错误信息\n * @throws 条件为假时抛出错误\n */\nexport function assert(condition: unknown, message?: string, silent?: boolean): asserts condition {\n\tif (!condition) {\n\t\tconst errorMessage = message ?? 'Assertion failed'\n\n\t\tif (!silent) console.error(formatTerminalText(`❌ assert: ${errorMessage}`, 'red'))\n\n\t\tthrow new Error(errorMessage)\n\t}\n}\n\n/**\n * 抑制函数产生的错误。\n */\nexport async function suppress<T>(fn: () => T | Promise<T>): Promise<Awaited<T> | undefined> {\n\ttry {\n\t\treturn await fn()\n\t} catch (error) {\n\t\tconsole.error(error)\n\t\treturn undefined\n\t}\n}\n","/**\n * PageAgent 的内部工具。\n * @note 改编自 browser-use\n */\nimport * as z from 'zod/v4'\n\nimport type { AgentRuntime } from '../AgentRuntime'\nimport { waitFor } from '../utils'\n\n/**\n * 每次工具调用都会传入的上下文。\n * 工具必须遵守 `signal` 以支持协作式取消。\n */\nexport interface ToolContext {\n\tsignal: AbortSignal // 取消信号,工具需配合以支持协作式取消\n}\n\n/**\n * 内部工具定义,可访问 PageAgent 的 `this` 上下文\n */\nexport interface AgentTool<TParams = any> {\n\t// name: string\n\tdescription: string // 工具描述,会注入到 LLM 提示词中\n\tinputSchema: z.ZodType<TParams> // 工具入参的 zod 校验 schema\n\texecute: (this: AgentRuntime, args: TParams, ctx: ToolContext) => Promise<string> // 工具执行逻辑\n}\n\n/** 创建一个工具定义(直接透传 options) */\nexport function tool<TParams>(options: AgentTool<TParams>): AgentTool<TParams> {\n\treturn options\n}\n\n/**\n * PageAgent 的内部工具。\n * 注:使用 any 以允许每个工具拥有不同的参数类型\n */\nexport const tools = new Map<string, AgentTool>()\n\ntools.set(\n\t'done',\n\ttool({\n\t\tdescription:\n\t\t\t'Complete task. Text is your final response to the user — keep it concise unless the user explicitly asks for detail.',\n\t\tinputSchema: z.object({\n\t\t\ttext: z.string(), // 最终回复用户的文本\n\t\t\tsuccess: z.boolean().default(true), // 任务是否成功\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, _input) {\n\t\t\t// @note 主循环会处理这个工具\n\t\t\treturn Promise.resolve('Task completed')\n\t\t},\n\t})\n)\n\ntools.set(\n\t'wait',\n\ttool({\n\t\tdescription: 'Wait for x seconds. Can be used to wait until the page or data is fully loaded.',\n\t\tinputSchema: z.object({\n\t\t\tseconds: z.number().min(1).max(10).default(1), // 等待秒数\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input, { signal }) {\n\t\t\t// 尝试从实际等待时间中扣减 LLM 调用耗时\n\t\t\tconst lastTimeUpdate = await this.browserController.getLastUpdateTime()\n\t\t\tconst secondsSinceLastUpdate = (Date.now() - lastTimeUpdate) / 1000\n\t\t\tconst actualWaitTime = Math.max(0, input.seconds - secondsSinceLastUpdate)\n\t\t\tconsole.log(`actualWaitTime: ${actualWaitTime} seconds`)\n\t\t\tawait waitFor(actualWaitTime, signal)\n\n\t\t\tconst waitedSeconds = (secondsSinceLastUpdate + actualWaitTime).toFixed(2)\n\t\t\treturn `✅ Waited for ${waitedSeconds} seconds.`\n\t\t},\n\t})\n)\n\ntools.set(\n\t'ask_user',\n\ttool({\n\t\tdescription:\n\t\t\t'Ask the user a question and wait for their answer. Use this if you need more information or clarification.',\n\t\tinputSchema: z.object({\n\t\t\tquestion: z.string(), // 要询问用户的问题\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input, { signal }) {\n\t\t\tif (!this.onAskUser) {\n\t\t\t\tthrow new Error('ask_user tool requires onAskUser callback to be set')\n\t\t\t}\n\t\t\tconst raw = await this.onAskUser(input.question, { signal })\n\t\t\t// 回答可能是纯文本,也可能附带回答时上传的附件(图片等)\n\t\t\tconst answer = typeof raw === 'string' ? raw : raw.text\n\t\t\tconst attachments = typeof raw === 'string' ? undefined : raw.attachments\n\t\t\tif (attachments?.length) this.pendingAnswerAttachments = attachments\n\t\t\treturn `User answered: ${answer}`\n\t\t},\n\t})\n)\n\ntools.set(\n\t'click_element_by_index',\n\ttool({\n\t\tdescription: 'Click element by index',\n\t\tinputSchema: z.object({\n\t\t\tindex: z.int().min(0), // 要点击的元素索引\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input) {\n\t\t\tconst result = await this.browserController.clickElement(input.index)\n\t\t\treturn result.message\n\t\t},\n\t})\n)\n\ntools.set(\n\t'input_text',\n\ttool({\n\t\tdescription: 'Click and type text into an interactive input element',\n\t\tinputSchema: z.object({\n\t\t\tindex: z.int().min(0), // 目标输入框的元素索引\n\t\t\ttext: z.string(), // 要输入的文本\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input) {\n\t\t\tconst result = await this.browserController.inputText(input.index, input.text)\n\t\t\treturn result.message\n\t\t},\n\t})\n)\n\ntools.set(\n\t'select_dropdown_option',\n\ttool({\n\t\tdescription:\n\t\t\t'Select dropdown option for interactive element index by the text of the option you want to select',\n\t\tinputSchema: z.object({\n\t\t\tindex: z.int().min(0), // 下拉框元素索引\n\t\t\ttext: z.string(), // 要选中的选项文本\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input) {\n\t\t\tconst result = await this.browserController.selectOption(input.index, input.text)\n\t\t\treturn result.message\n\t\t},\n\t})\n)\n\n/**\n * @note 参考自 browser-use\n */\ntools.set(\n\t'scroll',\n\ttool({\n\t\tdescription:\n\t\t\t'Scroll vertically. Without index: scrolls the document. With index: scrolls the container at that index (or its nearest scrollable ancestor). Use index of a data-scrollable element to scroll a specific area.',\n\t\tinputSchema: z.object({\n\t\t\tdown: z.boolean().default(true), // 是否向下滚动\n\t\t\tnum_pages: z.number().min(0).max(10).optional().default(0.1), // 滚动页数\n\t\t\tpixels: z.number().int().min(0).optional(), // 指定滚动的像素数(可选)\n\t\t\tindex: z.number().int().min(0).optional(), // 目标容器索引(可选)\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input) {\n\t\t\tconst result = await this.browserController.scroll({\n\t\t\t\t...input,\n\t\t\t\tnumPages: input.num_pages,\n\t\t\t})\n\t\t\treturn result.message\n\t\t},\n\t})\n)\n\n/**\n * @todo 表格需要专门的解析器来提取结构化数据。这个工具有点鸡肋。\n */\ntools.set(\n\t'scroll_horizontally',\n\ttool({\n\t\tdescription:\n\t\t\t'Scroll horizontally. Without index: scrolls the document. With index: scrolls the container at that index (or its nearest scrollable ancestor). Use index of a data-scrollable element to scroll a specific area.',\n\t\tinputSchema: z.object({\n\t\t\tright: z.boolean().default(true), // 是否向右滚动\n\t\t\tpixels: z.number().int().min(0), // 滚动的像素数\n\t\t\tindex: z.number().int().min(0).optional(), // 目标容器索引(可选)\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input) {\n\t\t\tconst result = await this.browserController.scrollHorizontally(input)\n\t\t\treturn result.message\n\t\t},\n\t})\n)\n\ntools.set(\n\t'execute_javascript',\n\ttool({\n\t\tdescription:\n\t\t\t'Execute JavaScript code on the current page. Supports async/await syntax. Use with caution! ' +\n\t\t\t'An `AbortSignal` named `signal` is available in scope: long-running async code MUST honor it ' +\n\t\t\t'(e.g. `await fetch(url, { signal })`, or `signal.throwIfAborted()` in loops)',\n\t\tinputSchema: z.object({\n\t\t\tscript: z.string(), // 要执行的 JavaScript 代码\n\t\t}),\n\t\texecute: async function (this: AgentRuntime, input, { signal }) {\n\t\t\tconst result = await this.browserController.executeJavascript(input.script, signal)\n\t\t\tsignal.throwIfAborted()\n\t\t\treturn result.message\n\t\t},\n\t})\n)\n\n// @todo send_keys(待实现)\n// @todo upload_file(待实现)\n// @todo extract_structured_data(待实现)\n","/**\n * Copyright (C) 2025 Alibaba Group Holding Limited\n * Copyright (C) 2026 SimonLuvRamen\n * All rights reserved.\n */\nimport type { BrowserState } from '../browser/dom'\nimport type { BrowserController } from '../browser/BrowserController'\nimport { InvokeError } from '../llm/errors'\nimport { LLM } from '../llm/LLM'\nimport type { ContentPart, Tool } from '../llm/types'\nimport { formatTerminalText } from '../shared/terminal'\nimport * as z from 'zod/v4'\n\nimport SYSTEM_PROMPT from './prompts/system_prompt.md?raw'\nimport { tools } from './tools'\nimport type {\n\tAgentActivity,\n\tAgentConfig,\n\tAgentReflection,\n\tAgentStatus,\n\tAgentStepEvent,\n\tAttachment,\n\tExecutionResult,\n\tHistoricalEvent,\n\tMacroToolInput,\n\tMacroToolResult,\n} from './types'\nimport { assert, fetchLlmsTxt, normalizeResponse, suppress, truncate, uid, waitFor } from './utils'\n\n/** 文本类附件注入 prompt 时的最大字符数,避免超大文件把上下文撑爆 */\nconst ATTACHMENT_TEXT_MAX_LENGTH = 20000\n\nexport { tool, type AgentTool, type ToolContext } from './tools'\nexport type * from './types'\n\nexport type AgentRuntimeConfig = AgentConfig & { browserController: BrowserController }\n\n/**\n * 用于浏览器自动化的 AI 智能体。\n *\n * @remarks\n * ## 反应式 Agent 循环\n * - 步骤\n * - 观察(收集当前环境与上下文信息)\n * - 思考(调用 LLM)\n * - 反思(评估历史、生成记忆、短期规划)\n * - 行动(给出接近下一个目标的动作)\n * - 执行(执行动作)\n * - 循环\n *\n * ## 事件系统\n * - `statuschange` - Agent 状态变更(idle → running → completed/error/stopped)\n * - `historychange` - 历史事件更新(持久化,属于 Agent 记忆的一部分)\n * - `activity` - 实时活动反馈(瞬态,仅用于 UI)\n * - `dispose` - 触发 Agent 清理\n *\n * ## 信息流\n * 1. **历史事件**(`history` 数组)\n * - 构成 Agent 记忆的持久事件流\n * - 跨步骤包含在 LLM 上下文中\n * - 类型:步骤、观察、用户接管、LLM 错误\n *\n * 2. **活动事件**(通过 `activity` 事件)\n * - 任务执行期间的瞬时 UI 反馈\n * - 不包含在 LLM 上下文中\n * - 类型:thinking、executing、executed、retrying、error\n */\nexport class AgentRuntime extends EventTarget {\n\t/** Agent 唯一 ID */\n\treadonly id = uid()\n\t/** 合并后的 Agent 配置(含最大步数上限) */\n\treadonly config: AgentRuntimeConfig & { maxSteps: number; historyWindow: number }\n\t/** 内部工具集合(按工具名索引) */\n\treadonly tools: typeof tools\n\t/** 用于 DOM 操作的 BrowserController */\n\treadonly browserController: BrowserController\n\n\t/** 当前任务描述 */\n\ttask = ''\n\t/** 当前任务 ID */\n\ttaskId = ''\n\t/** 历史事件 */\n\thistory: HistoricalEvent[] = []\n\t/** 该 Agent 是否已被销毁 */\n\tdisposed = false\n\n\t/**\n\t * 当 Agent 需要向用户提问时调用。\n\t * 若未设置,`ask_user` 工具将被禁用。\n\t * 实现应在 `signal` 中止时 reject 该 Promise。\n\t * 返回值可以是纯文本回答,也可以附带回答时用户上传的附件(图片等)。\n\t * @example onAskUser: (q) => window.prompt(q) || ''\n\t */\n\tonAskUser?: (\n\t\tquestion: string,\n\t\toptions?: { signal: AbortSignal }\n\t) => Promise<string | { text: string; attachments?: Attachment[] }>\n\n\t/** 当前 Agent 状态 */\n\t#status: AgentStatus = 'idle'\n\t/** LLM 实例(负责模型调用) */\n\t#llm: LLM\n\t/**\n\t * 任务取消原语:其 signal 会传递给 LLM fetch、工具(通过 `ctx.signal`)和异步回调。\n\t * 仅由 `stop`/`dispose`(任务进行中)或任务初始化时中止,且始终不带 reason,\n\t * 这样 `signal.reason` 保持为标准的 `AbortError`。\n\t */\n\t#abortController = new AbortController()\n\t/** 本轮任务收集的观察消息(暂存,稍后统一写入历史) */\n\t#observations: string[] = []\n\t/** 本次任务上传的附件;仅在任务第一步注入 LLM prompt,之后步骤不重复携带原文件以节省 token */\n\t#taskAttachments: Attachment[] = []\n\t/**\n\t * `ask_user` 工具收到带附件的回答后暂存于此,供下一步 `#assembleUserPrompt` 注入 LLM prompt 后清空。\n\t * 非私有字段:需要被 `tools/index.ts` 中的 `ask_user` 工具从外部写入。\n\t */\n\tpendingAnswerAttachments: Attachment[] = []\n\t/** 当前任务在 history 数组中的起始下标(用于跨任务保留历史时,把 prompt/循环检测限定在当前任务范围内) */\n\t#taskHistoryStart = 0\n\n\t/** 当前一次运行完全结束时 resolve。由 `stop()` 等待。 */\n\t#running: Promise<void> = Promise.resolve()\n\t#lastResult: ExecutionResult | null = null\n\n\t/** 单次任务执行期间的内部状态 */\n\t#states = {\n\t\t/** 累计等待时间(秒) */\n\t\ttotalWaitTime: 0,\n\t\t/** 用于检测页面导航 */\n\t\tlastURL: '',\n\t\t/** 浏览器状态 */\n\t\tbrowserState: null as BrowserState | null,\n\t}\n\n\t/** 构造函数:初始化配置、LLM、工具集,并注册重试事件监听与注入自定义工具 */\n\tconstructor(config: AgentRuntimeConfig) {\n\t\tsuper()\n\n\t\tthis.config = {\n\t\t\t...config,\n\t\t\tmaxSteps: config.maxSteps ?? 40,\n\t\t\thistoryWindow: config.historyWindow ?? 5,\n\t\t}\n\n\t\tthis.#llm = new LLM(this.config)\n\t\tthis.tools = new Map(tools)\n\t\tthis.browserController = config.browserController\n\n\t\tthis.#llm.addEventListener('retry', (e) => {\n\t\t\tconst { attempt, maxAttempts, lastError } = (e as CustomEvent).detail\n\t\t\tthis.#emitActivity({ type: 'retrying', attempt, maxAttempts })\n\t\t\tthis.history.push({\n\t\t\t\ttype: 'error',\n\t\t\t\tmessage: String(lastError),\n\t\t\t\trawResponse: (lastError as InvokeError).rawResponse,\n\t\t\t})\n\t\t\tthis.history.push({\n\t\t\t\ttype: 'retry',\n\t\t\t\tmessage: `LLM retry attempt ${attempt} of ${maxAttempts}`,\n\t\t\t\tattempt,\n\t\t\t\tmaxAttempts,\n\t\t\t})\n\t\t\tthis.#emitHistoryChange()\n\t\t})\n\n\t\tif (this.config.customTools) {\n\t\t\tfor (const [name, tool] of Object.entries(this.config.customTools)) {\n\t\t\t\tif (tool === null) {\n\t\t\t\t\tthis.tools.delete(name)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tthis.tools.set(name, tool)\n\t\t\t}\n\t\t}\n\n\t\tif (!this.config.experimentalScriptExecutionTool) {\n\t\t\tthis.tools.delete('execute_javascript')\n\t\t}\n\t}\n\n\t/** 获取当前 Agent 状态 */\n\tget status(): AgentStatus {\n\t\treturn this.#status\n\t}\n\n\t/** 最近一次运行的结果,首次运行完成前为 `null`。 */\n\tget lastResult(): ExecutionResult | null {\n\t\treturn this.#lastResult\n\t}\n\n\t/** 当前任务自身的历史事件(不含之前任务留下的记录),用于 prompt 拼装、循环检测等按任务隔离的场景 */\n\tget #currentTaskHistory(): HistoricalEvent[] {\n\t\treturn this.history.slice(this.#taskHistoryStart)\n\t}\n\n\t/** 触发 statuschange 事件 */\n\t#emitStatusChange(): void {\n\t\tthis.dispatchEvent(new Event('statuschange'))\n\t}\n\n\t/** 触发 historychange 事件 */\n\t#emitHistoryChange(pushHistoricalEvent?: HistoricalEvent): void {\n\t\tif (pushHistoricalEvent) this.history.push(pushHistoricalEvent)\n\t\tthis.dispatchEvent(new Event('historychange'))\n\t}\n\n\t/**\n\t * 触发 activity 事件 - 用于瞬态的 UI 反馈\n\t * @param activity - 当前的 Agent 活动\n\t */\n\t#emitActivity(activity: AgentActivity): void {\n\t\tthis.dispatchEvent(new CustomEvent('activity', { detail: activity }))\n\t}\n\n\t/** 更新状态并触发事件 */\n\t#setStatus(status: AgentStatus): void {\n\t\tif (this.#status !== status) {\n\t\t\tthis.#status = status\n\t\t\tthis.#emitStatusChange()\n\t\t}\n\t}\n\n\t/**\n\t * 向历史事件流推送一条观察消息。\n\t * 它会出现在 <agent_history> 中,并在跨步骤的记忆中保持持久。\n\t * @experimental @internal\n\t * @note 历史变更会在下一步开始前触发\n\t */\n\tpushObservation(content: string): void {\n\t\tthis.#observations.push(content)\n\t}\n\n\t/**\n\t * 停止当前任务,并等待运行完全结束(包括生命周期钩子)。\n\t * @note 切勿在生命周期钩子中 await .stop()。\n\t */\n\tasync stop(): Promise<void> {\n\t\tif (this.#status !== 'running') return\n\t\tthis.#abortController.abort()\n\t\tawait this.#running\n\t}\n\n\t/**\n\t * 外部错误(前置检查/配置/钩子)会直接抛出;\n\t * Agent 内部错误会被捕获并加入历史,同时返回失败结果\n\t */\n\tasync execute(task: string, attachments: Attachment[] = []): Promise<ExecutionResult> {\n\t\t// 前置检查\n\t\tif (this.disposed) throw new Error('PageAgent has been disposed. Create a new instance.')\n\t\tif (this.#status === 'running') throw new Error('A task is already running.')\n\t\tif (!task) throw new Error('Task is required')\n\n\t\tthis.task = task\n\t\tthis.taskId = uid()\n\t\tthis.#taskAttachments = attachments\n\t\tthis.pendingAnswerAttachments = [] // 新任务开始,清空上一任务遗留的追问附件\n\n\t\t// @note 跨任务保留 history(不再清空),便于 UI 展示历次任务的完整操作记录。\n\t\t// 通过 task_start 标记本任务的起点,prompt 拼装/循环检测等仅在当前任务范围内取用(见 #currentTaskHistory)。\n\t\tthis.#taskHistoryStart = this.history.length\n\t\tthis.#emitHistoryChange({\n\t\t\ttype: 'task_start',\n\t\t\ttask,\n\t\t\t...(attachments.length > 0 ? { attachmentNames: attachments.map((a) => a.name) } : {}),\n\t\t})\n\t\tthis.#observations = []\n\t\tthis.#states = { totalWaitTime: 0, lastURL: '', browserState: null }\n\t\tthis.#abortController = new AbortController()\n\t\tconst signal = this.#abortController.signal\n\n\t\tlet resolveRunning!: () => void\n\t\tthis.#running = new Promise<void>((r) => (resolveRunning = r))\n\n\t\tthis.#setStatus('running')\n\t\tthis.#emitHistoryChange()\n\n\t\t// 若未设置 onAskUser,则禁用 ask_user 工具\n\t\tif (!this.onAskUser) this.tools.delete('ask_user')\n\n\t\tconst onBeforeStep = this.config.onBeforeStep\n\t\tconst onAfterStep = this.config.onAfterStep\n\t\tconst onBeforeTask = this.config.onBeforeTask\n\t\tconst onAfterTask = this.config.onAfterTask\n\t\tconst stepDelay = this.config.stepDelay ?? 0.2\n\t\tconst maxSteps = this.config.maxSteps\n\n\t\tlet step = 0\n\t\tlet taskResult: ExecutionResult\n\t\tlet finalStatus: AgentStatus = 'error'\n\n\t\tawait suppress(() => this.browserController.showMask())\n\n\t\t// 优雅退出\n\t\ttry {\n\t\t\tawait onBeforeTask?.(this)\n\n\t\t\twhile (true) {\n\t\t\t\tif (step >= maxSteps) {\n\t\t\t\t\tconst message = 'Step count exceeded maximum limit'\n\t\t\t\t\tconsole.error(message)\n\t\t\t\t\tthis.#emitActivity({ type: 'error', message: message })\n\t\t\t\t\tthis.#emitHistoryChange({ type: 'error', message: message })\n\t\t\t\t\ttaskResult = { success: false, data: message, history: this.#currentTaskHistory }\n\t\t\t\t\tthis.#lastResult = taskResult\n\t\t\t\t\tfinalStatus = 'error'\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tawait onBeforeStep?.(this, step)\n\n\t\t\t\t// 处理 Agent 内部错误\n\t\t\t\ttry {\n\t\t\t\t\tconsole.group(`step: ${step}`)\n\n\t\t\t\t\t// @note 把 stepDelay 视为下一步的一部分很方便。\n\t\t\t\t\t// 也许应移到一个独立的 try 块中以获得更好的语义?\n\t\t\t\t\tif (step > 0) await waitFor(stepDelay, signal)\n\n\t\t\t\t\tsignal.throwIfAborted()\n\n\t\t\t\t\t// 观察\n\n\t\t\t\t\tconsole.log(formatTerminalText('👀 Observing...', 'blue', true))\n\n\t\t\t\t\tthis.#states.browserState = await this.browserController.getBrowserState()\n\t\t\t\t\tawait this.#handleObservations(step)\n\n\t\t\t\t\t// 组装提示词\n\n\t\t\t\t\tconst messages = [\n\t\t\t\t\t\t{ role: 'system' as const, content: this.#getSystemPrompt() },\n\t\t\t\t\t\t{ role: 'user' as const, content: await this.#assembleUserPrompt() },\n\t\t\t\t\t]\n\n\t\t\t\t\tconst macroTool = { AgentOutput: this.#packMacroTool() }\n\n\t\t\t\t\t// 调用 LLM\n\n\t\t\t\t\tconsole.log(formatTerminalText('🧠 Thinking...', 'blue', true))\n\t\t\t\t\tthis.#emitActivity({ type: 'thinking' })\n\n\t\t\t\t\tconst result = await this.#llm.invoke(messages, macroTool, signal, {\n\t\t\t\t\t\ttoolChoiceName: 'AgentOutput',\n\t\t\t\t\t\tnormalizeResponse: (res) => normalizeResponse(res, this.tools),\n\t\t\t\t\t})\n\n\t\t\t\t\t// 组装历史\n\n\t\t\t\t\tconst macroResult = result.toolResult as MacroToolResult\n\t\t\t\t\tconst input = macroResult.input\n\t\t\t\t\tconst output = macroResult.output\n\t\t\t\t\tconst reflection: Partial<AgentReflection> = {\n\t\t\t\t\t\tevaluation_previous_goal: input.evaluation_previous_goal,\n\t\t\t\t\t\tmemory: input.memory,\n\t\t\t\t\t\tnext_goal: input.next_goal,\n\t\t\t\t\t}\n\t\t\t\t\tconst actionName = Object.keys(input.action)[0]\n\t\t\t\t\tconst action: AgentStepEvent['action'] = {\n\t\t\t\t\t\tname: actionName,\n\t\t\t\t\t\tinput: input.action[actionName],\n\t\t\t\t\t\toutput: output,\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.#emitHistoryChange({\n\t\t\t\t\t\ttype: 'step',\n\t\t\t\t\t\tstepIndex: step,\n\t\t\t\t\t\treflection,\n\t\t\t\t\t\taction,\n\t\t\t\t\t\tusage: result.usage,\n\t\t\t\t\t\trawResponse: result.rawResponse,\n\t\t\t\t\t\trawRequest: result.rawRequest,\n\t\t\t\t\t})\n\n\t\t\t\t\tif (actionName === 'done') {\n\t\t\t\t\t\tconst success = action.input?.success ?? false\n\t\t\t\t\t\tconst data = action.input?.text || 'no text provided'\n\t\t\t\t\t\tconsole.log(formatTerminalText('Task completed', 'green', true), success, data)\n\t\t\t\t\t\ttaskResult = { success, data, history: this.#currentTaskHistory }\n\t\t\t\t\t\tthis.#lastResult = taskResult\n\t\t\t\t\t\tfinalStatus = 'completed'\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t// catch 块绝不能抛出错误,否则若 finally 块也抛出错误,错误可能被覆盖。\n\n\t\t\t\t\tconst isAbortError = (error as any)?.name === 'AbortError'\n\t\t\t\t\tif (!isAbortError) console.error('Task failed', error)\n\t\t\t\t\tconst message = isAbortError ? 'Task aborted' : String(error)\n\t\t\t\t\tthis.#emitActivity({ type: 'error', message: message })\n\t\t\t\t\tthis.#emitHistoryChange({ type: 'error', message: message, rawResponse: error })\n\t\t\t\t\ttaskResult = { success: false, data: message, history: this.#currentTaskHistory }\n\t\t\t\t\tthis.#lastResult = taskResult\n\t\t\t\t\tfinalStatus = isAbortError ? 'stopped' : 'error'\n\t\t\t\t\tbreak\n\t\t\t\t} finally {\n\t\t\t\t\t// finally 块会在上面的 break 之前执行。\n\n\t\t\t\t\tconsole.groupEnd()\n\t\t\t\t\t// @note 钩子可能抛出错误。\n\t\t\t\t\t// 它会覆盖上面的 `break`,并作为外部错误被处理。\n\t\t\t\t\t// 符合预期。\n\t\t\t\t\tawait onAfterStep?.(this, this.#currentTaskHistory)\n\t\t\t\t}\n\n\t\t\t\tstep++\n\t\t\t} // while\n\n\t\t\tawait onAfterTask?.(this, taskResult)\n\n\t\t\treturn taskResult\n\t\t} catch (error) {\n\t\t\tthis.#emitActivity({ type: 'error', message: String(error) })\n\t\t\tfinalStatus = 'error'\n\t\t\tthrow error\n\t\t} finally {\n\t\t\tawait suppress(() => this.browserController.cleanUpHighlights())\n\t\t\tawait suppress(() => this.browserController.hideMask())\n\t\t\tthis.#abortController.abort()\n\t\t\tresolveRunning()\n\t\t\tthis.#setStatus(finalStatus)\n\t\t}\n\t}\n\n\t/**\n\t * 将所有工具合并为单个 MacroTool,其输入如下:\n\t * - thinking: string\n\t * - evaluation_previous_goal: string\n\t * - memory: string\n\t * - next_goal: string\n\t * - action: { toolName: toolInput }\n\t * 其中 action 必须从 this.tools 中定义的工具中选择\n\t */\n\t#packMacroTool(): Tool<MacroToolInput, MacroToolResult> {\n\t\tconst tools = this.tools\n\n\t\tconst actionSchemas = Array.from(tools.entries()).map(([toolName, tool]) => {\n\t\t\treturn z.object({ [toolName]: tool.inputSchema }).describe(tool.description)\n\t\t})\n\n\t\tconst actionSchema = z.union(actionSchemas as unknown as [z.ZodType, z.ZodType, ...z.ZodType[]])\n\n\t\tconst macroToolSchema = z.object({\n\t\t\t// thinking: z.string().optional(),\n\t\t\tevaluation_previous_goal: z.string().optional(),\n\t\t\tmemory: z.string().optional(),\n\t\t\tnext_goal: z.string().optional(),\n\t\t\taction: actionSchema,\n\t\t})\n\n\t\treturn {\n\t\t\tdescription: 'You MUST call this tool every step!',\n\t\t\tinputSchema: macroToolSchema as z.ZodType<MacroToolInput>,\n\t\t\texecute: async (input: MacroToolInput): Promise<MacroToolResult> => {\n\t\t\t\tconst signal = this.#abortController.signal\n\t\t\t\tsignal.throwIfAborted()\n\n\t\t\t\tconsole.log(formatTerminalText('MacroTool input', 'blue', true), input)\n\t\t\t\tconst action = input.action\n\n\t\t\t\tconst toolName = Object.keys(action)[0]\n\t\t\t\tconst toolInput = action[toolName]\n\n\t\t\t\t// 构建反思文本,仅包含非空字段\n\t\t\t\tconst reflectionLines: string[] = []\n\t\t\t\tif (input.evaluation_previous_goal)\n\t\t\t\t\treflectionLines.push(`✅: ${input.evaluation_previous_goal}`)\n\t\t\t\tif (input.memory) reflectionLines.push(`💾: ${input.memory}`)\n\t\t\t\tif (input.next_goal) reflectionLines.push(`🎯: ${input.next_goal}`)\n\n\t\t\t\tconst reflectionText = reflectionLines.length > 0 ? reflectionLines.join('\\n') : ''\n\n\t\t\t\tif (reflectionText) {\n\t\t\t\t\tconsole.log(reflectionText)\n\t\t\t\t}\n\n\t\t\t\t// 查找对应的工具\n\t\t\t\tconst tool = tools.get(toolName)\n\t\t\t\tassert(tool, `Tool ${toolName} not found`)\n\n\t\t\t\tconsole.log(formatTerminalText(`Executing tool: ${toolName}`, 'blue', true), toolInput)\n\n\t\t\t\t// 触发 executing 活动\n\t\t\t\tthis.#emitActivity({ type: 'executing', tool: toolName, input: toolInput })\n\n\t\t\t\tconst startTime = Date.now()\n\n\t\t\t\tconst result = await tool.execute.bind(this)(toolInput, { signal })\n\t\t\t\t// 即使工具忽略了 signal 并正常 resolve,也要强制中止。\n\t\t\t\tsignal.throwIfAborted()\n\n\t\t\t\tconst duration = Date.now() - startTime\n\t\t\t\tconsole.log(\n\t\t\t\t\tformatTerminalText(`Tool (${toolName}) executed for ${duration}ms`, 'green', true),\n\t\t\t\t\tresult\n\t\t\t\t)\n\n\t\t\t\t// 触发 executed 活动\n\t\t\t\tthis.#emitActivity({\n\t\t\t\t\ttype: 'executed',\n\t\t\t\t\ttool: toolName,\n\t\t\t\t\tinput: toolInput,\n\t\t\t\t\toutput: result,\n\t\t\t\t\tduration,\n\t\t\t\t})\n\n\t\t\t\t// 累计等待时间\n\t\t\t\tif (toolName === 'wait') {\n\t\t\t\t\tthis.#states.totalWaitTime += toolInput?.seconds || 0\n\t\t\t\t} else {\n\t\t\t\t\tthis.#states.totalWaitTime = 0\n\t\t\t\t}\n\n\t\t\t\t// 返回结构化结果\n\t\t\t\treturn {\n\t\t\t\t\tinput,\n\t\t\t\t\toutput: result,\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t}\n\n\t/**\n\t * 获取系统提示词,根据配置的语言动态替换语言设置\n\t */\n\t#getSystemPrompt(): string {\n\t\tif (this.config.customSystemPrompt) {\n\t\t\treturn this.config.customSystemPrompt\n\t\t}\n\n\t\tconst targetLanguage = this.config.language === 'zh-CN' ? '中文' : 'English'\n\t\tconst systemPrompt = SYSTEM_PROMPT.replace(\n\t\t\t/Default working language: \\*\\*.*?\\*\\*/,\n\t\t\t`Default working language: **${targetLanguage}**`\n\t\t)\n\n\t\treturn systemPrompt\n\t}\n\n\t/**\n\t * 从配置中获取指令\n\t */\n\tasync #getInstructions(): Promise<string> {\n\t\tconst { instructions, experimentalLlmsTxt } = this.config\n\n\t\tconst systemInstructions = instructions?.system?.trim()\n\t\tlet pageInstructions: string | undefined\n\n\t\tconst url = this.#states.browserState?.url || ''\n\t\tif (instructions?.getPageInstructions && url) {\n\t\t\ttry {\n\t\t\t\tpageInstructions = instructions.getPageInstructions(url)?.trim()\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\n\t\t\t\t\tformatTerminalText('[PageAgent] Failed to execute getPageInstructions callback:', 'red'),\n\t\t\t\t\terror\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tconst llmsTxt = experimentalLlmsTxt && url ? await fetchLlmsTxt(url) : undefined\n\n\t\tif (!systemInstructions && !pageInstructions && !llmsTxt) return ''\n\n\t\tlet result = '<instructions>\\n'\n\n\t\tif (systemInstructions) {\n\t\t\tresult += `<system_instructions>\\n${systemInstructions}\\n</system_instructions>\\n`\n\t\t}\n\n\t\tif (pageInstructions) {\n\t\t\tresult += `<page_instructions>\\n${pageInstructions}\\n</page_instructions>\\n`\n\t\t}\n\n\t\tif (llmsTxt) {\n\t\t\tresult += `<llms_txt>\\n${llmsTxt}\\n</llms_txt>\\n`\n\t\t}\n\n\t\tresult += '</instructions>\\n\\n'\n\n\t\treturn result\n\t}\n\n\t/**\n\t * 在每一步之前生成系统观察\n\t * @todo 控制台错误\n\t */\n\tasync #handleObservations(step: number): Promise<void> {\n\t\t// 累计等待时间警告\n\t\tif (this.#states.totalWaitTime >= 3) {\n\t\t\tthis.pushObservation(\n\t\t\t\t`You have waited ${this.#states.totalWaitTime} seconds accumulatively. ` +\n\t\t\t\t\t`DO NOT wait any longer unless you have a good reason.`\n\t\t\t)\n\t\t}\n\n\t\t// 循环检测:相同的动作 + 相同的输入连续重复 3 次\n\t\tconst recentSteps = this.#currentTaskHistory\n\t\t\t.filter((e): e is AgentStepEvent => e.type === 'step')\n\t\t\t.slice(-3)\n\t\tif (recentSteps.length === 3) {\n\t\t\tconst isSameAction = (a: AgentStepEvent, b: AgentStepEvent) =>\n\t\t\t\ta.action.name === b.action.name &&\n\t\t\t\tJSON.stringify(a.action.input) === JSON.stringify(b.action.input)\n\t\t\tif (\n\t\t\t\tisSameAction(recentSteps[0], recentSteps[1]) &&\n\t\t\t\tisSameAction(recentSteps[1], recentSteps[2])\n\t\t\t) {\n\t\t\t\tthis.pushObservation(\n\t\t\t\t\t`⚠️ Loop detected: you repeated the exact same action ` +\n\t\t\t\t\t\t`(\"${recentSteps[0].action.name}\" with identical input) 3 times in a row with no new result. ` +\n\t\t\t\t\t\t`STOP repeating it. If the target genuinely does not exist (e.g. search returns empty), ` +\n\t\t\t\t\t\t`report this explicitly and call \\`done\\`, or try a clearly different action/strategy instead.`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// 检测 URL 变化\n\t\tconst currentURL = this.#states.browserState?.url || ''\n\t\tif (currentURL !== this.#states.lastURL) {\n\t\t\tthis.pushObservation(`Page navigated to → ${currentURL}`)\n\t\t\tthis.#states.lastURL = currentURL\n\t\t\tawait waitFor(0.5) // 等待页面稳定\n\t\t}\n\n\t\t// 剩余步数警告\n\t\tconst remaining = this.config.maxSteps - step\n\t\tif (remaining === 5) {\n\t\t\tthis.pushObservation(\n\t\t\t\t`⚠️ Only ${remaining} steps remaining. ` +\n\t\t\t\t\t`Consider wrapping up or calling done with partial results.`\n\t\t\t)\n\t\t} else if (remaining === 2) {\n\t\t\tthis.pushObservation(\n\t\t\t\t`⚠️ Critical: Only ${remaining} steps left! You must finish the task or call done immediately.`\n\t\t\t)\n\t\t}\n\n\t\t// 将观察推入历史并触发事件\n\t\tif (this.#observations.length > 0) {\n\t\t\tfor (const content of this.#observations) {\n\t\t\t\tthis.history.push({ type: 'observation', content })\n\t\t\t\tconsole.log(formatTerminalText('Observation:', 'cyan'), content)\n\t\t\t}\n\t\t\tthis.#observations = []\n\t\t\tthis.#emitHistoryChange()\n\t\t}\n\t}\n\n\t/**\n\t * 组装发给 LLM 的完整用户提示词(含指令、Agent 状态、历史、浏览器状态)\n\t */\n\tasync #assembleUserPrompt(): Promise<string | ContentPart[]> {\n\t\tconst browserState = this.#states.browserState!\n\n\t\tlet prompt = ''\n\n\t\t// <instructions> (可选)\n\n\t\tprompt += await this.#getInstructions()\n\n\t\t// <agent_state>\n\t\t// - <user_request>\n\t\t// - <step_info>\n\t\t// <agent_state>\n\n\t\t// @note 仅取当前任务自身的历史(history 现在跨任务持久保留,供 UI 展示)\n\t\tconst currentTaskHistory = this.#currentTaskHistory\n\t\tconst stepCount = currentTaskHistory.filter((e) => e.type === 'step').length\n\n\t\tprompt += '<agent_state>\\n'\n\t\tprompt += '<user_request>\\n'\n\t\tprompt += `${this.task}\\n`\n\t\tprompt += '</user_request>\\n'\n\t\tprompt += '<step_info>\\n'\n\t\tprompt += `Step ${stepCount + 1} of ${this.config.maxSteps} max possible steps\\n`\n\t\tprompt += `Current time: ${new Date().toLocaleString()}\\n`\n\t\tprompt += '</step_info>\\n'\n\t\tprompt += '</agent_state>\\n\\n'\n\n\t\t// <agent_history>\n\t\t// - <step_N> 用于步骤\n\t\t// - <sys> 用于观察和系统消息\n\t\t//\n\t\t// @edit @workaround 无上限的历史增长\n\t\t// 无限回放每个过去步骤的完整反思/动作文本,会让提示词(进而\"思考\"延迟)每一步都无上限地增长。\n\t\t// 只有最近的 historyWindow 步保留完整详情;更早的会被压缩成一行 ——\n\t\t// 模型自己的 `memory` 字段才是用来向前传递更早上下文的载体。\n\t\tconst windowStart = Math.max(0, stepCount - this.config.historyWindow)\n\n\t\tprompt += '<agent_history>\\n'\n\n\t\tlet stepIndex = 0\n\t\tfor (const event of currentTaskHistory) {\n\t\t\tif (event.type === 'step') {\n\t\t\t\tstepIndex++\n\t\t\t\tif (stepIndex <= windowStart) {\n\t\t\t\t\tprompt += `<step_${stepIndex}>Action: ${event.action.name} — Next Goal: ${event.reflection.next_goal}</step_${stepIndex}>\\n`\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tprompt += `<step_${stepIndex}>\\n`\n\t\t\t\tprompt += `Evaluation of Previous Step: ${event.reflection.evaluation_previous_goal}\\n`\n\t\t\t\tprompt += `Memory: ${event.reflection.memory}\\n`\n\t\t\t\tprompt += `Next Goal: ${event.reflection.next_goal}\\n`\n\t\t\t\tprompt += `Action Results: ${event.action.output}\\n`\n\t\t\t\tprompt += `</step_${stepIndex}>\\n`\n\t\t\t} else if (event.type === 'observation') {\n\t\t\t\tprompt += `<sys>${event.content}</sys>\\n`\n\t\t\t} else if (event.type === 'user_takeover') {\n\t\t\t\tprompt += `<sys>User took over control and made changes to the page</sys>\\n`\n\t\t\t} else if (event.type === 'error') {\n\t\t\t\t// 错误事件主要用于 UI 渲染,不包含在 LLM 上下文中,\n\t\t\t\t// 以免瞬态错误污染 Agent 的推理过程\n\t\t\t}\n\t\t}\n\n\t\tprompt += '</agent_history>\\n\\n'\n\n\t\t// <browser_state>\n\n\t\tlet pageContent = browserState.content\n\t\tif (this.config.transformPageContent) {\n\t\t\tpageContent = await this.config.transformPageContent(pageContent)\n\t\t}\n\n\t\tprompt += '<browser_state>\\n'\n\t\tprompt += browserState.header + '\\n'\n\t\tprompt += pageContent + '\\n'\n\t\tprompt += browserState.footer + '\\n\\n'\n\t\tprompt += '</browser_state>\\n\\n'\n\n\t\t// 附件仅注入一次:任务级附件限第一步;追问回答附带的附件限下一步,均在消费后清空避免重复消耗 token\n\t\tif (stepCount === 0 && this.#taskAttachments.length > 0) {\n\t\t\treturn await this.#injectAttachments(prompt, this.#taskAttachments)\n\t\t}\n\t\tif (this.pendingAnswerAttachments.length > 0) {\n\t\t\tconst attachments = this.pendingAnswerAttachments\n\t\t\tthis.pendingAnswerAttachments = []\n\t\t\treturn await this.#injectAttachments(prompt, attachments)\n\t\t}\n\n\t\treturn prompt\n\t}\n\n\t/**\n\t * 把附件内容注入 prompt:图片转为 image_url 片段;文本类文件解码后截断拼进文字部分。\n\t * 仅当存在图片附件时才返回多模态数组,纯文本附件保持返回字符串(不影响不支持 vision 的模型)。\n\t */\n\tasync #injectAttachments(\n\t\tprompt: string,\n\t\tattachments: Attachment[]\n\t): Promise<string | ContentPart[]> {\n\t\tconst textSegments: string[] = [prompt]\n\t\tconst imageParts: ContentPart[] = []\n\n\t\tfor (const attachment of attachments) {\n\t\t\tif (attachment.mimeType.startsWith('image/')) {\n\t\t\t\timageParts.push({ type: 'image_url', image_url: { url: attachment.dataUrl } })\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst text = await suppress(() => fetch(attachment.dataUrl).then((r) => r.text()))\n\t\t\ttextSegments.push(\n\t\t\t\t`<uploaded_file name=\"${attachment.name}\">\\n${truncate(text ?? '', ATTACHMENT_TEXT_MAX_LENGTH)}\\n</uploaded_file>`\n\t\t\t)\n\t\t}\n\n\t\tif (imageParts.length === 0) return textSegments.join('\\n\\n')\n\n\t\treturn [{ type: 'text', text: textSegments.join('\\n\\n') }, ...imageParts]\n\t}\n\n\t/** 销毁 Agent:中止任务、释放浏览器控制器,并触发 dispose 事件(一次性) */\n\tdispose() {\n\t\tif (this.disposed) return\n\t\tthis.disposed = true\n\n\t\tconsole.log('Disposing PageAgent...')\n\t\tconst errors: unknown[] = []\n\t\tconst cleanUp = (callback: () => void) => {\n\t\t\ttry {\n\t\t\t\tcallback()\n\t\t\t} catch (error: unknown) {\n\t\t\t\terrors.push(error)\n\t\t\t}\n\t\t}\n\n\t\tcleanUp(() => this.#abortController.abort())\n\t\tcleanUp(() => this.browserController.dispose())\n\t\t// this.history = []\n\n\t\t// 触发 dispose 事件供 UI 清理\n\t\tcleanUp(() => this.dispatchEvent(new Event('dispose')))\n\n\t\tcleanUp(() => this.config.onDispose?.(this))\n\n\t\tif (errors.length === 1) throw errors[0]\n\t\tif (errors.length > 1) throw new AggregateError(errors, 'PageAgent disposal failed')\n\t}\n}\n","import { BrowserController, type BrowserControllerConfig } from '../browser/BrowserController'\nimport { UI, type UIConfig } from '../ui/UI'\nimport { AgentRuntime } from './AgentRuntime'\nimport type { AgentConfig } from './types'\n\n/** MyPageAgent 的配置:Agent 配置 + 浏览器控制器配置 + UI 配置(语言除外) */\nexport type MyPageAgentConfig = Omit<AgentConfig, 'experimentalScriptExecutionTool'> &\n\tBrowserControllerConfig &\n\tOmit<UIConfig, 'language'>\n\n/**\n * 完整页面智能体:组合了 AgentRuntime、BrowserController 与 UI\n */\nexport class MyPageAgent extends AgentRuntime {\n\t/** UI 实例(用于用户交互与反馈) */\n\treadonly ui: UI\n\n\t/** 构造函数:创建浏览器控制器、父类运行时与 UI 实例 */\n\tconstructor(config: MyPageAgentConfig) {\n\t\tconst browserController = new BrowserController({\n\t\t\t...config,\n\t\t\tenableMask: config.enableMask ?? true,\n\t\t})\n\n\t\tsuper({ ...config, experimentalScriptExecutionTool: false, browserController })\n\n\t\tthis.ui = new UI(this, {\n\t\t\tlanguage: config.language,\n\t\t\tpromptForNextTask: config.promptForNextTask,\n\t\t})\n\t}\n}\n","// 包入口文件:统一导出智能体(Agent)的主类、配置与工具相关类型,并提供创建实例的快捷函数\nimport { MyPageAgent, type MyPageAgentConfig } from './agent/MyPageAgent'\n\nexport { MyPageAgent } // 导出智能体主类,供外部实例化使用\nexport type { MyPageAgentConfig } // 导出智能体配置类型,用于约束 createAgent 的入参\nexport { tool } from './agent/tools' // 导出工具定义辅助函数\nexport type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from './agent/types' // 导出智能体活动、状态、执行结果与历史事件等通用类型\n\n/**\n * 创建智能体实例的快捷函数,内部直接实例化 MyPageAgent。\n * @param config 智能体的配置对象\n * @returns 创建好的 MyPageAgent 实例\n */\nexport function createAgent(config: MyPageAgentConfig): MyPageAgent {\n\treturn new MyPageAgent(config)\n}\n"],"mappings":"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"}
|