@coolkiller007/my-page-agent 0.1.13 → 0.1.15

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.js CHANGED
@@ -1,4 +1,4 @@
1
- (function() { try { if (typeof document != "undefined") { var elementStyle = document.createElement("style"); elementStyle.appendChild(document.createTextNode("._wrapper_ur79v_1 {\n position: fixed;\n inset: 0;\n z-index: 2147483641; /* 确保在所有元素之上,除了 Agent UI */\n cursor: wait;\n overflow: hidden;\n\n display: none;\n}\n\n._wrapper_ur79v_1._visible_ur79v_11 {\n display: block;\n}\n/* AI 光标样式 */\n._cursor_eh09s_2 {\n position: absolute;\n width: var(--cursor-size, 48px);\n height: var(--cursor-size, 48px);\n pointer-events: none;\n z-index: 10000;\n}\n\n._cursorBorder_eh09s_10 {\n position: absolute;\n width: 100%;\n height: 100%;\n background: linear-gradient(45deg, rgb(57, 182, 255), rgb(189, 69, 251));\n mask-image: url(\"data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201024%201024'%20fill='none'%3e%3cpath%20d='M287.232%20128l514.752%20387.008L574.912%20576l149.376%20299.52-103.04%2051.392L472.32%20628.544l-185.856%20143.424z'%20stroke='%23000000'%20stroke-width='56'%20stroke-linejoin='miter'/%3e%3c/svg%3e\");\n mask-size: 100% 100%;\n mask-repeat: no-repeat;\n margin-left: -16px;\n margin-top: -7px;\n}\n\n._cursorFilling_eh09s_22 {\n position: absolute;\n width: 100%;\n height: 100%;\n background: url(\"data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201024%201024'%3e%3cpath%20d='M287.232%20128l514.752%20387.008L574.912%20576l149.376%20299.52-103.04%2051.392L472.32%20628.544l-185.856%20143.424z'%20fill='%23ffffff'/%3e%3c/svg%3e\");\n background-size: 100% 100%;\n background-repeat: no-repeat;\n margin-left: -16px;\n margin-top: -7px;\n}\n\n._cursorRipple_eh09s_33 {\n position: absolute;\n width: 100%;\n height: 100%;\n pointer-events: none;\n margin-left: -50%;\n margin-top: -50%;\n\n &::after {\n content: '';\n opacity: 0;\n position: absolute;\n inset: 0;\n border: 4px solid rgba(57, 182, 255, 1);\n border-radius: 50%;\n }\n}\n\n._cursor_eh09s_2._clicking_eh09s_51 ._cursorRipple_eh09s_33::after {\n animation: _cursor-ripple_eh09s_1 300ms ease-out forwards;\n}\n\n@keyframes _cursor-ripple_eh09s_1 {\n 0% {\n transform: scale(0);\n opacity: 1;\n }\n 100% {\n transform: scale(2);\n opacity: 0;\n }\n}\n._wrapper_h452n_1 {\n position: fixed;\n bottom: 100px;\n left: 50%;\n transform: translateX(-50%) translateY(20px);\n opacity: 0;\n z-index: 2147483642; /* 比 SimulatorMask 高一层 */\n box-sizing: border-box;\n\n /* 普通流布局:历史弹窗、面板依次纵向排列,靠子元素自身尺寸撑开,\n * 不再用 position:absolute + 手算偏移量拼位置(这套算法之前连续出过两次 bug:\n * 弹窗被状态条盖住、状态条运行态时下方角没跟着圆)。 */\n display: flex;\n flex-direction: column;\n align-items: center;\n\n * {\n box-sizing: border-box;\n }\n\n /* 宽度取窗口 40%,用 clamp 防止超窄/超宽窗口下过小或过大 */\n --width: clamp(280px, 40vw, 640px);\n --height: 40px; /* 状态条高度 */\n --input-height: 80px; /* 输入区高度 */\n --border-radius: 16px;\n\n --side-space: 12px; /* 控制栏两侧的间距 */\n --history-width: calc(var(--width) - var(--side-space) * 2);\n\n --color-1: rgb(57, 182, 255);\n --color-2: rgb(189, 69, 251);\n --color-3: rgb(255, 87, 51);\n --color-4: rgb(255, 214, 0);\n\n width: var(--width);\n\n transition:\n opacity 0.3s ease-in-out,\n transform 0.3s ease-in-out;\n\n /* 响应式设计 */\n @media (max-width: 480px) {\n width: calc(100vw - 40px);\n --width: calc(100vw - 40px);\n }\n}\n\n@keyframes _mask-running_h452n_1 {\n from {\n transform: translateX(-100%);\n }\n to {\n transform: translateX(100%);\n }\n}\n\n/* 状态条 + 输入区的面板容器;.background 光效挂在这一层,.panelInner 负责裁出圆角 */\n._panel_h452n_57 {\n position: relative;\n width: 100%;\n}\n\n._background_h452n_57 {\n position: absolute;\n inset: -2px;\n border-radius: calc(var(--border-radius) + 4px);\n filter: blur(4px);\n overflow: hidden;\n pointer-events: none;\n\n &::before {\n content: '';\n pointer-events: none;\n position: absolute;\n width: 100%;\n height: 100%;\n left: 0;\n top: 0;\n\n background-image: linear-gradient(\n to bottom left,\n var(--color-1),\n var(--color-2),\n var(--color-1)\n );\n animation: _mask-running_h452n_1 2s linear infinite;\n }\n &::after {\n content: '';\n pointer-events: none;\n position: absolute;\n width: 100%;\n height: 100%;\n left: 0;\n top: 0;\n\n background-image: linear-gradient(\n to bottom left,\n var(--color-2),\n var(--color-1),\n var(--color-2)\n );\n animation: _mask-running_h452n_1 2s linear infinite;\n animation-delay: 1s;\n }\n}\n\n/* 面板可视区域:靠 overflow:hidden + border-radius 统一裁角,\n * header 单独存在(运行中输入区隐藏)或 header+输入区一起存在时,四角都自动是圆的,\n * 不用再像以前那样给 header 写一条\"输入区隐藏时补圆角\"的特判规则。 */\n._panelInner_h452n_57 {\n position: relative;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n border-radius: var(--border-radius);\n border: 2px solid rgba(255, 255, 255, 0.4);\n box-shadow:\n 0 0 5px 1px rgba(255, 255, 255, 0.3),\n 0 4px 20px rgba(0, 0, 0, 0.5);\n}\n\n/* 控制栏 */\n._header_h452n_124 {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n user-select: none;\n\n height: var(--height);\n flex-shrink: 0; /* 防止 header 被压缩 */\n\n cursor: pointer;\n\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(10px);\n background-clip: padding-box;\n\n ._statusSection_h452n_140 {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-height: 24px; /* 确保垂直居中 */\n\n ._indicator_h452n_147 {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.5);\n flex-shrink: 0;\n animation: none; /* 默认无动画 */\n\n /* 运行状态 - 有动画 */\n &._thinking_h452n_156 {\n background: rgb(57, 182, 255);\n animation: _pulse_h452n_1 0.8s ease-in-out infinite;\n }\n\n &._tool_executing_h452n_161 {\n background: rgb(189, 69, 251);\n animation: _pulse_h452n_1 0.6s ease-in-out infinite;\n }\n\n &._retry_h452n_166 {\n background: rgb(255, 214, 0);\n animation: _retryPulse_h452n_1 1s ease-in-out infinite;\n }\n\n /* 静止状态 - 无动画 */\n &._completed_h452n_172,\n &._input_h452n_173,\n &._output_h452n_174 {\n background: rgb(34, 197, 94);\n animation: none;\n }\n\n &._error_h452n_179 {\n background: rgb(239, 68, 68);\n animation: none;\n }\n }\n\n ._statusText_h452n_185 {\n color: white;\n font-size: 12px;\n line-height: 1;\n font-weight: 500;\n transition: all 0.3s ease-in-out;\n position: relative;\n overflow: hidden;\n display: flex;\n align-items: center;\n min-height: 24px; /* 确保垂直居中 */\n\n &._fadeOut_h452n_197 {\n animation: _statusTextFadeOut_h452n_1 0.3s ease forwards;\n }\n\n &._fadeIn_h452n_201 {\n animation: _statusTextFadeIn_h452n_1 0.3s ease forwards;\n }\n }\n }\n\n ._controls_h452n_207 {\n display: flex;\n align-items: center;\n gap: 4px;\n\n ._controlButton_h452n_212 {\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 4px;\n background: rgba(255, 255, 255, 0.1);\n color: white;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n line-height: 1;\n\n &:hover {\n background: rgba(255, 255, 255, 0.2);\n }\n }\n\n ._stopButton_h452n_231 {\n background: rgba(239, 68, 68, 0.2);\n color: rgb(255, 41, 41);\n font-weight: 600;\n\n &:hover {\n background: rgba(239, 68, 68, 0.3);\n }\n }\n }\n}\n\n@keyframes _statusTextFadeIn_h452n_1 {\n 0% {\n opacity: 0;\n transform: translateY(5px);\n }\n 100% {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes _statusTextFadeOut_h452n_1 {\n 0% {\n opacity: 1;\n transform: translateY(0);\n }\n 100% {\n opacity: 0;\n transform: translateY(-5px);\n }\n}\n\n/* 历史记录弹窗:普通流里排在面板前面(视觉上在上方),默认收起为 0 高度,\n * 展开时靠 JS 量出的真实内容高度做 max-height 动画(不用固定封顶值),\n * 撑开多高、要不要出现,跟面板本身完全无关,不用再算\"面板一共多高、弹窗该挂在哪个 bottom 偏移\"。 */\n._historySectionWrapper_h452n_268 {\n width: 100%;\n overflow: hidden;\n max-height: 0;\n margin-bottom: 0;\n\n background: rgba(2, 0, 20, 0.5);\n backdrop-filter: blur(10px);\n\n text-shadow: 0 0 1px rgba(0, 0, 0, 0.2);\n\n border-radius: calc(var(--border-radius) + 4px);\n border: 0 solid rgba(255, 255, 255, 0);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0);\n\n transition:\n max-height 0.2s,\n margin-bottom 0.2s,\n border-width 0.2s,\n border-color 0.2s,\n box-shadow 0.2s;\n\n ._wrapper_h452n_1._expanded_h452n_290 & {\n margin-bottom: 8px;\n border-width: 2px;\n border-color: rgba(255, 255, 255, 0.4);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.6);\n }\n\n ._historySection_h452n_268 {\n max-height: min(\n 500px,\n calc(100vh - 200px - var(--height))\n ); /* 列表本身的滚动上限,跟展开/收起动画解耦 */\n overflow-y: auto;\n overscroll-behavior: contain;\n scrollbar-width: none;\n /* 底部留白用 padding 撑,不依赖最后一项 margin-bottom:\n * overflow:auto 容器会裁掉末尾子元素的 margin,导致最后一条记录贴底 */\n padding: 8px;\n\n ._historyItem_h452n_309 {\n /* backdrop-filter: blur(10px); */\n padding: 8px 10px;\n margin-bottom: 8px;\n background: linear-gradient(135deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.03));\n border-radius: 8px;\n border-left: 2px solid rgba(57, 182, 255, 0.5);\n font-size: 12px;\n color: white;\n /* color: black; */\n line-height: 1.3;\n position: relative;\n overflow: hidden;\n\n /* 微妙的内阴影 */\n box-shadow:\n 0 1px 0 rgba(255, 255, 255, 0.1),\n 0 1px 3px rgba(0, 0, 0, 0.1);\n\n &::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 1px;\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);\n }\n\n &:hover {\n background: linear-gradient(135deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.06));\n /* transform: translateY(-1px); */\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.15),\n 0 2px 4px rgba(0, 0, 0, 0.15);\n }\n\n &._completed_h452n_172,\n &._input_h452n_173,\n &._output_h452n_174 {\n border-left-color: rgb(34, 197, 94);\n background: linear-gradient(135deg, rgba(34, 197, 94, 0.1), rgba(34, 197, 94, 0.05));\n }\n\n &._error_h452n_179 {\n border-left-color: rgb(239, 68, 68);\n background: linear-gradient(135deg, rgba(239, 68, 68, 0.1), rgba(239, 68, 68, 0.05));\n }\n\n &._retry_h452n_166 {\n border-left-color: rgb(255, 214, 0);\n background: linear-gradient(135deg, rgba(255, 214, 0, 0.1), rgba(255, 214, 0, 0.05));\n }\n\n &._observation_h452n_363 {\n border-left-color: rgb(147, 51, 234);\n background: linear-gradient(135deg, rgba(147, 51, 234, 0.1), rgba(147, 51, 234, 0.05));\n }\n\n &._question_h452n_368 {\n border-left-color: rgb(255, 159, 67);\n background: linear-gradient(135deg, rgba(255, 159, 67, 0.15), rgba(255, 159, 67, 0.08));\n }\n\n /* 突出显示 done 成功结果 */\n &._doneSuccess_h452n_374 {\n background: linear-gradient(\n 135deg,\n rgba(34, 197, 94, 0.25),\n rgba(34, 197, 94, 0.15),\n rgba(34, 197, 94, 0.08)\n );\n border: none;\n border-left: 2px solid rgb(34, 197, 94);\n box-shadow:\n 0 4px 12px rgba(34, 197, 94, 0.3),\n inset 0 1px 0 rgba(255, 255, 255, 0.2),\n 0 0 20px rgba(34, 197, 94, 0.1);\n font-weight: 600;\n color: rgb(220, 252, 231);\n padding: 10px 12px;\n margin-bottom: 8px;\n border-radius: 8px;\n position: relative;\n overflow: hidden;\n\n &::before {\n background: linear-gradient(90deg, transparent, rgba(34, 197, 94, 0.4), transparent);\n }\n\n &::after {\n content: '';\n position: absolute;\n top: 0;\n left: -100%;\n width: 100%;\n height: 100%;\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);\n animation: _shimmer_h452n_1 2s ease-in-out infinite;\n }\n\n ._historyContent_h452n_410 {\n ._statusIcon_h452n_411 {\n font-size: 16px;\n animation: _celebrate_h452n_1 0.8s ease-in-out;\n filter: drop-shadow(0 2px 4px rgba(34, 197, 94, 0.5));\n }\n }\n }\n\n /* 突出显示 done 失败结果 */\n &._doneError_h452n_420 {\n background: linear-gradient(\n 135deg,\n rgba(239, 68, 68, 0.25),\n rgba(239, 68, 68, 0.15),\n rgba(239, 68, 68, 0.08)\n );\n border: none;\n border-left: 2px solid rgb(239, 68, 68);\n box-shadow:\n 0 4px 12px rgba(239, 68, 68, 0.3),\n inset 0 1px 0 rgba(255, 255, 255, 0.2),\n 0 0 20px rgba(239, 68, 68, 0.1);\n font-weight: 600;\n color: rgb(254, 226, 226);\n padding: 10px 12px;\n margin-bottom: 8px;\n border-radius: 8px;\n position: relative;\n overflow: hidden;\n\n &::before {\n background: linear-gradient(90deg, transparent, rgba(239, 68, 68, 0.4), transparent);\n }\n\n ._historyContent_h452n_410 {\n ._statusIcon_h452n_411 {\n font-size: 16px;\n filter: drop-shadow(0 2px 4px rgba(239, 68, 68, 0.5));\n }\n }\n }\n\n ._historyContent_h452n_410 {\n display: flex;\n align-items: center;\n gap: 8px;\n\n word-break: break-all;\n white-space: pre-wrap;\n\n /* overflow-x: auto; */\n\n ._statusIcon_h452n_411 {\n font-size: 12px;\n flex-shrink: 0;\n line-height: 1;\n transition: all 0.3s ease;\n }\n\n ._reflectionLines_h452n_470 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n }\n\n ._historyMeta_h452n_477 {\n font-size: 10px;\n color: rgba(255, 255, 255, 0.6);\n /* color: rgb(61, 61, 61); */\n margin-top: 8px;\n line-height: 1;\n }\n }\n }\n}\n\n/* 动画关键帧 - 更快的闪烁 */\n@keyframes _pulse_h452n_1 {\n 0%,\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n 50% {\n opacity: 0.4;\n transform: scale(1.3);\n }\n}\n\n/* 重试动画 - 旋转脉冲 */\n@keyframes _retryPulse_h452n_1 {\n 0%,\n 100% {\n opacity: 1;\n transform: scale(1) rotate(0deg);\n }\n 25% {\n opacity: 0.6;\n transform: scale(1.2) rotate(90deg);\n }\n 50% {\n opacity: 0.8;\n transform: scale(1.1) rotate(180deg);\n }\n 75% {\n opacity: 0.6;\n transform: scale(1.2) rotate(270deg);\n }\n}\n\n/* 庆祝动画 */\n@keyframes _celebrate_h452n_1 {\n 0%,\n 100% {\n transform: scale(1);\n }\n 25% {\n transform: scale(1.2) rotate(-5deg);\n }\n 75% {\n transform: scale(1.2) rotate(5deg);\n }\n}\n\n/* done 卡片的光泽效果 */\n@keyframes _shimmer_h452n_1 {\n 0% {\n left: -100%;\n }\n 100% {\n left: 100%;\n }\n}\n\n/* 输入区域样式 */\n._inputSectionWrapper_h452n_547 {\n width: 100%;\n margin: 0 auto;\n\n overflow: hidden;\n height: var(--input-height);\n\n transition: height 0.2s;\n\n /* 与 header 保持同色调,让面板看起来是一体的深色玻璃卡片 */\n background: rgba(0, 0, 0, 0.4);\n backdrop-filter: blur(10px);\n\n border-bottom-left-radius: calc(var(--border-radius) + 4px);\n border-bottom-right-radius: calc(var(--border-radius) + 4px);\n\n &._hidden_h452n_563 {\n height: 0;\n }\n\n ._inputSection_h452n_547 {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 100%;\n padding: 8px; /* 与上方状态条 padding 保持一致 */\n\n ._taskInput_h452n_574 {\n flex: 1;\n height: 100%;\n background: rgba(255, 255, 255, 0.08);\n border: 1px solid rgba(255, 255, 255, 0.15);\n border-radius: 12px;\n padding: 8px 14px;\n color: rgb(240, 240, 245);\n font-family: inherit;\n font-size: 14px;\n line-height: 1.4;\n outline: none;\n resize: none;\n overflow-y: auto;\n transition: all 0.2s ease;\n\n &::placeholder {\n color: rgba(255, 255, 255, 0.45);\n }\n\n &:focus {\n background: rgba(255, 255, 255, 0.14);\n border-color: rgba(57, 182, 255, 0.6);\n box-shadow: 0 0 0 2px rgba(57, 182, 255, 0.2);\n }\n }\n }\n}\n/*$vite$:1*/")); document.head.appendChild(elementStyle); } } catch (e) { console.error("vite-plugin-css-injected-by-js", e); }})();
1
+ (function() { try { if (typeof document != "undefined") { var elementStyle = document.createElement("style"); elementStyle.appendChild(document.createTextNode("._wrapper_ur79v_1 {\n position: fixed;\n inset: 0;\n z-index: 2147483641; /* 确保在所有元素之上,除了 Agent UI */\n cursor: wait;\n overflow: hidden;\n\n display: none;\n}\n\n._wrapper_ur79v_1._visible_ur79v_11 {\n display: block;\n}\n/* AI 光标样式 */\n._cursor_eh09s_2 {\n position: absolute;\n width: var(--cursor-size, 48px);\n height: var(--cursor-size, 48px);\n pointer-events: none;\n z-index: 10000;\n}\n\n._cursorBorder_eh09s_10 {\n position: absolute;\n width: 100%;\n height: 100%;\n background: linear-gradient(45deg, rgb(57, 182, 255), rgb(189, 69, 251));\n mask-image: url(\"data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201024%201024'%20fill='none'%3e%3cpath%20d='M287.232%20128l514.752%20387.008L574.912%20576l149.376%20299.52-103.04%2051.392L472.32%20628.544l-185.856%20143.424z'%20stroke='%23000000'%20stroke-width='56'%20stroke-linejoin='miter'/%3e%3c/svg%3e\");\n mask-size: 100% 100%;\n mask-repeat: no-repeat;\n margin-left: -16px;\n margin-top: -7px;\n}\n\n._cursorFilling_eh09s_22 {\n position: absolute;\n width: 100%;\n height: 100%;\n background: url(\"data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201024%201024'%3e%3cpath%20d='M287.232%20128l514.752%20387.008L574.912%20576l149.376%20299.52-103.04%2051.392L472.32%20628.544l-185.856%20143.424z'%20fill='%23ffffff'/%3e%3c/svg%3e\");\n background-size: 100% 100%;\n background-repeat: no-repeat;\n margin-left: -16px;\n margin-top: -7px;\n}\n\n._cursorRipple_eh09s_33 {\n position: absolute;\n width: 100%;\n height: 100%;\n pointer-events: none;\n margin-left: -50%;\n margin-top: -50%;\n\n &::after {\n content: '';\n opacity: 0;\n position: absolute;\n inset: 0;\n border: 4px solid rgba(57, 182, 255, 1);\n border-radius: 50%;\n }\n}\n\n._cursor_eh09s_2._clicking_eh09s_51 ._cursorRipple_eh09s_33::after {\n animation: _cursor-ripple_eh09s_1 300ms ease-out forwards;\n}\n\n@keyframes _cursor-ripple_eh09s_1 {\n 0% {\n transform: scale(0);\n opacity: 1;\n }\n 100% {\n transform: scale(2);\n opacity: 0;\n }\n}\n._wrapper_18lru_1 {\n position: fixed;\n bottom: 100px;\n left: 50%;\n transform: translateX(-50%) translateY(20px);\n opacity: 0;\n z-index: 2147483642; /* 比 SimulatorMask 高一层 */\n box-sizing: border-box;\n\n /* 普通流布局:历史弹窗、面板依次纵向排列,靠子元素自身尺寸撑开,\n * 不再用 position:absolute + 手算偏移量拼位置(这套算法之前连续出过两次 bug:\n * 弹窗被状态条盖住、状态条运行态时下方角没跟着圆)。 */\n display: flex;\n flex-direction: column;\n align-items: center;\n\n * {\n box-sizing: border-box;\n }\n\n /* 宽度取窗口 40%,用 clamp 防止超窄/超宽窗口下过小或过大 */\n --width: clamp(280px, 40vw, 640px);\n --height: 40px; /* 状态条高度 */\n --input-height: 80px; /* 输入区高度 */\n --border-radius: 16px;\n\n --side-space: 12px; /* 控制栏两侧的间距 */\n --history-width: calc(var(--width) - var(--side-space) * 2);\n\n --color-1: rgb(57, 182, 255);\n --color-2: rgb(189, 69, 251);\n --color-3: rgb(255, 87, 51);\n --color-4: rgb(255, 214, 0);\n\n width: var(--width);\n\n transition:\n opacity 0.3s ease-in-out,\n transform 0.3s ease-in-out;\n\n /* 响应式设计 */\n @media (max-width: 480px) {\n width: calc(100vw - 40px);\n --width: calc(100vw - 40px);\n }\n}\n\n@keyframes _mask-running_18lru_1 {\n from {\n transform: translateX(-100%);\n }\n to {\n transform: translateX(100%);\n }\n}\n\n/* 状态条 + 输入区的外层容器;.background 光效挂在这一层,.shellInner 负责裁出圆角 */\n._shell_18lru_57 {\n position: relative;\n width: 100%;\n}\n\n._background_18lru_57 {\n position: absolute;\n inset: -2px;\n border-radius: calc(var(--border-radius) + 4px);\n filter: blur(4px);\n overflow: hidden;\n pointer-events: none;\n\n &::before {\n content: '';\n pointer-events: none;\n position: absolute;\n width: 100%;\n height: 100%;\n left: 0;\n top: 0;\n\n background-image: linear-gradient(\n to bottom left,\n var(--color-1),\n var(--color-2),\n var(--color-1)\n );\n animation: _mask-running_18lru_1 2s linear infinite;\n }\n &::after {\n content: '';\n pointer-events: none;\n position: absolute;\n width: 100%;\n height: 100%;\n left: 0;\n top: 0;\n\n background-image: linear-gradient(\n to bottom left,\n var(--color-2),\n var(--color-1),\n var(--color-2)\n );\n animation: _mask-running_18lru_1 2s linear infinite;\n animation-delay: 1s;\n }\n}\n\n/* 可视区域:靠 overflow:hidden + border-radius 统一裁角,\n * header 单独存在(运行中输入区隐藏)或 header+输入区一起存在时,四角都自动是圆的,\n * 不用再像以前那样给 header 写一条\"输入区隐藏时补圆角\"的特判规则。 */\n._shellInner_18lru_57 {\n position: relative;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n border-radius: var(--border-radius);\n border: 2px solid rgba(255, 255, 255, 0.4);\n box-shadow:\n 0 0 5px 1px rgba(255, 255, 255, 0.3),\n 0 4px 20px rgba(0, 0, 0, 0.5);\n}\n\n/* 控制栏 */\n._header_18lru_124 {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n user-select: none;\n\n height: var(--height);\n flex-shrink: 0; /* 防止 header 被压缩 */\n\n cursor: pointer;\n\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(10px);\n background-clip: padding-box;\n\n ._statusSection_18lru_140 {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-height: 24px; /* 确保垂直居中 */\n\n ._indicator_18lru_147 {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.5);\n flex-shrink: 0;\n animation: none; /* 默认无动画 */\n\n /* 运行状态 - 有动画 */\n &._thinking_18lru_156 {\n background: rgb(57, 182, 255);\n animation: _pulse_18lru_1 0.8s ease-in-out infinite;\n }\n\n &._tool_executing_18lru_161 {\n background: rgb(189, 69, 251);\n animation: _pulse_18lru_1 0.6s ease-in-out infinite;\n }\n\n &._retry_18lru_166 {\n background: rgb(255, 214, 0);\n animation: _retryPulse_18lru_1 1s ease-in-out infinite;\n }\n\n /* 静止状态 - 无动画 */\n &._completed_18lru_172,\n &._input_18lru_173,\n &._output_18lru_174 {\n background: rgb(34, 197, 94);\n animation: none;\n }\n\n &._error_18lru_179 {\n background: rgb(239, 68, 68);\n animation: none;\n }\n }\n\n ._statusText_18lru_185 {\n color: white;\n font-size: 12px;\n line-height: 1;\n font-weight: 500;\n transition: all 0.3s ease-in-out;\n position: relative;\n overflow: hidden;\n display: flex;\n align-items: center;\n min-height: 24px; /* 确保垂直居中 */\n\n &._fadeOut_18lru_197 {\n animation: _statusTextFadeOut_18lru_1 0.3s ease forwards;\n }\n\n &._fadeIn_18lru_201 {\n animation: _statusTextFadeIn_18lru_1 0.3s ease forwards;\n }\n }\n }\n\n ._controls_18lru_207 {\n display: flex;\n align-items: center;\n gap: 4px;\n\n ._controlButton_18lru_212 {\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 4px;\n background: rgba(255, 255, 255, 0.1);\n color: white;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n line-height: 1;\n\n &:hover {\n background: rgba(255, 255, 255, 0.2);\n }\n }\n\n ._stopButton_18lru_231 {\n background: rgba(239, 68, 68, 0.2);\n color: rgb(255, 41, 41);\n font-weight: 600;\n\n &:hover {\n background: rgba(239, 68, 68, 0.3);\n }\n }\n }\n}\n\n@keyframes _statusTextFadeIn_18lru_1 {\n 0% {\n opacity: 0;\n transform: translateY(5px);\n }\n 100% {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@keyframes _statusTextFadeOut_18lru_1 {\n 0% {\n opacity: 1;\n transform: translateY(0);\n }\n 100% {\n opacity: 0;\n transform: translateY(-5px);\n }\n}\n\n/* 历史记录弹窗:普通流里排在面板前面(视觉上在上方),默认收起为 0 高度,\n * 展开时靠 JS 量出的真实内容高度做 max-height 动画(不用固定封顶值),\n * 撑开多高、要不要出现,跟面板本身完全无关,不用再算\"面板一共多高、弹窗该挂在哪个 bottom 偏移\"。 */\n._historySectionWrapper_18lru_268 {\n width: 100%;\n overflow: hidden;\n max-height: 0;\n margin-bottom: 0;\n\n background: rgba(2, 0, 20, 0.5);\n backdrop-filter: blur(10px);\n\n text-shadow: 0 0 1px rgba(0, 0, 0, 0.2);\n\n border-radius: calc(var(--border-radius) + 4px);\n border: 0 solid rgba(255, 255, 255, 0);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0);\n\n transition:\n max-height 0.2s,\n margin-bottom 0.2s,\n border-width 0.2s,\n border-color 0.2s,\n box-shadow 0.2s;\n\n ._wrapper_18lru_1._expanded_18lru_290 & {\n margin-bottom: 8px;\n border-width: 2px;\n border-color: rgba(255, 255, 255, 0.4);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.6);\n }\n\n ._historySection_18lru_268 {\n max-height: min(\n 500px,\n calc(100vh - 200px - var(--height))\n ); /* 列表本身的滚动上限,跟展开/收起动画解耦 */\n overflow-y: auto;\n overscroll-behavior: contain;\n scrollbar-width: none;\n /* 底部留白用 padding 撑,不依赖最后一项 margin-bottom:\n * overflow:auto 容器会裁掉末尾子元素的 margin,导致最后一条记录贴底 */\n padding: 8px;\n\n ._historyItem_18lru_309 {\n /* backdrop-filter: blur(10px); */\n padding: 8px 10px;\n margin-bottom: 8px;\n background: linear-gradient(135deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.03));\n border-radius: 8px;\n border-left: 2px solid rgba(57, 182, 255, 0.5);\n font-size: 12px;\n color: white;\n /* color: black; */\n line-height: 1.3;\n position: relative;\n overflow: hidden;\n\n /* 微妙的内阴影 */\n box-shadow:\n 0 1px 0 rgba(255, 255, 255, 0.1),\n 0 1px 3px rgba(0, 0, 0, 0.1);\n\n &::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 1px;\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);\n }\n\n &:hover {\n background: linear-gradient(135deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.06));\n /* transform: translateY(-1px); */\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, 0.15),\n 0 2px 4px rgba(0, 0, 0, 0.15);\n }\n\n &._completed_18lru_172,\n &._input_18lru_173,\n &._output_18lru_174 {\n border-left-color: rgb(34, 197, 94);\n background: linear-gradient(135deg, rgba(34, 197, 94, 0.1), rgba(34, 197, 94, 0.05));\n }\n\n &._error_18lru_179 {\n border-left-color: rgb(239, 68, 68);\n background: linear-gradient(135deg, rgba(239, 68, 68, 0.1), rgba(239, 68, 68, 0.05));\n }\n\n &._retry_18lru_166 {\n border-left-color: rgb(255, 214, 0);\n background: linear-gradient(135deg, rgba(255, 214, 0, 0.1), rgba(255, 214, 0, 0.05));\n }\n\n &._observation_18lru_363 {\n border-left-color: rgb(147, 51, 234);\n background: linear-gradient(135deg, rgba(147, 51, 234, 0.1), rgba(147, 51, 234, 0.05));\n }\n\n &._question_18lru_368 {\n border-left-color: rgb(255, 159, 67);\n background: linear-gradient(135deg, rgba(255, 159, 67, 0.15), rgba(255, 159, 67, 0.08));\n }\n\n /* 突出显示 done 成功结果 */\n &._doneSuccess_18lru_374 {\n background: linear-gradient(\n 135deg,\n rgba(34, 197, 94, 0.25),\n rgba(34, 197, 94, 0.15),\n rgba(34, 197, 94, 0.08)\n );\n border: none;\n border-left: 2px solid rgb(34, 197, 94);\n box-shadow:\n 0 4px 12px rgba(34, 197, 94, 0.3),\n inset 0 1px 0 rgba(255, 255, 255, 0.2),\n 0 0 20px rgba(34, 197, 94, 0.1);\n font-weight: 600;\n color: rgb(220, 252, 231);\n padding: 10px 12px;\n margin-bottom: 8px;\n border-radius: 8px;\n position: relative;\n overflow: hidden;\n\n &::before {\n background: linear-gradient(90deg, transparent, rgba(34, 197, 94, 0.4), transparent);\n }\n\n &::after {\n content: '';\n position: absolute;\n top: 0;\n left: -100%;\n width: 100%;\n height: 100%;\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);\n animation: _shimmer_18lru_1 2s ease-in-out infinite;\n }\n\n ._historyContent_18lru_410 {\n ._statusIcon_18lru_411 {\n font-size: 16px;\n animation: _celebrate_18lru_1 0.8s ease-in-out;\n filter: drop-shadow(0 2px 4px rgba(34, 197, 94, 0.5));\n }\n }\n }\n\n /* 突出显示 done 失败结果 */\n &._doneError_18lru_420 {\n background: linear-gradient(\n 135deg,\n rgba(239, 68, 68, 0.25),\n rgba(239, 68, 68, 0.15),\n rgba(239, 68, 68, 0.08)\n );\n border: none;\n border-left: 2px solid rgb(239, 68, 68);\n box-shadow:\n 0 4px 12px rgba(239, 68, 68, 0.3),\n inset 0 1px 0 rgba(255, 255, 255, 0.2),\n 0 0 20px rgba(239, 68, 68, 0.1);\n font-weight: 600;\n color: rgb(254, 226, 226);\n padding: 10px 12px;\n margin-bottom: 8px;\n border-radius: 8px;\n position: relative;\n overflow: hidden;\n\n &::before {\n background: linear-gradient(90deg, transparent, rgba(239, 68, 68, 0.4), transparent);\n }\n\n ._historyContent_18lru_410 {\n ._statusIcon_18lru_411 {\n font-size: 16px;\n filter: drop-shadow(0 2px 4px rgba(239, 68, 68, 0.5));\n }\n }\n }\n\n ._historyContent_18lru_410 {\n display: flex;\n align-items: center;\n gap: 8px;\n\n word-break: break-all;\n white-space: pre-wrap;\n\n /* overflow-x: auto; */\n\n ._statusIcon_18lru_411 {\n font-size: 12px;\n flex-shrink: 0;\n line-height: 1;\n transition: all 0.3s ease;\n }\n\n ._reflectionLines_18lru_470 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n }\n\n ._historyMeta_18lru_477 {\n font-size: 10px;\n color: rgba(255, 255, 255, 0.6);\n /* color: rgb(61, 61, 61); */\n margin-top: 8px;\n line-height: 1;\n }\n }\n }\n}\n\n/* 动画关键帧 - 更快的闪烁 */\n@keyframes _pulse_18lru_1 {\n 0%,\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n 50% {\n opacity: 0.4;\n transform: scale(1.3);\n }\n}\n\n/* 重试动画 - 旋转脉冲 */\n@keyframes _retryPulse_18lru_1 {\n 0%,\n 100% {\n opacity: 1;\n transform: scale(1) rotate(0deg);\n }\n 25% {\n opacity: 0.6;\n transform: scale(1.2) rotate(90deg);\n }\n 50% {\n opacity: 0.8;\n transform: scale(1.1) rotate(180deg);\n }\n 75% {\n opacity: 0.6;\n transform: scale(1.2) rotate(270deg);\n }\n}\n\n/* 庆祝动画 */\n@keyframes _celebrate_18lru_1 {\n 0%,\n 100% {\n transform: scale(1);\n }\n 25% {\n transform: scale(1.2) rotate(-5deg);\n }\n 75% {\n transform: scale(1.2) rotate(5deg);\n }\n}\n\n/* done 卡片的光泽效果 */\n@keyframes _shimmer_18lru_1 {\n 0% {\n left: -100%;\n }\n 100% {\n left: 100%;\n }\n}\n\n/* 输入区域样式 */\n._inputSectionWrapper_18lru_547 {\n width: 100%;\n margin: 0 auto;\n\n overflow: hidden;\n height: var(--input-height);\n\n transition: height 0.2s;\n\n /* 与 header 保持同色调,让面板看起来是一体的深色玻璃卡片 */\n background: rgba(0, 0, 0, 0.4);\n backdrop-filter: blur(10px);\n\n border-bottom-left-radius: calc(var(--border-radius) + 4px);\n border-bottom-right-radius: calc(var(--border-radius) + 4px);\n\n &._hidden_18lru_563 {\n height: 0;\n }\n\n ._inputSection_18lru_547 {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 100%;\n padding: 8px; /* 与上方状态条 padding 保持一致 */\n\n ._taskInput_18lru_574 {\n flex: 1;\n height: 100%;\n background: rgba(255, 255, 255, 0.08);\n border: 1px solid rgba(255, 255, 255, 0.15);\n border-radius: 12px;\n padding: 8px 14px;\n color: rgb(240, 240, 245);\n font-family: inherit;\n font-size: 14px;\n line-height: 1.4;\n outline: none;\n resize: none;\n overflow-y: auto;\n transition: all 0.2s ease;\n\n &::placeholder {\n color: rgba(255, 255, 255, 0.45);\n }\n\n &:focus {\n background: rgba(255, 255, 255, 0.14);\n border-color: rgba(57, 182, 255, 0.6);\n box-shadow: 0 0 0 2px rgba(57, 182, 255, 0.2);\n }\n }\n }\n}\n/*$vite$:1*/")); document.head.appendChild(elementStyle); } } catch (e) { console.error("vite-plugin-css-injected-by-js", e); }})();
2
2
  import { Motion } from "ai-motion";
3
3
  import * as z from "zod/v4";
4
4
  //#region \0rolldown/runtime.js
@@ -22,22 +22,27 @@ var __exportAll = (all, no_symbols) => {
22
22
  };
23
23
  //#endregion
24
24
  //#region src/browser/utils/index.ts
25
+ /** 判断值是否为 HTMLElement(type guard)。 */
25
26
  function isHTMLElement(el) {
26
27
  return !!el && el.nodeType === 1;
27
28
  }
29
+ /** 判断元素是否为 <input>(type guard)。 */
28
30
  function isInputElement(el) {
29
31
  return el?.nodeType === 1 && el.tagName === "INPUT";
30
32
  }
33
+ /** 判断元素是否为 <textarea>(type guard)。 */
31
34
  function isTextAreaElement(el) {
32
35
  return el?.nodeType === 1 && el.tagName === "TEXTAREA";
33
36
  }
37
+ /** 判断元素是否为 <select>(type guard)。 */
34
38
  function isSelectElement(el) {
35
39
  return el?.nodeType === 1 && el.tagName === "SELECT";
36
40
  }
41
+ /** 判断元素是否为 <a>(type guard)。 */
37
42
  function isAnchorElement(el) {
38
43
  return el?.nodeType === 1 && el.tagName === "A";
39
44
  }
40
- /** Iframe offset for translating element coordinates to top-frame viewport. */
45
+ /** 用于将元素坐标换算为顶层视口坐标的 iframe 偏移量。 */
41
46
  function getIframeOffset(element) {
42
47
  const frame = element.ownerDocument.defaultView?.frameElement;
43
48
  if (!frame) return {
@@ -51,19 +56,20 @@ function getIframeOffset(element) {
51
56
  };
52
57
  }
53
58
  /**
54
- * Get native value setter from the element's own prototype (iframe-safe).
55
- * @note for React
59
+ * 从元素自身的原型上获取原生 value setter(iframe 安全)。
60
+ * @note 用于 React
56
61
  */
57
62
  function getNativeValueSetter(element) {
58
63
  return Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), "value").set;
59
64
  }
65
+ /** 等待指定的秒数。 */
60
66
  async function waitFor$1(seconds) {
61
67
  await new Promise((resolve) => setTimeout(resolve, seconds * 1e3));
62
68
  }
63
69
  /**
64
- * Move the visual pointer to a position within an element.
65
- * @param x - x coordinate in the element's document viewport
66
- * @param y - y coordinate in the element's document viewport
70
+ * 将视觉指针移动到元素内的某个位置。
71
+ * @param x - 元素所属文档视口内的 x 坐标
72
+ * @param y - 元素所属文档视口内的 y 坐标
67
73
  */
68
74
  async function movePointerToElement(element, x, y) {
69
75
  const offset = getIframeOffset(element);
@@ -73,20 +79,23 @@ async function movePointerToElement(element, x, y) {
73
79
  } }));
74
80
  await waitFor$1(.3);
75
81
  }
82
+ /** 触发一次模拟光标点击动画。 */
76
83
  async function clickPointer() {
77
84
  window.dispatchEvent(new CustomEvent("PageAgent::ClickPointer"));
78
85
  }
86
+ /** 开启遮罩穿透,鼠标事件不再被遮罩拦截。 */
79
87
  async function enablePassThrough() {
80
88
  window.dispatchEvent(new CustomEvent("PageAgent::EnablePassThrough"));
81
89
  }
90
+ /** 关闭遮罩穿透,遮罩重新拦截鼠标事件。 */
82
91
  async function disablePassThrough() {
83
92
  window.dispatchEvent(new CustomEvent("PageAgent::DisablePassThrough"));
84
93
  }
85
94
  //#endregion
86
95
  //#region src/browser/actions.ts
87
96
  /**
88
- * Get the HTMLElement by index from a selectorMap.
89
- * @private Internal method, subject to change at any time.
97
+ * 根据索引从 selectorMap 中获取对应的 HTMLElement
98
+ * @private 内部方法,随时可能变更。
90
99
  */
91
100
  function getElementByIndex(selectorMap, index) {
92
101
  const interactiveNode = selectorMap.get(index);
@@ -97,6 +106,9 @@ function getElementByIndex(selectorMap, index) {
97
106
  return element;
98
107
  }
99
108
  var lastClickedElement = null;
109
+ /**
110
+ * 触发上次点击元素的失焦相关事件(pointerout/pointerleave/mouseout/mouseleave/blur)并清空记录。
111
+ */
100
112
  function blurLastClickedElement() {
101
113
  if (lastClickedElement) {
102
114
  lastClickedElement.dispatchEvent(new PointerEvent("pointerout", { bubbles: true }));
@@ -108,11 +120,11 @@ function blurLastClickedElement() {
108
120
  }
109
121
  }
110
122
  /**
111
- * Simulate a full click following W3C Pointer Events + UI Events spec order:
123
+ * 模拟一次完整的点击,遵循 W3C Pointer Events + UI Events 规范顺序:
112
124
  * pointerover/enter → mouseover/enter → pointerdown → mousedown → [focus] →
113
125
  * pointerup → mouseup → click
114
126
  *
115
- * @private Internal method, subject to change at any time.
127
+ * @private 内部方法,随时可能变更。
116
128
  */
117
129
  async function clickElement(element) {
118
130
  blurLastClickedElement();
@@ -164,7 +176,8 @@ async function clickElement(element) {
164
176
  await waitFor$1(.1);
165
177
  }
166
178
  /**
167
- * @private Internal method, subject to change at any time.
179
+ * 向输入类元素(input / textarea / contenteditable)输入文本,并触发相应的输入事件。
180
+ * @private 内部方法,随时可能变更。
168
181
  */
169
182
  async function inputTextElement(element, text) {
170
183
  const isContentEditable = element.isContentEditable;
@@ -214,8 +227,9 @@ async function inputTextElement(element, text) {
214
227
  blurLastClickedElement();
215
228
  }
216
229
  /**
217
- * @todo browser-use version is very complex and supports menu tags, need to follow up
218
- * @private Internal method, subject to change at any time.
230
+ * 在下拉选择框(select)中按文本匹配并选中对应选项,然后触发 change 事件。
231
+ * @todo browser-use 版本非常复杂且支持菜单标签,需要跟进
232
+ * @private 内部方法,随时可能变更。
219
233
  */
220
234
  async function selectOptionElement(selectElement, optionText) {
221
235
  if (!isSelectElement(selectElement)) throw new Error("Element is not a select element");
@@ -226,7 +240,8 @@ async function selectOptionElement(selectElement, optionText) {
226
240
  await waitFor$1(.05);
227
241
  }
228
242
  /**
229
- * @private Internal method, subject to change at any time.
243
+ * 将元素滚动到可视区域内;优先使用原生 scrollIntoViewIfNeeded,不支持时回退为 scrollIntoView。
244
+ * @private 内部方法,随时可能变更。
230
245
  */
231
246
  async function scrollIntoViewIfNeeded(element) {
232
247
  const el = element;
@@ -237,6 +252,12 @@ async function scrollIntoViewIfNeeded(element) {
237
252
  inline: "nearest"
238
253
  });
239
254
  }
255
+ /**
256
+ * 垂直滚动页面或指定元素容器。
257
+ * @param scroll_amount - 滚动像素数(正数向下,负数向上)
258
+ * @param element - 可选;若传入,则优先滚动该元素及其祖先中最近的可纵向滚动容器,否则滚动页面
259
+ * @returns 滚动结果的描述消息
260
+ */
240
261
  async function scrollVertically(scroll_amount, element) {
241
262
  if (element) {
242
263
  const targetElement = element;
@@ -310,6 +331,12 @@ async function scrollVertically(scroll_amount, element) {
310
331
  return `✅ ${warningMsg} Scrolled container (${el.tagName}) by ${scrolled}px.`;
311
332
  }
312
333
  }
334
+ /**
335
+ * 水平滚动页面或指定元素容器。
336
+ * @param scroll_amount - 滚动像素数(正数向右,负数向左)
337
+ * @param element - 可选;若传入,则优先滚动该元素及其祖先中最近的可横向滚动容器,否则滚动页面
338
+ * @returns 滚动结果的描述消息
339
+ */
313
340
  async function scrollHorizontally(scroll_amount, element) {
314
341
  if (element) {
315
342
  const targetElement = element;
@@ -1426,13 +1453,13 @@ var dom_tree_default = (args = {
1426
1453
  //#endregion
1427
1454
  //#region src/browser/dom/index.ts
1428
1455
  /**
1429
- * Viewport expansion for DOM tree extraction.
1430
- * -1 means full page (no viewport restriction)
1431
- * 0 means viewport only
1432
- * positive values expand the viewport by that many pixels
1456
+ * DOM 树提取时的视口扩展。
1457
+ * -1 表示整页(不受视口限制)
1458
+ * 0 表示仅视口
1459
+ * 正数表示在视口基础上额外扩展的像素数
1433
1460
  *
1434
- * @note Since isTopElement depends on elementFromPoint,
1435
- * it returns null when out of viewport, this feature has no practical use, only differ between -1 and 0
1461
+ * @note 由于 isTopElement 依赖 elementFromPoint
1462
+ * 当元素在视口外时会返回 null,该特性实际用处不大,仅在 -1 0 之间有区别
1436
1463
  */
1437
1464
  var DEFAULT_VIEWPORT_EXPANSION = -1;
1438
1465
  function resolveViewportExpansion(viewportExpansion) {
@@ -1780,13 +1807,13 @@ function patchReact(_browserController) {
1780
1807
  for (const element of reactRootElements) element.setAttribute("data-page-agent-not-interactive", "true");
1781
1808
  }
1782
1809
  /**
1783
- * @todo (Heavy, might have false negatives) Interaction detection, if element width/height equals body offsetWidth/Height, consider it root element and non-interactive (React often attaches many events to root elements, causing false positives)
1810
+ * @todo (较重,可能有漏判)交互检测:如果元素宽高等于 body offsetWidth/Height,则视为根元素并标记为不可交互(React 经常把大量事件绑定在根元素上,导致误判为可交互)
1784
1811
  */
1785
1812
  //#endregion
1786
1813
  //#region src/browser/visual/checkDarkMode.ts
1787
1814
  /**
1788
- * A comprehensive function to determine if the page is currently in a dark theme.
1789
- * Heuristic check. Only work for common patterns. Return false by default.
1815
+ * 判断页面当前是否处于深色主题的综合函数。
1816
+ * 启发式检测,只覆盖常见模式,默认返回 false
1790
1817
  */
1791
1818
  function isPageDark() {
1792
1819
  try {
@@ -1803,7 +1830,7 @@ function isPageDark() {
1803
1830
  }
1804
1831
  }
1805
1832
  /**
1806
- * Checks for common dark mode CSS classes on the html or body elements.
1833
+ * 检查 html body 元素上常见的深色模式 CSS 类名。
1807
1834
  */
1808
1835
  function hasDarkModeClass() {
1809
1836
  const DEFAULT_DARK_MODE_CLASSES = [
@@ -1819,7 +1846,7 @@ function hasDarkModeClass() {
1819
1846
  return false;
1820
1847
  }
1821
1848
  /**
1822
- * Some UI frameworks use data attributes to indicate theme
1849
+ * 某些 UI 框架使用 data 属性来表示主题
1823
1850
  */
1824
1851
  function hasDarkModeDataAttribute() {
1825
1852
  const htmlElement = document.documentElement;
@@ -1837,8 +1864,8 @@ function hasDarkModeDataAttribute() {
1837
1864
  return false;
1838
1865
  }
1839
1866
  /**
1840
- * Checks the CSS `color-scheme` property and `<meta name="color-scheme">` tag.
1841
- * Only "dark"/"only dark" counts as dark; "light dark" is ambiguous and ignored.
1867
+ * 检查 CSS `color-scheme` 属性和 `<meta name="color-scheme">` 标签。
1868
+ * 只有 "dark"/"only dark" 才视为深色;"light dark" 存在歧义,忽略。
1842
1869
  */
1843
1870
  function isColorSchemeDark() {
1844
1871
  const metaContent = document.querySelector("meta[name=\"color-scheme\"]")?.content.toLowerCase();
@@ -1847,7 +1874,7 @@ function isColorSchemeDark() {
1847
1874
  return colorScheme === "dark" || colorScheme === "only dark";
1848
1875
  }
1849
1876
  /**
1850
- * Checks the background color of the body element to determine if the page is dark.
1877
+ * 检查 body 元素的背景色以判断页面是否为深色。
1851
1878
  */
1852
1879
  function isBackgroundDark() {
1853
1880
  const htmlStyle = window.getComputedStyle(document.documentElement);
@@ -1859,18 +1886,18 @@ function isBackgroundDark() {
1859
1886
  return false;
1860
1887
  }
1861
1888
  /**
1862
- * Checks if the text color on the body is light, which implies a dark background.
1889
+ * 检查 body 上的文字颜色是否为浅色,浅色文字通常意味着深色背景。
1863
1890
  */
1864
1891
  function isTextColorLight() {
1865
- /** Luminance (0-255) above which body text is considered light */
1892
+ /** 亮度(0-255)超过该值时认为 body 文字为浅色 */
1866
1893
  const LIGHT_TEXT_LUMINANCE = 200;
1867
1894
  const luminance = getLuminance(window.getComputedStyle(document.body || document.documentElement).color);
1868
1895
  return luminance !== null && luminance > LIGHT_TEXT_LUMINANCE;
1869
1896
  }
1870
1897
  /**
1871
- * Checks the background color of major layout elements (#app, #root, etc.).
1872
- * Many SPAs render into a container that may have its own dark background while
1873
- * <body> remains transparent.
1898
+ * 检查主要布局元素(#app、#root 等)的背景色。
1899
+ * 很多 SPA 会把内容渲染到某个容器中,该容器可能有自己的深色背景,
1900
+ * <body> 仍然是透明的。
1874
1901
  */
1875
1902
  function isMainContentBackgroundDark() {
1876
1903
  const { innerWidth: vw, innerHeight: vh } = window;
@@ -1889,8 +1916,8 @@ function isMainContentBackgroundDark() {
1889
1916
  return false;
1890
1917
  }
1891
1918
  /**
1892
- * Parses an RGB or RGBA color string and returns an object with r, g, b properties.
1893
- * @param {string} colorString - e.g., "rgb(34, 34, 34)" or "rgba(0, 0, 0, 0.5)"
1919
+ * 解析 RGB RGBA 颜色字符串,返回包含 rgb 属性的对象。
1920
+ * @param {string} colorString - 例如 "rgb(34, 34, 34)" "rgba(0, 0, 0, 0.5)"
1894
1921
  * @returns {{r: number, g: number, b: number}|null}
1895
1922
  */
1896
1923
  function parseRgbColor(colorString) {
@@ -1903,9 +1930,9 @@ function parseRgbColor(colorString) {
1903
1930
  };
1904
1931
  }
1905
1932
  /**
1906
- * Calculates the perceived luminance (0-255) of a CSS color string.
1907
- * @param {string} colorString - e.g., "rgb(50, 50, 50)" or "rgba(0, 0, 0, 0.5)"
1908
- * @returns {number|null} - The luminance, or null if the color is transparent or unparseable.
1933
+ * 计算 CSS 颜色字符串的感知亮度(0-255)。
1934
+ * @param {string} colorString - 例如 "rgb(50, 50, 50)" "rgba(0, 0, 0, 0.5)"
1935
+ * @returns {number|null} - 亮度值,若颜色为透明或无法解析则返回 null
1909
1936
  */
1910
1937
  function getLuminance(colorString) {
1911
1938
  if (!colorString || colorString === "transparent" || colorString.startsWith("rgba(0, 0, 0, 0)")) return null;
@@ -1914,9 +1941,9 @@ function getLuminance(colorString) {
1914
1941
  return .299 * rgb.r + .587 * rgb.g + .114 * rgb.b;
1915
1942
  }
1916
1943
  /**
1917
- * Determines if a color is "dark" based on its calculated luminance.
1918
- * @param {string} colorString - The CSS color string (e.g., "rgb(50, 50, 50)").
1919
- * @param {number} threshold - A value between 0 and 255. Colors with luminance below this will be considered dark. Default is 128.
1944
+ * 根据计算出的亮度判断颜色是否为“深色”。
1945
+ * @param {string} colorString - CSS 颜色字符串(例如 "rgb(50, 50, 50)")。
1946
+ * @param {number} threshold - 0 255 之间的值。亮度低于该值的颜色视为深色。默认为 128
1920
1947
  */
1921
1948
  function isColorDark(colorString, threshold = 128) {
1922
1949
  const luminance = getLuminance(colorString);
@@ -1970,6 +1997,9 @@ var init_SimulatorMask = __esmMin((() => {
1970
1997
  #currentCursorY = 0;
1971
1998
  #targetCursorX = 0;
1972
1999
  #targetCursorY = 0;
2000
+ /**
2001
+ * 构造函数:初始化遮罩 DOM、创建 ai-motion 动效、拦截底层事件,并注册全局事件监听。
2002
+ */
1973
2003
  constructor() {
1974
2004
  super();
1975
2005
  this.wrapper.id = "page-agent-runtime_simulator-mask";
@@ -2048,6 +2078,7 @@ var init_SimulatorMask = __esmMin((() => {
2048
2078
  window.removeEventListener("PageAgent::DisablePassThrough", disablePassThroughListener);
2049
2079
  });
2050
2080
  }
2081
+ /** 创建模拟光标的 DOM 结构(包含波纹、填充层、边框层)。 */
2051
2082
  #createCursor() {
2052
2083
  this.#cursor.className = cursor_module_default.cursor;
2053
2084
  const rippleContainer = document.createElement("div");
@@ -2061,6 +2092,7 @@ var init_SimulatorMask = __esmMin((() => {
2061
2092
  this.#cursor.appendChild(borderLayer);
2062
2093
  this.wrapper.appendChild(this.#cursor);
2063
2094
  }
2095
+ /** 每帧将光标向目标位置移动 20% 距离,实现平滑跟随动画。 */
2064
2096
  #moveCursorToTarget() {
2065
2097
  if (this.#disposed) return;
2066
2098
  const newX = this.#currentCursorX + (this.#targetCursorX - this.#currentCursorX) * .2;
@@ -2079,17 +2111,24 @@ var init_SimulatorMask = __esmMin((() => {
2079
2111
  }
2080
2112
  requestAnimationFrame(() => this.#moveCursorToTarget());
2081
2113
  }
2114
+ /**
2115
+ * 设置光标的目标坐标(由外部通过事件触发调用)。
2116
+ * @param x - 目标 X 坐标
2117
+ * @param y - 目标 Y 坐标
2118
+ */
2082
2119
  setCursorPosition(x, y) {
2083
2120
  if (this.#disposed) return;
2084
2121
  this.#targetCursorX = x;
2085
2122
  this.#targetCursorY = y;
2086
2123
  }
2124
+ /** 触发一次光标点击动画(通过重放 clicking class 实现)。 */
2087
2125
  triggerClickAnimation() {
2088
2126
  if (this.#disposed) return;
2089
2127
  this.#cursor.classList.remove(cursor_module_default.clicking);
2090
2128
  this.#cursor.offsetHeight;
2091
2129
  this.#cursor.classList.add(cursor_module_default.clicking);
2092
2130
  }
2131
+ /** 显示遮罩及模拟光标。 */
2093
2132
  show() {
2094
2133
  if (this.shown || this.#disposed) return;
2095
2134
  this.shown = true;
@@ -2103,6 +2142,7 @@ var init_SimulatorMask = __esmMin((() => {
2103
2142
  this.#cursor.style.left = `${this.#currentCursorX}px`;
2104
2143
  this.#cursor.style.top = `${this.#currentCursorY}px`;
2105
2144
  }
2145
+ /** 隐藏遮罩及模拟光标。 */
2106
2146
  hide() {
2107
2147
  if (!this.shown || this.#disposed) return;
2108
2148
  this.shown = false;
@@ -2113,6 +2153,7 @@ var init_SimulatorMask = __esmMin((() => {
2113
2153
  this.wrapper.classList.remove(SimulatorMask_module_default.visible);
2114
2154
  }, 800);
2115
2155
  }
2156
+ /** 销毁遮罩:释放动效、移除 DOM 并触发 dispose 事件。 */
2116
2157
  dispose() {
2117
2158
  this.#disposed = true;
2118
2159
  this.motion?.dispose();
@@ -2124,44 +2165,46 @@ var init_SimulatorMask = __esmMin((() => {
2124
2165
  //#endregion
2125
2166
  //#region src/browser/BrowserController.ts
2126
2167
  /**
2127
- * Copyright (C) 2025 Alibaba Group Holding Limited
2128
- * All rights reserved.
2168
+ * 版权所有 (C) 2025 Alibaba Group Holding Limited
2169
+ * 保留所有权利。
2129
2170
  *
2130
- * BrowserController - Manages DOM operations and element interactions.
2131
- * Designed to be independent of LLM and can be tested in unit tests.
2132
- * All public methods are async for potential remote calling support.
2171
+ * BrowserController - 管理 DOM 操作和元素交互。
2172
+ * 设计上独立于 LLM,可在单元测试中直接测试。
2173
+ * 所有公开方法均为异步,以支持潜在的远程调用。
2133
2174
  */
2134
2175
  /**
2135
- * BrowserController manages DOM state and element interactions.
2136
- * It provides async methods for all DOM operations, keeping state isolated.
2176
+ * BrowserController 管理 DOM 状态和元素交互。
2177
+ * 它为所有 DOM 操作提供异步方法,并保持状态隔离。
2137
2178
  *
2138
2179
  * @lifecycle
2139
- * - beforeUpdate: Emitted before the DOM tree is updated.
2140
- * - afterUpdate: Emitted after the DOM tree is updated.
2180
+ * - beforeUpdate: DOM 树更新之前触发。
2181
+ * - afterUpdate: DOM 树更新之后触发。
2141
2182
  */
2142
2183
  var BrowserController = class extends EventTarget {
2143
2184
  config;
2144
- /** Corresponds to eval_page in browser-use */
2185
+ /** 对应 browser-use 中的 eval_page */
2145
2186
  flatTree = null;
2146
2187
  /**
2147
- * All highlighted index-mapped interactive elements
2148
- * Corresponds to DOMState.selector_map in browser-use
2188
+ * 所有已索引的高亮交互元素
2189
+ * 对应 browser-use 中的 DOMState.selector_map
2149
2190
  */
2150
2191
  selectorMap = /* @__PURE__ */ new Map();
2151
- /** Index -> element text description mapping */
2192
+ /** 索引 -> 元素文本描述映射 */
2152
2193
  elementTextMap = /* @__PURE__ */ new Map();
2153
2194
  /**
2154
- * Simplified HTML for LLM consumption.
2155
- * Corresponds to clickable_elements_to_string in browser-use
2195
+ * LLM 消费的简化 HTML。
2196
+ * 对应 browser-use 中的 clickable_elements_to_string
2156
2197
  */
2157
2198
  simplifiedHTML = "<EMPTY>";
2158
- /** last time the tree was updated */
2199
+ /** 树最后一次更新的时间 */
2159
2200
  lastTimeUpdate = 0;
2160
- /** Whether the tree has been indexed at least once */
2201
+ /** 树是否至少被索引过一次 */
2161
2202
  isIndexed = false;
2162
- /** Visual mask overlay for blocking user interaction during automation */
2203
+ /** 用于在自动化期间阻止用户交互的视觉遮罩层 */
2163
2204
  mask = null;
2205
+ /** mask 异步初始化完成的 Promise,用于在调用前等待初始化完成 */
2164
2206
  maskReady = null;
2207
+ /** 是否已释放资源 */
2165
2208
  disposed = false;
2166
2209
  constructor(config = {}) {
2167
2210
  super();
@@ -2170,7 +2213,7 @@ var BrowserController = class extends EventTarget {
2170
2213
  if (config.enableMask) this.initMask();
2171
2214
  }
2172
2215
  /**
2173
- * Initialize mask asynchronously (dynamic import to avoid CSS loading in Node)
2216
+ * 异步初始化遮罩层(动态导入以避免在 Node 中加载 CSS
2174
2217
  */
2175
2218
  initMask() {
2176
2219
  if (this.disposed || this.maskReady !== null) return;
@@ -2186,20 +2229,20 @@ var BrowserController = class extends EventTarget {
2186
2229
  })();
2187
2230
  }
2188
2231
  /**
2189
- * Get current page URL
2232
+ * 获取当前页面 URL
2190
2233
  */
2191
2234
  async getCurrentUrl() {
2192
2235
  return window.location.href;
2193
2236
  }
2194
2237
  /**
2195
- * Get last tree update timestamp
2238
+ * 获取树最后一次更新的时间戳
2196
2239
  */
2197
2240
  async getLastUpdateTime() {
2198
2241
  return this.lastTimeUpdate;
2199
2242
  }
2200
2243
  /**
2201
- * Get structured browser state for LLM consumption.
2202
- * Automatically calls updateTree() to refresh the DOM state.
2244
+ * 获取供 LLM 消费的结构化浏览器状态。
2245
+ * 自动调用 updateTree() 刷新 DOM 状态。
2203
2246
  */
2204
2247
  async getBrowserState() {
2205
2248
  const url = window.location.href;
@@ -2217,9 +2260,9 @@ var BrowserController = class extends EventTarget {
2217
2260
  };
2218
2261
  }
2219
2262
  /**
2220
- * Update DOM tree, returns simplified HTML for LLM.
2221
- * This is the main method to refresh the page state.
2222
- * Automatically bypasses mask during DOM extraction if enabled.
2263
+ * 更新 DOM 树,返回供 LLM 使用的简化 HTML
2264
+ * 这是刷新页面状态的主要方法。
2265
+ * 若已启用遮罩,DOM 提取期间会自动绕过遮罩。
2223
2266
  */
2224
2267
  async updateTree() {
2225
2268
  this.dispatchEvent(new Event("beforeUpdate"));
@@ -2242,21 +2285,21 @@ var BrowserController = class extends EventTarget {
2242
2285
  return this.simplifiedHTML;
2243
2286
  }
2244
2287
  /**
2245
- * Clean up all element highlights
2288
+ * 清理所有元素高亮
2246
2289
  */
2247
2290
  async cleanUpHighlights() {
2248
2291
  console.log("[BrowserController] cleanUpHighlights");
2249
2292
  cleanUpHighlights();
2250
2293
  }
2251
2294
  /**
2252
- * Ensure the tree has been indexed before any index-based operation.
2253
- * Throws if updateTree() hasn't been called yet.
2295
+ * 在任何基于索引的操作之前确保树已被索引。
2296
+ * 若尚未调用 updateTree() 则抛出异常。
2254
2297
  */
2255
2298
  assertIndexed() {
2256
2299
  if (!this.isIndexed) throw new Error("DOM tree not indexed yet. Can not perform actions on elements.");
2257
2300
  }
2258
2301
  /**
2259
- * Click element by index
2302
+ * 根据索引点击元素
2260
2303
  */
2261
2304
  async clickElement(index) {
2262
2305
  try {
@@ -2280,7 +2323,7 @@ var BrowserController = class extends EventTarget {
2280
2323
  }
2281
2324
  }
2282
2325
  /**
2283
- * Input text into element by index
2326
+ * 根据索引向元素输入文本
2284
2327
  */
2285
2328
  async inputText(index, text) {
2286
2329
  try {
@@ -2300,7 +2343,7 @@ var BrowserController = class extends EventTarget {
2300
2343
  }
2301
2344
  }
2302
2345
  /**
2303
- * Select dropdown option by index and option text
2346
+ * 根据索引和选项文本在下拉框中选中选项
2304
2347
  */
2305
2348
  async selectOption(index, optionText) {
2306
2349
  try {
@@ -2320,7 +2363,7 @@ var BrowserController = class extends EventTarget {
2320
2363
  }
2321
2364
  }
2322
2365
  /**
2323
- * Scroll vertically
2366
+ * 垂直滚动
2324
2367
  */
2325
2368
  async scroll(options) {
2326
2369
  try {
@@ -2338,7 +2381,7 @@ var BrowserController = class extends EventTarget {
2338
2381
  }
2339
2382
  }
2340
2383
  /**
2341
- * Scroll horizontally
2384
+ * 水平滚动
2342
2385
  */
2343
2386
  async scrollHorizontally(options) {
2344
2387
  try {
@@ -2356,9 +2399,8 @@ var BrowserController = class extends EventTarget {
2356
2399
  }
2357
2400
  }
2358
2401
  /**
2359
- * Execute arbitrary JavaScript on the page.
2360
- * The optional `signal` is exposed to the script scope so cooperative code
2361
- * can abort promptly when the task is stopped.
2402
+ * 在页面上执行任意 JavaScript
2403
+ * 可选的 `signal` 会暴露到脚本作用域中,使协作代码能在任务被中止时及时退出。
2362
2404
  */
2363
2405
  async executeJavascript(script, signal) {
2364
2406
  try {
@@ -2374,8 +2416,8 @@ var BrowserController = class extends EventTarget {
2374
2416
  }
2375
2417
  }
2376
2418
  /**
2377
- * Show the visual mask overlay.
2378
- * Only works after mask is setup.
2419
+ * 显示视觉遮罩层。
2420
+ * 仅在遮罩初始化完成后可用。
2379
2421
  */
2380
2422
  async showMask() {
2381
2423
  if (this.disposed) return;
@@ -2384,8 +2426,8 @@ var BrowserController = class extends EventTarget {
2384
2426
  this.mask?.show();
2385
2427
  }
2386
2428
  /**
2387
- * Hide the visual mask overlay.
2388
- * Only works after mask is setup.
2429
+ * 隐藏视觉遮罩层。
2430
+ * 仅在遮罩初始化完成后可用。
2389
2431
  */
2390
2432
  async hideMask() {
2391
2433
  if (this.disposed) return;
@@ -2394,7 +2436,7 @@ var BrowserController = class extends EventTarget {
2394
2436
  this.mask?.hide();
2395
2437
  }
2396
2438
  /**
2397
- * Dispose and clean up resources
2439
+ * 释放资源并清理
2398
2440
  */
2399
2441
  dispose() {
2400
2442
  if (this.disposed) return;
@@ -2421,6 +2463,7 @@ var BrowserController = class extends EventTarget {
2421
2463
  if (errors.length > 1) throw new AggregateError(errors, "BrowserController disposal failed");
2422
2464
  }
2423
2465
  };
2466
+ /** 语言包集合:键为语言代码,值为对应翻译表 */
2424
2467
  var locales = {
2425
2468
  "en-US": { ui: {
2426
2469
  ready: "Ready",
@@ -2525,81 +2568,85 @@ var I18n = class {
2525
2568
  if (params) return this.interpolate(value, params);
2526
2569
  return value;
2527
2570
  }
2571
+ /** 根据点分路径从翻译对象中取值 */
2528
2572
  getNestedValue(obj, path) {
2529
2573
  return path.split(".").reduce((current, key) => current?.[key], obj);
2530
2574
  }
2575
+ /** 将模板中的 {{key}} 占位符替换为参数值 */
2531
2576
  interpolate(template, params) {
2532
2577
  return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
2533
2578
  return params[key] != null ? params[key].toString() : match;
2534
2579
  });
2535
2580
  }
2581
+ /** 获取当前语言代码 */
2536
2582
  getLanguage() {
2537
2583
  return this.language;
2538
2584
  }
2539
2585
  };
2540
2586
  //#endregion
2541
2587
  //#region src/ui/utils.ts
2588
+ /** 截断字符串,超过最大长度时以省略号结尾 */
2542
2589
  function truncate$1(text, maxLength) {
2543
2590
  if (text.length > maxLength) return text.substring(0, maxLength) + "...";
2544
2591
  return text;
2545
2592
  }
2546
2593
  /**
2547
- * Escape HTML special characters to prevent XSS and rendering issues
2594
+ * 转义 HTML 特殊字符,防止 XSS 及渲染问题
2548
2595
  */
2549
2596
  function escapeHtml(text) {
2550
2597
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
2551
2598
  }
2552
2599
  var UI_module_default = {
2553
- wrapper: "_wrapper_h452n_1",
2554
- panel: "_panel_h452n_57",
2555
- background: "_background_h452n_57",
2556
- "mask-running": "_mask-running_h452n_1",
2557
- panelInner: "_panelInner_h452n_57",
2558
- header: "_header_h452n_124",
2559
- pulse: "_pulse_h452n_1",
2560
- retryPulse: "_retryPulse_h452n_1",
2561
- statusTextFadeOut: "_statusTextFadeOut_h452n_1",
2562
- statusTextFadeIn: "_statusTextFadeIn_h452n_1",
2563
- statusSection: "_statusSection_h452n_140",
2564
- indicator: "_indicator_h452n_147",
2565
- thinking: "_thinking_h452n_156",
2566
- tool_executing: "_tool_executing_h452n_161",
2567
- retry: "_retry_h452n_166",
2568
- completed: "_completed_h452n_172",
2569
- input: "_input_h452n_173",
2570
- output: "_output_h452n_174",
2571
- error: "_error_h452n_179",
2572
- statusText: "_statusText_h452n_185",
2573
- fadeOut: "_fadeOut_h452n_197",
2574
- fadeIn: "_fadeIn_h452n_201",
2575
- controls: "_controls_h452n_207",
2576
- controlButton: "_controlButton_h452n_212",
2577
- stopButton: "_stopButton_h452n_231",
2578
- historySectionWrapper: "_historySectionWrapper_h452n_268",
2579
- shimmer: "_shimmer_h452n_1",
2580
- celebrate: "_celebrate_h452n_1",
2581
- expanded: "_expanded_h452n_290",
2582
- historySection: "_historySection_h452n_268",
2583
- historyItem: "_historyItem_h452n_309",
2584
- observation: "_observation_h452n_363",
2585
- question: "_question_h452n_368",
2586
- doneSuccess: "_doneSuccess_h452n_374",
2587
- historyContent: "_historyContent_h452n_410",
2588
- statusIcon: "_statusIcon_h452n_411",
2589
- doneError: "_doneError_h452n_420",
2590
- reflectionLines: "_reflectionLines_h452n_470",
2591
- historyMeta: "_historyMeta_h452n_477",
2592
- inputSectionWrapper: "_inputSectionWrapper_h452n_547",
2593
- hidden: "_hidden_h452n_563",
2594
- inputSection: "_inputSection_h452n_547",
2595
- taskInput: "_taskInput_h452n_574"
2600
+ wrapper: "_wrapper_18lru_1",
2601
+ shell: "_shell_18lru_57",
2602
+ background: "_background_18lru_57",
2603
+ "mask-running": "_mask-running_18lru_1",
2604
+ shellInner: "_shellInner_18lru_57",
2605
+ header: "_header_18lru_124",
2606
+ pulse: "_pulse_18lru_1",
2607
+ retryPulse: "_retryPulse_18lru_1",
2608
+ statusTextFadeOut: "_statusTextFadeOut_18lru_1",
2609
+ statusTextFadeIn: "_statusTextFadeIn_18lru_1",
2610
+ statusSection: "_statusSection_18lru_140",
2611
+ indicator: "_indicator_18lru_147",
2612
+ thinking: "_thinking_18lru_156",
2613
+ tool_executing: "_tool_executing_18lru_161",
2614
+ retry: "_retry_18lru_166",
2615
+ completed: "_completed_18lru_172",
2616
+ input: "_input_18lru_173",
2617
+ output: "_output_18lru_174",
2618
+ error: "_error_18lru_179",
2619
+ statusText: "_statusText_18lru_185",
2620
+ fadeOut: "_fadeOut_18lru_197",
2621
+ fadeIn: "_fadeIn_18lru_201",
2622
+ controls: "_controls_18lru_207",
2623
+ controlButton: "_controlButton_18lru_212",
2624
+ stopButton: "_stopButton_18lru_231",
2625
+ historySectionWrapper: "_historySectionWrapper_18lru_268",
2626
+ shimmer: "_shimmer_18lru_1",
2627
+ celebrate: "_celebrate_18lru_1",
2628
+ expanded: "_expanded_18lru_290",
2629
+ historySection: "_historySection_18lru_268",
2630
+ historyItem: "_historyItem_18lru_309",
2631
+ observation: "_observation_18lru_363",
2632
+ question: "_question_18lru_368",
2633
+ doneSuccess: "_doneSuccess_18lru_374",
2634
+ historyContent: "_historyContent_18lru_410",
2635
+ statusIcon: "_statusIcon_18lru_411",
2636
+ doneError: "_doneError_18lru_420",
2637
+ reflectionLines: "_reflectionLines_18lru_470",
2638
+ historyMeta: "_historyMeta_18lru_477",
2639
+ inputSectionWrapper: "_inputSectionWrapper_18lru_547",
2640
+ hidden: "_hidden_18lru_563",
2641
+ inputSection: "_inputSection_18lru_547",
2642
+ taskInput: "_taskInput_18lru_574"
2596
2643
  };
2597
2644
  //#endregion
2598
2645
  //#region src/ui/cards.ts
2599
2646
  /**
2600
- * Card HTML generation utilities for UI
2647
+ * UI 的卡片 HTML 生成工具
2601
2648
  */
2602
- /** Create a single history card */
2649
+ /** 创建单条历史卡片 */
2603
2650
  function createCard({ icon, content, meta, type }) {
2604
2651
  const typeClass = type ? UI_module_default[type] : "";
2605
2652
  const contentHtml = Array.isArray(content) ? `<div class="${UI_module_default.reflectionLines}">${content.map((line) => `<span>${escapeHtml(line)}</span>`).join("")}</div>` : `<span>${escapeHtml(content)}</span>`;
@@ -2613,7 +2660,7 @@ function createCard({ icon, content, meta, type }) {
2613
2660
  </div>
2614
2661
  `;
2615
2662
  }
2616
- /** Create reflection lines from reflection object */
2663
+ /** 从反思对象生成反思行 */
2617
2664
  function createReflectionLines(reflection) {
2618
2665
  const lines = [];
2619
2666
  if (reflection.evaluation_previous_goal) lines.push(`🔍 ${reflection.evaluation_previous_goal}`);
@@ -2624,14 +2671,14 @@ function createReflectionLines(reflection) {
2624
2671
  //#endregion
2625
2672
  //#region src/ui/UI.ts
2626
2673
  /**
2627
- * Agent control UI
2674
+ * Agent 控制 UI
2628
2675
  *
2629
- * Architecture:
2630
- * - History list: renders directly from agent.history (historical events)
2631
- * - Header bar: shows activity events (transient state) and agent status
2676
+ * 架构:
2677
+ * - 历史列表:直接从 agent.history 渲染(历史事件)
2678
+ * - 头部栏:展示活动事件(瞬时状态)和 agent 状态
2632
2679
  *
2633
- * This separation ensures data consistency - history is the single source of truth
2634
- * for what has been done, while activity shows what is happening now.
2680
+ * 这种分离保证了数据一致性 - 历史是已完成内容的唯一数据源,
2681
+ * 而活动状态展示的是当前正在发生的事情。
2635
2682
  */
2636
2683
  var UI = class {
2637
2684
  #wrapper;
@@ -2662,13 +2709,14 @@ var UI = class {
2662
2709
  #onActivity = (e) => this.#handleActivity(e.detail);
2663
2710
  #onAgentDispose = () => this.dispose();
2664
2711
  #askUserCallback = (question, options) => this.#askUser(question, options?.signal);
2712
+ /** 获取 UI 的根容器元素 */
2665
2713
  get wrapper() {
2666
2714
  return this.#wrapper;
2667
2715
  }
2668
2716
  /**
2669
- * Create a UI bound to an agent
2670
- * @param agent - Agent instance that implements UIAdapter
2671
- * @param config - Optional UI configuration
2717
+ * 创建绑定到某个 agent UI
2718
+ * @param agent - 实现 UIAdapter Agent 实例
2719
+ * @param config - 可选的 UI 配置
2672
2720
  */
2673
2721
  constructor(agent, config = {}) {
2674
2722
  this.#agent = agent;
@@ -2693,7 +2741,7 @@ var UI = class {
2693
2741
  this.#showInputArea();
2694
2742
  this.hide();
2695
2743
  }
2696
- /** Handle agent status change */
2744
+ /** 处理 agent 状态变化 */
2697
2745
  #handleStatusChange() {
2698
2746
  const status = this.#agent.status;
2699
2747
  const failed = status === "completed" && this.#agent.lastResult?.success === false;
@@ -2708,19 +2756,20 @@ var UI = class {
2708
2756
  if (status === "running") {
2709
2757
  this.show();
2710
2758
  this.#hideInputArea();
2759
+ if (!this.#isExpanded) this.#expand();
2711
2760
  }
2712
2761
  if (status === "completed" || status === "error" || status === "stopped") {
2713
2762
  if (!this.#isExpanded) this.#expand();
2714
2763
  if (this.#shouldShowInputArea()) this.#showInputArea();
2715
2764
  }
2716
2765
  }
2717
- /** Handle agent history change - re-render history list from agent.history */
2766
+ /** 处理 agent 历史变化 - 根据 agent.history 重新渲染历史列表 */
2718
2767
  #handleHistoryChange() {
2719
2768
  this.#renderHistory();
2720
2769
  }
2721
2770
  /**
2722
- * Handle agent activity - transient state for immediate UI feedback
2723
- * Activity events are NOT persisted in history, only used for header bar updates
2771
+ * 处理 agent 活动 - 用于即时 UI 反馈的瞬时状态
2772
+ * 活动事件不会持久化到历史中,仅用于头部栏更新
2724
2773
  */
2725
2774
  #handleActivity(activity) {
2726
2775
  switch (activity.type) {
@@ -2745,9 +2794,9 @@ var UI = class {
2745
2794
  }
2746
2795
  }
2747
2796
  /**
2748
- * Ask for user input (internal, called by agent via onAskUser).
2749
- * Rejects when `signal` aborts (task stopped or disposed), cleaning up the
2750
- * question card and pending state so the agent loop can settle.
2797
+ * 请求用户输入(内部方法,由 agent 通过 onAskUser 调用)。
2798
+ * `signal` 中止(任务被停止或销毁)时拒绝该请求,
2799
+ * 并清理问题卡片和挂起状态,以便 agent 循环能够收尾。
2751
2800
  */
2752
2801
  #askUser(question, signal) {
2753
2802
  if (this.#disposed) return Promise.reject(this.#createAbortError());
@@ -2777,9 +2826,11 @@ var UI = class {
2777
2826
  }
2778
2827
  });
2779
2828
  }
2829
+ /** 创建标准的中止异常(AbortError) */
2780
2830
  #createAbortError() {
2781
2831
  return new DOMException("The operation was aborted.", "AbortError");
2782
2832
  }
2833
+ /** 清空用户回答相关状态并移除临时卡片 */
2783
2834
  #clearQuestionState() {
2784
2835
  if (this.#userAnswerSignal && this.#userAnswerAbortHandler) this.#userAnswerSignal.removeEventListener("abort", this.#userAnswerAbortHandler);
2785
2836
  this.#userAnswerSignal = null;
@@ -2789,30 +2840,34 @@ var UI = class {
2789
2840
  this.#isWaitingForUserAnswer = false;
2790
2841
  this.#removeTempCards();
2791
2842
  }
2843
+ /** 拒绝挂起的用户问题(存在待回答问题时) */
2792
2844
  #rejectPendingQuestion() {
2793
2845
  const reject = this.#userAnswerRejecter;
2794
2846
  if (!reject) return;
2795
2847
  this.#clearQuestionState();
2796
2848
  reject(this.#createAbortError());
2797
2849
  }
2798
- /** Remove temporary question cards (only direct children for safety) */
2850
+ /** 移除临时问题卡片(为安全起见仅移除直接子节点) */
2799
2851
  #removeTempCards() {
2800
2852
  Array.from(this.#historySection.children).forEach((child) => {
2801
2853
  if (child.getAttribute("data-temp-card") === "true") child.remove();
2802
2854
  });
2803
2855
  this.#syncHistoryHeight();
2804
2856
  }
2857
+ /** 显示 UI(淡入 + 平移效果) */
2805
2858
  show() {
2806
2859
  this.wrapper.style.display = "block";
2807
2860
  this.wrapper.offsetHeight;
2808
2861
  this.wrapper.style.opacity = "1";
2809
2862
  this.wrapper.style.transform = "translateX(-50%) translateY(0)";
2810
2863
  }
2864
+ /** 隐藏 UI(淡出 + 下移效果) */
2811
2865
  hide() {
2812
2866
  this.wrapper.style.opacity = "0";
2813
2867
  this.wrapper.style.transform = "translateX(-50%) translateY(20px)";
2814
2868
  this.wrapper.style.display = "none";
2815
2869
  }
2870
+ /** 重置 UI 状态(拒绝挂起问题、清空状态文本、收起历史并显示输入框) */
2816
2871
  reset() {
2817
2872
  this.#rejectPendingQuestion();
2818
2873
  this.#statusText.textContent = this.#i18n.t("ui.ready");
@@ -2821,14 +2876,16 @@ var UI = class {
2821
2876
  this.#collapse();
2822
2877
  this.#showInputArea();
2823
2878
  }
2879
+ /** 展开历史弹窗(公开方法) */
2824
2880
  expand() {
2825
2881
  this.#expand();
2826
2882
  }
2883
+ /** 收起历史弹窗(公开方法) */
2827
2884
  collapse() {
2828
2885
  this.#collapse();
2829
2886
  }
2830
2887
  /**
2831
- * Dispose UI and clean up event listeners
2888
+ * 销毁 UI 并清理事件监听器
2832
2889
  */
2833
2890
  dispose() {
2834
2891
  if (this.#disposed) return;
@@ -2845,6 +2902,7 @@ var UI = class {
2845
2902
  this.#timers.clear();
2846
2903
  this.wrapper.remove();
2847
2904
  }
2905
+ /** 根据工具名称和参数生成"正在执行"提示文本 */
2848
2906
  #getToolExecutingText(toolName, args) {
2849
2907
  const a = args;
2850
2908
  switch (toolName) {
@@ -2859,14 +2917,14 @@ var UI = class {
2859
2917
  }
2860
2918
  }
2861
2919
  /**
2862
- * Action button handler: stop when running, close (dispose) when idle
2920
+ * 动作按钮处理:运行中则停止,空闲则关闭(销毁)
2863
2921
  */
2864
2922
  #handleActionButton() {
2865
2923
  if (this.#agent.status === "running") this.#agent.stop();
2866
2924
  else this.#agent.dispose();
2867
2925
  }
2868
2926
  /**
2869
- * Submit task
2927
+ * 提交任务
2870
2928
  */
2871
2929
  #submitTask() {
2872
2930
  const input = this.#taskInput.value.trim();
@@ -2880,7 +2938,7 @@ var UI = class {
2880
2938
  });
2881
2939
  }
2882
2940
  /**
2883
- * Handle user answer
2941
+ * 处理用户回答
2884
2942
  */
2885
2943
  #handleUserAnswer(input) {
2886
2944
  this.#removeTempCards();
@@ -2889,7 +2947,7 @@ var UI = class {
2889
2947
  resolve?.(input);
2890
2948
  }
2891
2949
  /**
2892
- * Show input area
2950
+ * 显示输入区域
2893
2951
  */
2894
2952
  #showInputArea(placeholder) {
2895
2953
  this.#taskInput.value = "";
@@ -2900,13 +2958,13 @@ var UI = class {
2900
2958
  }, 100);
2901
2959
  }
2902
2960
  /**
2903
- * Hide input area
2961
+ * 隐藏输入区域
2904
2962
  */
2905
2963
  #hideInputArea() {
2906
2964
  this.#inputSection.classList.add(UI_module_default.hidden);
2907
2965
  }
2908
2966
  /**
2909
- * Check if input area should be shown
2967
+ * 检查是否应显示输入区域
2910
2968
  */
2911
2969
  #shouldShowInputArea() {
2912
2970
  if (this.#isWaitingForUserAnswer) return true;
@@ -2915,6 +2973,7 @@ var UI = class {
2915
2973
  if (status === "completed" || status === "error" || status === "stopped") return this.#config.promptForNextTask ?? true;
2916
2974
  return false;
2917
2975
  }
2976
+ /** 创建 UI 根元素并挂载到 body,返回根容器 */
2918
2977
  #createWrapper() {
2919
2978
  const taskInputMaxLength = 1e3;
2920
2979
  const wrapper = document.createElement("div");
@@ -2933,9 +2992,9 @@ var UI = class {
2933
2992
  </div>
2934
2993
  </div>
2935
2994
  </div>
2936
- <div class="${UI_module_default.panel}">
2995
+ <div class="${UI_module_default.shell}">
2937
2996
  <div class="${UI_module_default.background}"></div>
2938
- <div class="${UI_module_default.panelInner}">
2997
+ <div class="${UI_module_default.shellInner}">
2939
2998
  <div class="${UI_module_default.header}">
2940
2999
  <div class="${UI_module_default.statusSection}">
2941
3000
  <div class="${UI_module_default.indicator} ${UI_module_default.thinking}"></div>
@@ -2965,6 +3024,7 @@ var UI = class {
2965
3024
  document.body.appendChild(wrapper);
2966
3025
  return wrapper;
2967
3026
  }
3027
+ /** 绑定所有 DOM 事件监听器(头部点击、展开按钮、动作按钮、回车提交等) */
2968
3028
  #setupEventListeners() {
2969
3029
  this.wrapper.querySelector(`.${UI_module_default.header}`).addEventListener("click", (e) => {
2970
3030
  if (e.target.closest(`.${UI_module_default.controlButton}`)) return;
@@ -2989,17 +3049,18 @@ var UI = class {
2989
3049
  e.stopPropagation();
2990
3050
  });
2991
3051
  }
3052
+ /** 在展开/收起状态之间切换 */
2992
3053
  #toggle() {
2993
3054
  if (this.#isExpanded) this.#collapse();
2994
3055
  else this.#expand();
2995
3056
  }
2996
3057
  /**
2997
- * Expand the history popup.
3058
+ * 展开历史弹窗。
2998
3059
  *
2999
- * `max-height` is measured in JS (not animated to a fixed CSS cap) so the
3000
- * transition distance always matches the real content height otherwise
3001
- * expand/collapse feel like different speeds (expand reaches its real
3002
- * height early and idles, collapse animates the full cap every time).
3060
+ * `max-height` JS 测量(而非动画到固定的 CSS 上限),因此
3061
+ * 过渡距离始终匹配真实内容高度否则展开/收起会给人
3062
+ * 速度不一致的感觉(展开会提前达到真实高度并停滞,
3063
+ * 而收起每次都按完整上限做动画)。
3003
3064
  */
3004
3065
  #expand() {
3005
3066
  this.#isExpanded = true;
@@ -3007,19 +3068,20 @@ var UI = class {
3007
3068
  this.#expandButton.textContent = "▲";
3008
3069
  this.#syncHistoryHeight();
3009
3070
  }
3071
+ /** 收起历史弹窗 */
3010
3072
  #collapse() {
3011
3073
  this.#isExpanded = false;
3012
3074
  this.wrapper.classList.remove(UI_module_default.expanded);
3013
3075
  this.#expandButton.textContent = "▼";
3014
3076
  this.#historySectionWrapper.style.maxHeight = "0px";
3015
3077
  }
3016
- /** Sync the history popup's max-height to its current (already capped) content height */
3078
+ /** 将历史弹窗的 max-height 同步为当前(已封顶的)内容高度 */
3017
3079
  #syncHistoryHeight() {
3018
3080
  if (!this.#isExpanded) return;
3019
3081
  this.#historySectionWrapper.style.maxHeight = `${this.#historySectionWrapper.scrollHeight}px`;
3020
3082
  }
3021
3083
  /**
3022
- * Start periodic header update loop
3084
+ * 启动周期性的头部更新循环
3023
3085
  */
3024
3086
  #startHeaderUpdateLoop() {
3025
3087
  this.#headerUpdateTimer = setInterval(() => {
@@ -3027,7 +3089,7 @@ var UI = class {
3027
3089
  }, 450);
3028
3090
  }
3029
3091
  /**
3030
- * Stop periodic header update loop
3092
+ * 停止周期性的头部更新循环
3031
3093
  */
3032
3094
  #stopHeaderUpdateLoop() {
3033
3095
  if (this.#headerUpdateTimer) {
@@ -3035,6 +3097,7 @@ var UI = class {
3035
3097
  this.#headerUpdateTimer = null;
3036
3098
  }
3037
3099
  }
3100
+ /** 设置一个受控延时定时器(销毁后自动忽略回调) */
3038
3101
  #setTimer(callback, delay) {
3039
3102
  const timer = setTimeout(() => {
3040
3103
  this.#timers.delete(timer);
@@ -3043,7 +3106,7 @@ var UI = class {
3043
3106
  this.#timers.add(timer);
3044
3107
  }
3045
3108
  /**
3046
- * Check if header needs update and trigger animation if not currently animating
3109
+ * 检查头部是否需要更新,若当前未在动画中则触发动画
3047
3110
  */
3048
3111
  #checkAndUpdateHeader() {
3049
3112
  if (!this.#pendingHeaderText || this.#isAnimating) return;
@@ -3056,7 +3119,7 @@ var UI = class {
3056
3119
  this.#animateTextChange(textToShow);
3057
3120
  }
3058
3121
  /**
3059
- * Animate text change with fade out/in effect
3122
+ * 以淡出/淡入效果动画切换文本
3060
3123
  */
3061
3124
  #animateTextChange(newText) {
3062
3125
  this.#isAnimating = true;
@@ -3071,35 +3134,37 @@ var UI = class {
3071
3134
  }, 300);
3072
3135
  }, 150);
3073
3136
  }
3137
+ /** 更新状态指示器的样式类 */
3074
3138
  #updateStatusIndicator(type) {
3075
3139
  const variant = type === "running" ? "thinking" : type === "executing" ? "tool_executing" : type === "retrying" ? "retry" : type;
3076
3140
  this.#indicator.className = UI_module_default.indicator;
3077
3141
  if (variant !== "idle" && variant !== "stopped") this.#indicator.classList.add(UI_module_default[variant]);
3078
3142
  }
3143
+ /** 将历史区域滚动到底部 */
3079
3144
  #scrollToBottom() {
3080
3145
  this.#setTimer(() => {
3081
3146
  this.#historySection.scrollTop = this.#historySection.scrollHeight;
3082
3147
  }, 0);
3083
3148
  }
3084
3149
  /**
3085
- * Render history directly from agent.history
3150
+ * 直接从 agent.history 渲染历史
3086
3151
  *
3087
- * Renders:
3088
- * 1. Task (first item, from agent.task)
3089
- * 2. Reflection cards (evaluation, memory, next_goal)
3090
- * 3. Tool execution with output
3091
- * 4. Observations
3152
+ * 渲染内容:
3153
+ * 1. 任务卡片(每个 task_start 事件对应一张,history 跨任务持久保留,因此可展示历次任务)
3154
+ * 2. 反思卡片(评估、记忆、下一步目标)
3155
+ * 3. 工具执行及输出
3156
+ * 4. 观察
3092
3157
  */
3093
3158
  #renderHistory() {
3094
3159
  const items = [];
3095
- const task = this.#agent.task;
3096
- if (task) items.push(this.#createTaskCard(task));
3097
3160
  const history = this.#agent.history;
3161
+ if (history.length === 0 && this.#agent.task) items.push(this.#createTaskCard(this.#agent.task));
3098
3162
  for (const event of history) items.push(...this.#createHistoryCards(event));
3099
3163
  this.#historySection.innerHTML = items.join("");
3100
3164
  this.#scrollToBottom();
3101
3165
  this.#syncHistoryHeight();
3102
3166
  }
3167
+ /** 生成任务卡片 HTML */
3103
3168
  #createTaskCard(task) {
3104
3169
  return createCard({
3105
3170
  icon: "🎯",
@@ -3107,11 +3172,13 @@ var UI = class {
3107
3172
  type: "input"
3108
3173
  });
3109
3174
  }
3110
- /** Create cards for a history event */
3175
+ /** 为一条历史事件生成卡片 */
3111
3176
  #createHistoryCards(event) {
3112
3177
  const cards = [];
3113
3178
  const meta = event.type === "step" && event.stepIndex !== void 0 ? this.#i18n.t("ui.step", { number: (event.stepIndex + 1).toString() }) : void 0;
3114
- if (event.type === "step") {
3179
+ if (event.type === "task_start") {
3180
+ if (event.task) cards.push(this.#createTaskCard(event.task));
3181
+ } else if (event.type === "step") {
3115
3182
  if (event.reflection) {
3116
3183
  const lines = createReflectionLines(event.reflection);
3117
3184
  if (lines.length > 0) cards.push(createCard({
@@ -3150,7 +3217,7 @@ var UI = class {
3150
3217
  }));
3151
3218
  return cards;
3152
3219
  }
3153
- /** Create cards for an action */
3220
+ /** 为一个动作生成卡片 */
3154
3221
  #createActionCards(action, meta) {
3155
3222
  const cards = [];
3156
3223
  if (action.name === "done") {
@@ -3197,7 +3264,7 @@ var UI = class {
3197
3264
  //#endregion
3198
3265
  //#region src/llm/errors.ts
3199
3266
  /**
3200
- * Error types and error handling for LLM invocations.
3267
+ * LLM 调用相关的错误类型与错误处理。
3201
3268
  */
3202
3269
  var InvokeErrorTypes = {
3203
3270
  NETWORK_ERROR: "network_error",
@@ -3250,19 +3317,30 @@ var colorCodes = {
3250
3317
  red: 31,
3251
3318
  yellow: 33
3252
3319
  };
3253
- /** Format text with standard ANSI terminal color escape sequences. */
3320
+ /**
3321
+ * 使用标准 ANSI 终端颜色转义序列格式化文本。
3322
+ * 给文本添加标准的 ANSI 终端颜色转义序列,使文本在终端中以指定颜色显示。
3323
+ * @param text 需要上色的原始文本
3324
+ * @param color 终端颜色名称
3325
+ * @param bold 是否加粗显示,默认 false
3326
+ * @returns 带 ANSI 颜色转义序列的文本
3327
+ */
3254
3328
  function formatTerminalText(text, color, bold = false) {
3255
3329
  return `${bold ? "\x1B[1m" : ""}\u001B[${colorCodes[color]}m${text}\u001B[0m`;
3256
3330
  }
3257
3331
  //#endregion
3258
3332
  //#region src/llm/utils.ts
3259
3333
  /**
3260
- * Utility functions for LLM integration
3334
+ * LLM 集成的工具函数
3261
3335
  */
3262
3336
  var debug = console.debug.bind(console, formatTerminalText("[LLM]", "gray"));
3263
3337
  /**
3264
- * Convert Zod schema to OpenAI tool format
3265
- * Uses Zod 4 native z.toJSONSchema()
3338
+ * Zod schema 转换为 OpenAI 工具格式
3339
+ * 使用 Zod 4 原生 z.toJSONSchema()
3340
+ *
3341
+ * @param name - 工具名称
3342
+ * @param tool - 工具定义
3343
+ * @returns OpenAI 格式的工具定义
3266
3344
  */
3267
3345
  function zodToOpenAITool(name, tool) {
3268
3346
  return {
@@ -3275,17 +3353,21 @@ function zodToOpenAITool(name, tool) {
3275
3353
  };
3276
3354
  }
3277
3355
  /**
3278
- * Patch model specific parameters. Only patches known models.
3356
+ * 为特定模型打补丁。仅修补已知的模型。
3279
3357
  *
3280
3358
  * @purpose
3281
- * - Reconcile the differences in the parameter schema each model accepts.
3282
- * - Disable thinking/reasoning, or lower it to the minimum where a full disable is impossible.
3283
- * - Minimize returned tokens.
3284
- * - Raise temperature for known smaller models to improve auto-recovery odds.
3285
- * @note Honor temperature if explicitly set by the user
3359
+ * - 调和各模型所接受的参数 schema 之间的差异。
3360
+ * - 禁用思考/推理,或在无法完全禁用时将其降到最低。
3361
+ * - 尽量减少返回的 token。
3362
+ * - 对已知的较小模型提高 temperature,以提升自动恢复的几率。
3363
+ * @note 若用户显式设置了 temperature 则尊重该设置
3286
3364
  *
3287
- * @todo Need vendor-specific patches.
3288
- * Local and 3rd-party hosted models may have different schema.
3365
+ * @todo 需要针对不同厂商的补丁。
3366
+ * 本地托管和第三方托管的模型可能有不同的 schema
3367
+ *
3368
+ * @param body - 请求体对象(会就地修改)
3369
+ * @param baseURL - 模型服务的基础 URL,用于识别 provider
3370
+ * @returns 修改后的请求体对象
3289
3371
  */
3290
3372
  function modelPatch(body, baseURL) {
3291
3373
  const model = body.model || "";
@@ -3402,19 +3484,25 @@ function modelPatch(body, baseURL) {
3402
3484
  return body;
3403
3485
  }
3404
3486
  /**
3405
- * check if a given model ID fits a specific model name
3487
+ * 检查给定的模型 ID 是否符合某个具体模型名称
3406
3488
  *
3407
3489
  * @note
3408
- * Different model providers may use different model IDs for the same model.
3409
- * For example, openai's `gpt-5.2` may called:
3490
+ * 不同的模型服务商可能对同一模型使用不同的模型 ID。
3491
+ * 例如,OpenAI `gpt-5.2` 可能被命名为:
3410
3492
  *
3411
3493
  * - `gpt-5.2-version`
3412
3494
  * - `gpt-5_2-date`
3413
3495
  * - `GPT-52-version-date`
3414
3496
  * - `openai/gpt-5.2-chat`
3415
3497
  *
3416
- * They should be treated as the same model.
3417
- * Normalize them to `gpt-52`
3498
+ * 它们应被视为同一模型。
3499
+ * 将其归一化为 `gpt-52`
3500
+ */
3501
+ /**
3502
+ * 归一化模型名称,将不同厂商对同一模型的命名统一为同一形式(如 `gpt-5.2` -> `gpt52`)
3503
+ *
3504
+ * @param modelName - 原始模型名称
3505
+ * @returns 归一化后的模型名称(小写、去掉前缀与特殊字符)
3418
3506
  */
3419
3507
  function normalizeModelName(modelName) {
3420
3508
  let normalizedName = modelName.toLowerCase();
@@ -3423,6 +3511,12 @@ function normalizeModelName(modelName) {
3423
3511
  normalizedName = normalizedName.replace(/\./g, "");
3424
3512
  return normalizedName;
3425
3513
  }
3514
+ /**
3515
+ * 根据 baseURL 判断服务提供商(目前仅识别 openrouter)
3516
+ *
3517
+ * @param baseURL - 模型服务的基础 URL
3518
+ * @returns 'openrouter' 或 undefined
3519
+ */
3426
3520
  function getProvider(baseURL) {
3427
3521
  if (!baseURL) return void 0;
3428
3522
  try {
@@ -3435,10 +3529,10 @@ function getProvider(baseURL) {
3435
3529
  //#endregion
3436
3530
  //#region src/llm/OpenAICompatibleClient.ts
3437
3531
  /**
3438
- * OpenAI Client implementation
3532
+ * OpenAI 客户端实现
3439
3533
  */
3440
3534
  /**
3441
- * Client for OpenAI compatible APIs
3535
+ * 兼容 OpenAI API 客户端
3442
3536
  */
3443
3537
  var OpenAICompatibleClient = class {
3444
3538
  config;
@@ -3447,6 +3541,16 @@ var OpenAICompatibleClient = class {
3447
3541
  this.config = config;
3448
3542
  this.fetch = config.customFetch;
3449
3543
  }
3544
+ /**
3545
+ * 调用 LLM 接口并执行模型请求的工具
3546
+ * 流程:转换工具定义 -> 构建并转换请求体 -> 调用 API -> 处理 HTTP 错误 -> 解析并校验响应 -> 执行工具
3547
+ *
3548
+ * @param messages - 对话消息列表
3549
+ * @param tools - 可用工具集合
3550
+ * @param abortSignal - 中止信号,用于取消请求
3551
+ * @param options - 可选的调用选项
3552
+ * @returns 工具调用结果
3553
+ */
3450
3554
  async invoke(messages, tools, abortSignal, options) {
3451
3555
  abortSignal?.throwIfAborted();
3452
3556
  const openaiTools = Object.entries(tools).map(([name, t]) => zodToOpenAITool(name, t));
@@ -3564,7 +3668,7 @@ var OpenAICompatibleClient = class {
3564
3668
  //#endregion
3565
3669
  //#region src/llm/LLM.ts
3566
3670
  /**
3567
- * LLM module
3671
+ * LLM 模块
3568
3672
  */
3569
3673
  var LLM = class extends EventTarget {
3570
3674
  config;
@@ -3575,9 +3679,9 @@ var LLM = class extends EventTarget {
3575
3679
  this.client = new OpenAICompatibleClient(this.config);
3576
3680
  }
3577
3681
  /**
3578
- * - call llm api *once*
3579
- * - invoke tool call *once*
3580
- * - return the result of the tool
3682
+ * - 调用一次 LLM API
3683
+ * - 调用一次工具
3684
+ * - 返回工具执行结果
3581
3685
  */
3582
3686
  async invoke(messages, tools, abortSignal, options) {
3583
3687
  return await withRetry(async () => this.client.invoke(messages, tools, abortSignal, options), {
@@ -3593,7 +3697,7 @@ var LLM = class extends EventTarget {
3593
3697
  }
3594
3698
  };
3595
3699
  /**
3596
- * Retry a function until it succeeds or reaches the maximum number of retries.
3700
+ * 重试某个函数,直到成功或达到最大重试次数。
3597
3701
  */
3598
3702
  async function withRetry(fn, settings) {
3599
3703
  let attempt = 0;
@@ -3609,6 +3713,12 @@ async function withRetry(fn, settings) {
3609
3713
  await new Promise((resolve) => setTimeout(resolve, 100));
3610
3714
  }
3611
3715
  }
3716
+ /**
3717
+ * 解析并校验 LLM 配置,填充默认值后返回完整配置
3718
+ *
3719
+ * @param config - 用户传入的 LLM 配置
3720
+ * @returns 解析后的完整配置
3721
+ */
3612
3722
  function parseLLMConfig(config) {
3613
3723
  if (!config.baseURL || !config.model) throw new Error("[PageAgent] LLM configuration required. Please provide: baseURL, model. See: https://alibaba.github.io/page-agent/docs/features/models");
3614
3724
  if (config.temperature !== void 0) console.warn("[PageAgent] LLMConfig.temperature is deprecated and will be removed in a future version. Use transformRequestBody to set it only for models you have verified accept it.");
@@ -3630,16 +3740,16 @@ var system_prompt_default = "You are an AI agent designed to operate in an itera
3630
3740
  //#region src/agent/utils/autoFixer.ts
3631
3741
  var log = console.log.bind(console, formatTerminalText("[autoFixer]", "yellow"));
3632
3742
  /**
3633
- * Normalize LLM response and fix common format issues.
3743
+ * 规范化 LLM 响应并修复常见的格式问题。
3634
3744
  *
3635
- * Handles:
3636
- * - No tool_calls but JSON in message.content (fallback)
3637
- * - Model returns action name as tool call instead of AgentOutput
3638
- * - Arguments wrapped as double JSON string
3639
- * - Nested function call format
3640
- * - Missing action field (fallback to wait)
3641
- * - Primitive action input for single-field tools (e.g. `{"click_element_by_index": 2}`)
3642
- * - etc.
3745
+ * 处理:
3746
+ * - 没有 tool_calls message.content 中含 JSON(回退)
3747
+ * - 模型将动作名作为工具调用返回而非 AgentOutput
3748
+ * - 参数被双重 JSON 字符串化
3749
+ * - 嵌套的函数调用格式
3750
+ * - 缺少 action 字段(回退为 wait
3751
+ * - 单字段工具的原始动作输入(如 `{"click_element_by_index": 2}`)
3752
+ * - 等等
3643
3753
  */
3644
3754
  function normalizeResponse(response, tools) {
3645
3755
  let resolvedArguments;
@@ -3698,11 +3808,11 @@ function normalizeResponse(response, tools) {
3698
3808
  };
3699
3809
  }
3700
3810
  /**
3701
- * Validate action against tool schemas. Provides clear error messages
3702
- * instead of letting the union schema produce unreadable errors.
3811
+ * 根据工具 schema 校验动作。提供清晰的错误信息,
3812
+ * 而不是让联合 schema 产生难以阅读的错误。
3703
3813
  *
3704
- * Also coerces primitive inputs for single-field tools:
3705
- * e.g. `{"click_element_by_index": 2}` → `{"click_element_by_index": {"index": 2}}`
3814
+ * 同时为单字段工具强制转换原始输入:
3815
+ * `{"click_element_by_index": 2}` → `{"click_element_by_index": {"index": 2}}`
3706
3816
  */
3707
3817
  function validateAction(action, tools) {
3708
3818
  if (typeof action !== "object" || action === null) return action;
@@ -3727,7 +3837,7 @@ function validateAction(action, tools) {
3727
3837
  return { [toolName]: result.data };
3728
3838
  }
3729
3839
  /**
3730
- * Safely parse JSON, return original input if not json.
3840
+ * 安全解析 JSON,如果不是 JSON 则返回原始输入。
3731
3841
  */
3732
3842
  function safeJsonParse(input) {
3733
3843
  if (typeof input === "string") try {
@@ -3738,9 +3848,9 @@ function safeJsonParse(input) {
3738
3848
  return input;
3739
3849
  }
3740
3850
  /**
3741
- * Extract and parse JSON from a string.
3742
- * - Treat content between the first `{` and the last `}` as JSON.
3743
- * - Try to parse that content as JSON and return the parsed value (object/array/primitive) if successful, otherwise return null.
3851
+ * 从字符串中提取并解析 JSON
3852
+ * - 将第一个 `{` 与最后一个 `}` 之间的内容视为 JSON
3853
+ * - 尝试将该内容解析为 JSON 并返回解析后的值(对象/数组/原始值),成功则返回,否则返回 null
3744
3854
  */
3745
3855
  function retrieveJsonFromString(str) {
3746
3856
  try {
@@ -3754,8 +3864,8 @@ function retrieveJsonFromString(str) {
3754
3864
  //#endregion
3755
3865
  //#region src/agent/utils/index.ts
3756
3866
  /**
3757
- * Wait for `seconds`. If a `signal` is provided, the wait is cancellable:
3758
- * aborting rejects with the signal's reason (an `AbortError`).
3867
+ * 等待 `seconds` 秒。若提供了 `signal`,则等待可被取消:
3868
+ * 中止时会以 signal reason(一个 `AbortError`)reject。
3759
3869
  */
3760
3870
  async function waitFor(seconds, signal) {
3761
3871
  if (!signal) {
@@ -3795,8 +3905,8 @@ var _global = globalThis;
3795
3905
  if (!_global.__PAGE_AGENT_IDS__) _global.__PAGE_AGENT_IDS__ = [];
3796
3906
  var ids = _global.__PAGE_AGENT_IDS__;
3797
3907
  /**
3798
- * Generate a random ID.
3799
- * @note Unique within this window.
3908
+ * 生成一个随机 ID
3909
+ * @note 在当前窗口内唯一。
3800
3910
  */
3801
3911
  function uid() {
3802
3912
  const id = randomID(ids);
@@ -3804,7 +3914,7 @@ function uid() {
3804
3914
  return id;
3805
3915
  }
3806
3916
  var llmsTxtCache = /* @__PURE__ */ new Map();
3807
- /** Fetch /llms.txt for a URL's origin. Cached per origin, `null` = tried and not found. */
3917
+ /** 获取某个 URL 源的 /llms.txt。按源缓存,`null` 表示已尝试过但未找到。 */
3808
3918
  async function fetchLlmsTxt(url) {
3809
3919
  let origin;
3810
3920
  try {
@@ -3834,10 +3944,10 @@ async function fetchLlmsTxt(url) {
3834
3944
  return result;
3835
3945
  }
3836
3946
  /**
3837
- * Simple assertion function that throws an error if the condition is falsy
3838
- * @param condition - The condition to assert
3839
- * @param message - Optional error message
3840
- * @throws Error if condition is falsy
3947
+ * 简单的断言函数:条件为假时抛出错误
3948
+ * @param condition - 要断言的条件
3949
+ * @param message - 可选的错误信息
3950
+ * @throws 条件为假时抛出错误
3841
3951
  */
3842
3952
  function assert(condition, message, silent) {
3843
3953
  if (!condition) {
@@ -3847,7 +3957,7 @@ function assert(condition, message, silent) {
3847
3957
  }
3848
3958
  }
3849
3959
  /**
3850
- * Suppress errors from a function.
3960
+ * 抑制函数产生的错误。
3851
3961
  */
3852
3962
  async function suppress(fn) {
3853
3963
  try {
@@ -3860,15 +3970,16 @@ async function suppress(fn) {
3860
3970
  //#endregion
3861
3971
  //#region src/agent/tools/index.ts
3862
3972
  /**
3863
- * Internal tools for PageAgent.
3864
- * @note Adapted from browser-use
3973
+ * PageAgent 的内部工具。
3974
+ * @note 改编自 browser-use
3865
3975
  */
3976
+ /** 创建一个工具定义(直接透传 options) */
3866
3977
  function tool(options) {
3867
3978
  return options;
3868
3979
  }
3869
3980
  /**
3870
- * Internal tools for PageAgent.
3871
- * Note: Using any to allow different parameter types for each tool
3981
+ * PageAgent 的内部工具。
3982
+ * 注:使用 any 以允许每个工具拥有不同的参数类型
3872
3983
  */
3873
3984
  var tools = /* @__PURE__ */ new Map();
3874
3985
  tools.set("done", tool({
@@ -3929,7 +4040,7 @@ tools.set("select_dropdown_option", tool({
3929
4040
  }
3930
4041
  }));
3931
4042
  /**
3932
- * @note Reference from browser-use
4043
+ * @note 参考自 browser-use
3933
4044
  */
3934
4045
  tools.set("scroll", tool({
3935
4046
  description: "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.",
@@ -3947,7 +4058,7 @@ tools.set("scroll", tool({
3947
4058
  }
3948
4059
  }));
3949
4060
  /**
3950
- * @todo Tables need a dedicated parser to extract structured data. This tool is useless.
4061
+ * @todo 表格需要专门的解析器来提取结构化数据。这个工具有点鸡肋。
3951
4062
  */
3952
4063
  tools.set("scroll_horizontally", tool({
3953
4064
  description: "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.",
@@ -3972,81 +4083,92 @@ tools.set("execute_javascript", tool({
3972
4083
  //#endregion
3973
4084
  //#region src/agent/AgentRuntime.ts
3974
4085
  /**
3975
- * AI agent for browser automation.
4086
+ * 用于浏览器自动化的 AI 智能体。
3976
4087
  *
3977
4088
  * @remarks
3978
- * ## Re-act Agent Loop
3979
- * - step
3980
- * - observe (gather information about current environment and context)
3981
- * - think (LLM calling)
3982
- * - reflection (evaluate history, generate memory, short-term planning)
3983
- * - action (give the action to approach the next goal)
3984
- * - act (execute the action)
3985
- * - loop
4089
+ * ## 反应式 Agent 循环
4090
+ * - 步骤
4091
+ * - 观察(收集当前环境与上下文信息)
4092
+ * - 思考(调用 LLM
4093
+ * - 反思(评估历史、生成记忆、短期规划)
4094
+ * - 行动(给出接近下一个目标的动作)
4095
+ * - 执行(执行动作)
4096
+ * - 循环
3986
4097
  *
3987
- * ## Event System
3988
- * - `statuschange` - Agent status transitions (idle → running → completed/error/stopped)
3989
- * - `historychange` - History events updated (persistent, part of agent memory)
3990
- * - `activity` - Real-time activity feedback (transient, for UI only)
3991
- * - `dispose` - Agent cleanup triggered
4098
+ * ## 事件系统
4099
+ * - `statuschange` - Agent 状态变更(idle → running → completed/error/stopped
4100
+ * - `historychange` - 历史事件更新(持久化,属于 Agent 记忆的一部分)
4101
+ * - `activity` - 实时活动反馈(瞬态,仅用于 UI
4102
+ * - `dispose` - 触发 Agent 清理
3992
4103
  *
3993
- * ## Information Streams
3994
- * 1. **History Events** (`history` array)
3995
- * - Persistent event stream that forms agent's memory
3996
- * - Included in LLM context across steps
3997
- * - Types: steps, observations, user takeovers, llm errors
4104
+ * ## 信息流
4105
+ * 1. **历史事件**(`history` 数组)
4106
+ * - 构成 Agent 记忆的持久事件流
4107
+ * - 跨步骤包含在 LLM 上下文中
4108
+ * - 类型:步骤、观察、用户接管、LLM 错误
3998
4109
  *
3999
- * 2. **Activity Events** (via `activity` event)
4000
- * - Transient UI feedback during task execution
4001
- * - NOT included in LLM context
4002
- * - Types: thinking, executing, executed, retrying, error
4110
+ * 2. **活动事件**(通过 `activity` 事件)
4111
+ * - 任务执行期间的瞬时 UI 反馈
4112
+ * - 不包含在 LLM 上下文中
4113
+ * - 类型:thinkingexecutingexecutedretryingerror
4003
4114
  */
4004
4115
  var AgentRuntime = class extends EventTarget {
4116
+ /** Agent 唯一 ID */
4005
4117
  id = uid();
4118
+ /** 合并后的 Agent 配置(含最大步数上限) */
4006
4119
  config;
4120
+ /** 内部工具集合(按工具名索引) */
4007
4121
  tools;
4008
- /** BrowserController for DOM operations */
4122
+ /** 用于 DOM 操作的 BrowserController */
4009
4123
  browserController;
4124
+ /** 当前任务描述 */
4010
4125
  task = "";
4126
+ /** 当前任务 ID */
4011
4127
  taskId = "";
4012
- /** History events */
4128
+ /** 历史事件 */
4013
4129
  history = [];
4014
- /** Whether this agent has been disposed */
4130
+ /** Agent 是否已被销毁 */
4015
4131
  disposed = false;
4016
4132
  /**
4017
- * Called when the agent needs to ask the user questions.
4018
- * If unset, the `ask_user` tool will be disabled.
4019
- * Implementations should reject the promise when `signal` aborts.
4133
+ * Agent 需要向用户提问时调用。
4134
+ * 若未设置,`ask_user` 工具将被禁用。
4135
+ * 实现应在 `signal` 中止时 reject Promise。
4020
4136
  * @example onAskUser: (q) => window.prompt(q) || ''
4021
4137
  */
4022
4138
  onAskUser;
4139
+ /** 当前 Agent 状态 */
4023
4140
  #status = "idle";
4141
+ /** LLM 实例(负责模型调用) */
4024
4142
  #llm;
4025
4143
  /**
4026
- * Task cancellation primitive: its signal reaches the LLM fetch, tools
4027
- * (via `ctx.signal`) and async callbacks. Aborted only by `stop`/`dispose`
4028
- * (during a task) or task setup, always WITHOUT a reason so `signal.reason`
4029
- * stays a standard `AbortError`.
4144
+ * 任务取消原语:其 signal 会传递给 LLM fetch、工具(通过 `ctx.signal`)和异步回调。
4145
+ * 仅由 `stop`/`dispose`(任务进行中)或任务初始化时中止,且始终不带 reason,
4146
+ * 这样 `signal.reason` 保持为标准的 `AbortError`。
4030
4147
  */
4031
4148
  #abortController = new AbortController();
4149
+ /** 本轮任务收集的观察消息(暂存,稍后统一写入历史) */
4032
4150
  #observations = [];
4033
- /** Resolves when the current run has fully settled. Awaited by `stop()`. */
4151
+ /** 当前任务在 history 数组中的起始下标(用于跨任务保留历史时,把 prompt/循环检测限定在当前任务范围内) */
4152
+ #taskHistoryStart = 0;
4153
+ /** 当前一次运行完全结束时 resolve。由 `stop()` 等待。 */
4034
4154
  #running = Promise.resolve();
4035
4155
  #lastResult = null;
4036
- /** internal states during a single task execution */
4156
+ /** 单次任务执行期间的内部状态 */
4037
4157
  #states = {
4038
- /** Accumulated wait time in seconds */
4158
+ /** 累计等待时间(秒) */
4039
4159
  totalWaitTime: 0,
4040
- /** For detecting navigation */
4160
+ /** 用于检测页面导航 */
4041
4161
  lastURL: "",
4042
- /** Browser state */
4162
+ /** 浏览器状态 */
4043
4163
  browserState: null
4044
4164
  };
4165
+ /** 构造函数:初始化配置、LLM、工具集,并注册重试事件监听与注入自定义工具 */
4045
4166
  constructor(config) {
4046
4167
  super();
4047
4168
  this.config = {
4048
4169
  ...config,
4049
- maxSteps: config.maxSteps ?? 40
4170
+ maxSteps: config.maxSteps ?? 40,
4171
+ historyWindow: config.historyWindow ?? 5
4050
4172
  };
4051
4173
  this.#llm = new LLM(this.config);
4052
4174
  this.tools = new Map(tools);
@@ -4080,31 +4202,35 @@ var AgentRuntime = class extends EventTarget {
4080
4202
  }
4081
4203
  if (!this.config.experimentalScriptExecutionTool) this.tools.delete("execute_javascript");
4082
4204
  }
4083
- /** Get current agent status */
4205
+ /** 获取当前 Agent 状态 */
4084
4206
  get status() {
4085
4207
  return this.#status;
4086
4208
  }
4087
- /** Result of the most recent run, or `null` before the first run completes. */
4209
+ /** 最近一次运行的结果,首次运行完成前为 `null`。 */
4088
4210
  get lastResult() {
4089
4211
  return this.#lastResult;
4090
4212
  }
4091
- /** Emit statuschange event */
4213
+ /** 当前任务自身的历史事件(不含之前任务留下的记录),用于 prompt 拼装、循环检测等按任务隔离的场景 */
4214
+ get #currentTaskHistory() {
4215
+ return this.history.slice(this.#taskHistoryStart);
4216
+ }
4217
+ /** 触发 statuschange 事件 */
4092
4218
  #emitStatusChange() {
4093
4219
  this.dispatchEvent(new Event("statuschange"));
4094
4220
  }
4095
- /** Emit historychange event */
4221
+ /** 触发 historychange 事件 */
4096
4222
  #emitHistoryChange(pushHistoricalEvent) {
4097
4223
  if (pushHistoricalEvent) this.history.push(pushHistoricalEvent);
4098
4224
  this.dispatchEvent(new Event("historychange"));
4099
4225
  }
4100
4226
  /**
4101
- * Emit activity event - for transient UI feedback
4102
- * @param activity - Current agent activity
4227
+ * 触发 activity 事件 - 用于瞬态的 UI 反馈
4228
+ * @param activity - 当前的 Agent 活动
4103
4229
  */
4104
4230
  #emitActivity(activity) {
4105
4231
  this.dispatchEvent(new CustomEvent("activity", { detail: activity }));
4106
4232
  }
4107
- /** Update status and emit event */
4233
+ /** 更新状态并触发事件 */
4108
4234
  #setStatus(status) {
4109
4235
  if (this.#status !== status) {
4110
4236
  this.#status = status;
@@ -4112,17 +4238,17 @@ var AgentRuntime = class extends EventTarget {
4112
4238
  }
4113
4239
  }
4114
4240
  /**
4115
- * Push an observation message to the history event stream.
4116
- * This will be visible in <agent_history> and remain persistent in memory across steps.
4241
+ * 向历史事件流推送一条观察消息。
4242
+ * 它会出现在 <agent_history> 中,并在跨步骤的记忆中保持持久。
4117
4243
  * @experimental @internal
4118
- * @note history change will be emitted before next step starts
4244
+ * @note 历史变更会在下一步开始前触发
4119
4245
  */
4120
4246
  pushObservation(content) {
4121
4247
  this.#observations.push(content);
4122
4248
  }
4123
4249
  /**
4124
- * Stop the current task and wait until the run has fully settled (including lifecycle hooks).
4125
- * @note never await .stop() in a lifecycle hook.
4250
+ * 停止当前任务,并等待运行完全结束(包括生命周期钩子)。
4251
+ * @note 切勿在生命周期钩子中 await .stop()
4126
4252
  */
4127
4253
  async stop() {
4128
4254
  if (this.#status !== "running") return;
@@ -4130,8 +4256,8 @@ var AgentRuntime = class extends EventTarget {
4130
4256
  await this.#running;
4131
4257
  }
4132
4258
  /**
4133
- * external errors (pre-checks/config/hooks) will threw;
4134
- * agent errors will be caught and added to history, and return a failed result
4259
+ * 外部错误(前置检查/配置/钩子)会直接抛出;
4260
+ * Agent 内部错误会被捕获并加入历史,同时返回失败结果
4135
4261
  */
4136
4262
  async execute(task) {
4137
4263
  if (this.disposed) throw new Error("PageAgent has been disposed. Create a new instance.");
@@ -4139,7 +4265,11 @@ var AgentRuntime = class extends EventTarget {
4139
4265
  if (!task) throw new Error("Task is required");
4140
4266
  this.task = task;
4141
4267
  this.taskId = uid();
4142
- this.history = [];
4268
+ this.#taskHistoryStart = this.history.length;
4269
+ this.#emitHistoryChange({
4270
+ type: "task_start",
4271
+ task
4272
+ });
4143
4273
  this.#observations = [];
4144
4274
  this.#states = {
4145
4275
  totalWaitTime: 0,
@@ -4180,7 +4310,7 @@ var AgentRuntime = class extends EventTarget {
4180
4310
  taskResult = {
4181
4311
  success: false,
4182
4312
  data: message,
4183
- history: this.history
4313
+ history: this.#currentTaskHistory
4184
4314
  };
4185
4315
  this.#lastResult = taskResult;
4186
4316
  finalStatus = "error";
@@ -4238,7 +4368,7 @@ var AgentRuntime = class extends EventTarget {
4238
4368
  taskResult = {
4239
4369
  success,
4240
4370
  data,
4241
- history: this.history
4371
+ history: this.#currentTaskHistory
4242
4372
  };
4243
4373
  this.#lastResult = taskResult;
4244
4374
  finalStatus = "completed";
@@ -4260,14 +4390,14 @@ var AgentRuntime = class extends EventTarget {
4260
4390
  taskResult = {
4261
4391
  success: false,
4262
4392
  data: message,
4263
- history: this.history
4393
+ history: this.#currentTaskHistory
4264
4394
  };
4265
4395
  this.#lastResult = taskResult;
4266
4396
  finalStatus = isAbortError ? "stopped" : "error";
4267
4397
  break;
4268
4398
  } finally {
4269
4399
  console.groupEnd();
4270
- await onAfterStep?.(this, this.history);
4400
+ await onAfterStep?.(this, this.#currentTaskHistory);
4271
4401
  }
4272
4402
  step++;
4273
4403
  }
@@ -4289,13 +4419,13 @@ var AgentRuntime = class extends EventTarget {
4289
4419
  }
4290
4420
  }
4291
4421
  /**
4292
- * Merge all tools into a single MacroTool with the following input:
4422
+ * 将所有工具合并为单个 MacroTool,其输入如下:
4293
4423
  * - thinking: string
4294
4424
  * - evaluation_previous_goal: string
4295
4425
  * - memory: string
4296
4426
  * - next_goal: string
4297
4427
  * - action: { toolName: toolInput }
4298
- * where action must be selected from tools defined in this.tools
4428
+ * 其中 action 必须从 this.tools 中定义的工具中选择
4299
4429
  */
4300
4430
  #packMacroTool() {
4301
4431
  const tools = this.tools;
@@ -4354,7 +4484,7 @@ var AgentRuntime = class extends EventTarget {
4354
4484
  };
4355
4485
  }
4356
4486
  /**
4357
- * Get system prompt, dynamically replace language settings based on configured language
4487
+ * 获取系统提示词,根据配置的语言动态替换语言设置
4358
4488
  */
4359
4489
  #getSystemPrompt() {
4360
4490
  if (this.config.customSystemPrompt) return this.config.customSystemPrompt;
@@ -4362,7 +4492,7 @@ var AgentRuntime = class extends EventTarget {
4362
4492
  return system_prompt_default.replace(/Default working language: \*\*.*?\*\*/, `Default working language: **${targetLanguage}**`);
4363
4493
  }
4364
4494
  /**
4365
- * Get instructions from config
4495
+ * 从配置中获取指令
4366
4496
  */
4367
4497
  async #getInstructions() {
4368
4498
  const { instructions, experimentalLlmsTxt } = this.config;
@@ -4384,12 +4514,12 @@ var AgentRuntime = class extends EventTarget {
4384
4514
  return result;
4385
4515
  }
4386
4516
  /**
4387
- * Generate system observations before each step
4388
- * @todo console error
4517
+ * 在每一步之前生成系统观察
4518
+ * @todo 控制台错误
4389
4519
  */
4390
4520
  async #handleObservations(step) {
4391
4521
  if (this.#states.totalWaitTime >= 3) this.pushObservation(`You have waited ${this.#states.totalWaitTime} seconds accumulatively. DO NOT wait any longer unless you have a good reason.`);
4392
- const recentSteps = this.history.filter((e) => e.type === "step").slice(-3);
4522
+ const recentSteps = this.#currentTaskHistory.filter((e) => e.type === "step").slice(-3);
4393
4523
  if (recentSteps.length === 3) {
4394
4524
  const isSameAction = (a, b) => a.action.name === b.action.name && JSON.stringify(a.action.input) === JSON.stringify(b.action.input);
4395
4525
  if (isSameAction(recentSteps[0], recentSteps[1]) && isSameAction(recentSteps[1], recentSteps[2])) this.pushObservation(`⚠️ Loop detected: you repeated the exact same action ("${recentSteps[0].action.name}" with identical input) 3 times in a row with no new result. STOP repeating it. If the target genuinely does not exist (e.g. search returns empty), report this explicitly and call \`done\`, or try a clearly different action/strategy instead.`);
@@ -4415,11 +4545,15 @@ var AgentRuntime = class extends EventTarget {
4415
4545
  this.#emitHistoryChange();
4416
4546
  }
4417
4547
  }
4548
+ /**
4549
+ * 组装发给 LLM 的完整用户提示词(含指令、Agent 状态、历史、浏览器状态)
4550
+ */
4418
4551
  async #assembleUserPrompt() {
4419
4552
  const browserState = this.#states.browserState;
4420
4553
  let prompt = "";
4421
4554
  prompt += await this.#getInstructions();
4422
- const stepCount = this.history.filter((e) => e.type === "step").length;
4555
+ const currentTaskHistory = this.#currentTaskHistory;
4556
+ const stepCount = currentTaskHistory.filter((e) => e.type === "step").length;
4423
4557
  prompt += "<agent_state>\n";
4424
4558
  prompt += "<user_request>\n";
4425
4559
  prompt += `${this.task}\n`;
@@ -4429,10 +4563,10 @@ var AgentRuntime = class extends EventTarget {
4429
4563
  prompt += `Current time: ${(/* @__PURE__ */ new Date()).toLocaleString()}\n`;
4430
4564
  prompt += "</step_info>\n";
4431
4565
  prompt += "</agent_state>\n\n";
4432
- const windowStart = Math.max(0, stepCount - 5);
4566
+ const windowStart = Math.max(0, stepCount - this.config.historyWindow);
4433
4567
  prompt += "<agent_history>\n";
4434
4568
  let stepIndex = 0;
4435
- for (const event of this.history) if (event.type === "step") {
4569
+ for (const event of currentTaskHistory) if (event.type === "step") {
4436
4570
  stepIndex++;
4437
4571
  if (stepIndex <= windowStart) {
4438
4572
  prompt += `<step_${stepIndex}>Action: ${event.action.name} — Next Goal: ${event.reflection.next_goal}</step_${stepIndex}>\n`;
@@ -4457,6 +4591,7 @@ var AgentRuntime = class extends EventTarget {
4457
4591
  prompt += "</browser_state>\n\n";
4458
4592
  return prompt;
4459
4593
  }
4594
+ /** 销毁 Agent:中止任务、释放浏览器控制器,并触发 dispose 事件(一次性) */
4460
4595
  dispose() {
4461
4596
  if (this.disposed) return;
4462
4597
  this.disposed = true;
@@ -4479,8 +4614,13 @@ var AgentRuntime = class extends EventTarget {
4479
4614
  };
4480
4615
  //#endregion
4481
4616
  //#region src/agent/MyPageAgent.ts
4617
+ /**
4618
+ * 完整页面智能体:组合了 AgentRuntime、BrowserController 与 UI
4619
+ */
4482
4620
  var MyPageAgent = class extends AgentRuntime {
4621
+ /** UI 实例(用于用户交互与反馈) */
4483
4622
  ui;
4623
+ /** 构造函数:创建浏览器控制器、父类运行时与 UI 实例 */
4484
4624
  constructor(config) {
4485
4625
  const browserController = new BrowserController({
4486
4626
  ...config,
@@ -4499,6 +4639,11 @@ var MyPageAgent = class extends AgentRuntime {
4499
4639
  };
4500
4640
  //#endregion
4501
4641
  //#region src/index.ts
4642
+ /**
4643
+ * 创建智能体实例的快捷函数,内部直接实例化 MyPageAgent。
4644
+ * @param config 智能体的配置对象
4645
+ * @returns 创建好的 MyPageAgent 实例
4646
+ */
4502
4647
  function createAgent(config) {
4503
4648
  return new MyPageAgent(config);
4504
4649
  }