@coolkiller007/my-page-agent 0.1.15 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -275,7 +275,7 @@ declare class AgentRuntime extends EventTarget {
275
275
  * 外部错误(前置检查/配置/钩子)会直接抛出;
276
276
  * Agent 内部错误会被捕获并加入历史,同时返回失败结果
277
277
  */
278
- execute(task: string): Promise<ExecutionResult>;
278
+ execute(task: string, attachments?: Attachment[]): Promise<ExecutionResult>;
279
279
  /** 销毁 Agent:中止任务、释放浏览器控制器,并触发 dispose 事件(一次性) */
280
280
  dispose(): void;
281
281
  }
@@ -323,6 +323,25 @@ declare interface AgentTool<TParams = any> {
323
323
  execute: (this: AgentRuntime, args: TParams, ctx: ToolContext) => Promise<string>;
324
324
  }
325
325
 
326
+ /**
327
+ * 用户随任务一起上传的附件(图片或文本类文件)
328
+ */
329
+ declare interface Attachment {
330
+ name: string;
331
+ mimeType: string;
332
+ dataUrl: string;
333
+ }
334
+
335
+ /**
336
+ * 用户随任务一起上传的附件(图片或文本类文件)
337
+ * @note 与 agent/types.ts 中的 Attachment 结构一致,但独立声明以保持 UI 与 Agent 解耦
338
+ */
339
+ declare interface Attachment_2 {
340
+ name: string;
341
+ mimeType: string;
342
+ dataUrl: string;
343
+ }
344
+
326
345
  /**
327
346
  * BrowserController 管理 DOM 状态和元素交互。
328
347
  * 它为所有 DOM 操作提供异步方法,并保持状态隔离。
@@ -634,6 +653,11 @@ declare const locales: {
634
653
  readonly expand: "Expand history";
635
654
  readonly collapse: "Collapse history";
636
655
  readonly step: "Step {{number}}";
656
+ readonly attachButtonTitle: "Attach files";
657
+ readonly attachTooMany: "You can attach up to {{max}} files";
658
+ readonly attachTooLarge: "\"{{name}}\" exceeds the {{maxMB}}MB size limit";
659
+ readonly attachUnsupportedType: "\"{{name}}\" is not a supported file type";
660
+ readonly attachmentsLabel: "📎 Attachments: {{names}}";
637
661
  readonly tools: {
638
662
  readonly clicking: "Clicking element [{{index}}]...";
639
663
  readonly inputting: "Inputting text to element [{{index}}]...";
@@ -678,6 +702,11 @@ declare const locales: {
678
702
  readonly expand: "展开历史";
679
703
  readonly collapse: "收起历史";
680
704
  readonly step: "步骤 {{number}}";
705
+ readonly attachButtonTitle: "添加附件";
706
+ readonly attachTooMany: "最多可添加 {{max}} 个文件";
707
+ readonly attachTooLarge: "\"{{name}}\" 超过 {{maxMB}}MB 大小限制";
708
+ readonly attachUnsupportedType: "\"{{name}}\" 不是支持的文件类型";
709
+ readonly attachmentsLabel: "📎 附件: {{names}}";
681
710
  readonly tools: {
682
711
  readonly clicking: "正在点击元素 [{{index}}]...";
683
712
  readonly inputting: "正在输入文本到元素 [{{index}}]...";
@@ -755,6 +784,7 @@ declare type SupportedLanguage_2 = keyof typeof locales;
755
784
  declare interface TaskStartEvent {
756
785
  type: 'task_start';
757
786
  task: string;
787
+ attachmentNames?: string[];
758
788
  }
759
789
 
760
790
  declare interface TextDomNode {
@@ -873,6 +903,8 @@ declare interface UIAdapter extends EventTarget {
873
903
  content?: string;
874
904
  /** 仅用于 'task_start' 类型 */
875
905
  task?: string;
906
+ /** 仅用于 'task_start' 类型 */
907
+ attachmentNames?: string[];
876
908
  /** 仅用于 'retry' 类型 */
877
909
  attempt?: number;
878
910
  maxAttempts?: number;
@@ -890,8 +922,8 @@ declare interface UIAdapter extends EventTarget {
890
922
  onAskUser?: (question: string, options?: {
891
923
  signal: AbortSignal;
892
924
  }) => Promise<string>;
893
- /** 执行一个任务 */
894
- execute(task: string): Promise<unknown>;
925
+ /** 执行一个任务;attachments 为随任务一起上传的图片/文本附件(可选) */
926
+ execute(task: string, attachments?: Attachment_2[]): Promise<unknown>;
895
927
  /** 停止当前任务(agent 仍可复用) */
896
928
  stop(): Promise<void>;
897
929
  /** 销毁 agent(终态,不可复用) */
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_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); }})();
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_194hg_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_194hg_1 {\n from {\n transform: translateX(-100%);\n }\n to {\n transform: translateX(100%);\n }\n}\n\n/* 状态条 + 输入区的外层容器;.background 光效挂在这一层,.shellInner 负责裁出圆角 */\n._shell_194hg_57 {\n position: relative;\n width: 100%;\n}\n\n._background_194hg_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_194hg_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_194hg_1 2s linear infinite;\n animation-delay: 1s;\n }\n}\n\n/* 可视区域:靠 overflow:hidden + border-radius 统一裁角,\n * header 单独存在(运行中输入区隐藏)或 header+输入区一起存在时,四角都自动是圆的,\n * 不用再像以前那样给 header 写一条\"输入区隐藏时补圆角\"的特判规则。 */\n._shellInner_194hg_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_194hg_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_194hg_140 {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-height: 24px; /* 确保垂直居中 */\n\n ._indicator_194hg_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_194hg_156 {\n background: rgb(57, 182, 255);\n animation: _pulse_194hg_1 0.8s ease-in-out infinite;\n }\n\n &._tool_executing_194hg_161 {\n background: rgb(189, 69, 251);\n animation: _pulse_194hg_1 0.6s ease-in-out infinite;\n }\n\n &._retry_194hg_166 {\n background: rgb(255, 214, 0);\n animation: _retryPulse_194hg_1 1s ease-in-out infinite;\n }\n\n /* 静止状态 - 无动画 */\n &._completed_194hg_172,\n &._input_194hg_173,\n &._output_194hg_174 {\n background: rgb(34, 197, 94);\n animation: none;\n }\n\n &._error_194hg_179 {\n background: rgb(239, 68, 68);\n animation: none;\n }\n }\n\n ._statusText_194hg_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_194hg_197 {\n animation: _statusTextFadeOut_194hg_1 0.3s ease forwards;\n }\n\n &._fadeIn_194hg_201 {\n animation: _statusTextFadeIn_194hg_1 0.3s ease forwards;\n }\n }\n }\n\n ._controls_194hg_207 {\n display: flex;\n align-items: center;\n gap: 4px;\n\n ._controlButton_194hg_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_194hg_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_194hg_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_194hg_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_194hg_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_194hg_1._expanded_194hg_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_194hg_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_194hg_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_194hg_172,\n &._input_194hg_173,\n &._output_194hg_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_194hg_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_194hg_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_194hg_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_194hg_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_194hg_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_194hg_1 2s ease-in-out infinite;\n }\n\n ._historyContent_194hg_410 {\n ._statusIcon_194hg_411 {\n font-size: 16px;\n animation: _celebrate_194hg_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_194hg_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_194hg_410 {\n ._statusIcon_194hg_411 {\n font-size: 16px;\n filter: drop-shadow(0 2px 4px rgba(239, 68, 68, 0.5));\n }\n }\n }\n\n ._historyContent_194hg_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_194hg_411 {\n font-size: 12px;\n flex-shrink: 0;\n line-height: 1;\n transition: all 0.3s ease;\n }\n\n ._reflectionLines_194hg_470 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n }\n\n ._historyMeta_194hg_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_194hg_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_194hg_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_194hg_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_194hg_1 {\n 0% {\n left: -100%;\n }\n 100% {\n left: 100%;\n }\n}\n\n/* 输入区域样式 */\n._inputSectionWrapper_194hg_547 {\n width: 100%;\n margin: 0 auto;\n\n overflow: hidden;\n max-height: 0; /* 由 JS 同步为真实内容高度(含附件条、错误提示),见 #syncInputHeight */\n\n transition: max-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_194hg_563 {\n max-height: 0;\n }\n\n ._inputSection_194hg_547 {\n position: relative;\n display: flex;\n align-items: center;\n height: var(--input-height);\n padding: 8px; /* 与上方状态条 padding 保持一致 */\n\n ._attachButton_194hg_574 {\n position: absolute;\n right: 14px;\n bottom: 12px;\n z-index: 1;\n width: 26px;\n height: 26px;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: rgba(255, 255, 255, 0.85);\n font-size: 13px;\n line-height: 1;\n cursor: pointer;\n }\n\n ._taskInput_194hg_590 {\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 40px 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 ._attachmentsBar_194hg_618 {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 8px 8px 0;\n\n &:empty {\n display: none;\n }\n }\n\n ._attachmentChip_194hg_629 {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n max-width: 160px;\n padding: 3px 4px 3px 8px;\n border-radius: 10px;\n background: rgba(255, 255, 255, 0.1);\n color: rgba(255, 255, 255, 0.85);\n font-size: 12px;\n white-space: nowrap;\n\n span {\n overflow: hidden;\n text-overflow: ellipsis;\n }\n }\n\n ._attachmentChipRemove_194hg_647 {\n flex-shrink: 0;\n border: none;\n background: transparent;\n color: rgba(255, 255, 255, 0.6);\n cursor: pointer;\n font-size: 13px;\n line-height: 1;\n padding: 0 4px;\n\n &:hover {\n color: rgba(255, 255, 255, 0.95);\n }\n }\n\n ._attachError_194hg_662 {\n padding: 4px 8px 0;\n color: rgb(255, 138, 138);\n font-size: 12px;\n\n &:empty {\n display: none;\n padding: 0;\n }\n }\n\n ._fileInput_194hg_673 {\n display: none;\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
@@ -2480,6 +2480,11 @@ var locales = {
2480
2480
  expand: "Expand history",
2481
2481
  collapse: "Collapse history",
2482
2482
  step: "Step {{number}}",
2483
+ attachButtonTitle: "Attach files",
2484
+ attachTooMany: "You can attach up to {{max}} files",
2485
+ attachTooLarge: "\"{{name}}\" exceeds the {{maxMB}}MB size limit",
2486
+ attachUnsupportedType: "\"{{name}}\" is not a supported file type",
2487
+ attachmentsLabel: "📎 Attachments: {{names}}",
2483
2488
  tools: {
2484
2489
  clicking: "Clicking element [{{index}}]...",
2485
2490
  inputting: "Inputting text to element [{{index}}]...",
@@ -2522,6 +2527,11 @@ var locales = {
2522
2527
  expand: "展开历史",
2523
2528
  collapse: "收起历史",
2524
2529
  step: "步骤 {{number}}",
2530
+ attachButtonTitle: "添加附件",
2531
+ attachTooMany: "最多可添加 {{max}} 个文件",
2532
+ attachTooLarge: "\"{{name}}\" 超过 {{maxMB}}MB 大小限制",
2533
+ attachUnsupportedType: "\"{{name}}\" 不是支持的文件类型",
2534
+ attachmentsLabel: "📎 附件: {{names}}",
2525
2535
  tools: {
2526
2536
  clicking: "正在点击元素 [{{index}}]...",
2527
2537
  inputting: "正在输入文本到元素 [{{index}}]...",
@@ -2597,49 +2607,55 @@ function escapeHtml(text) {
2597
2607
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
2598
2608
  }
2599
2609
  var UI_module_default = {
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"
2610
+ wrapper: "_wrapper_194hg_1",
2611
+ shell: "_shell_194hg_57",
2612
+ background: "_background_194hg_57",
2613
+ "mask-running": "_mask-running_194hg_1",
2614
+ shellInner: "_shellInner_194hg_57",
2615
+ header: "_header_194hg_124",
2616
+ pulse: "_pulse_194hg_1",
2617
+ retryPulse: "_retryPulse_194hg_1",
2618
+ statusTextFadeOut: "_statusTextFadeOut_194hg_1",
2619
+ statusTextFadeIn: "_statusTextFadeIn_194hg_1",
2620
+ statusSection: "_statusSection_194hg_140",
2621
+ indicator: "_indicator_194hg_147",
2622
+ thinking: "_thinking_194hg_156",
2623
+ tool_executing: "_tool_executing_194hg_161",
2624
+ retry: "_retry_194hg_166",
2625
+ completed: "_completed_194hg_172",
2626
+ input: "_input_194hg_173",
2627
+ output: "_output_194hg_174",
2628
+ error: "_error_194hg_179",
2629
+ statusText: "_statusText_194hg_185",
2630
+ fadeOut: "_fadeOut_194hg_197",
2631
+ fadeIn: "_fadeIn_194hg_201",
2632
+ controls: "_controls_194hg_207",
2633
+ controlButton: "_controlButton_194hg_212",
2634
+ stopButton: "_stopButton_194hg_231",
2635
+ historySectionWrapper: "_historySectionWrapper_194hg_268",
2636
+ shimmer: "_shimmer_194hg_1",
2637
+ celebrate: "_celebrate_194hg_1",
2638
+ expanded: "_expanded_194hg_290",
2639
+ historySection: "_historySection_194hg_268",
2640
+ historyItem: "_historyItem_194hg_309",
2641
+ observation: "_observation_194hg_363",
2642
+ question: "_question_194hg_368",
2643
+ doneSuccess: "_doneSuccess_194hg_374",
2644
+ historyContent: "_historyContent_194hg_410",
2645
+ statusIcon: "_statusIcon_194hg_411",
2646
+ doneError: "_doneError_194hg_420",
2647
+ reflectionLines: "_reflectionLines_194hg_470",
2648
+ historyMeta: "_historyMeta_194hg_477",
2649
+ inputSectionWrapper: "_inputSectionWrapper_194hg_547",
2650
+ hidden: "_hidden_194hg_563",
2651
+ inputSection: "_inputSection_194hg_547",
2652
+ attachButton: "_attachButton_194hg_574",
2653
+ taskInput: "_taskInput_194hg_590",
2654
+ attachmentsBar: "_attachmentsBar_194hg_618",
2655
+ attachmentChip: "_attachmentChip_194hg_629",
2656
+ attachmentChipRemove: "_attachmentChipRemove_194hg_647",
2657
+ attachError: "_attachError_194hg_662",
2658
+ fileInput: "_fileInput_194hg_673"
2643
2659
  };
2644
2660
  //#endregion
2645
2661
  //#region src/ui/cards.ts
@@ -2670,6 +2686,17 @@ function createReflectionLines(reflection) {
2670
2686
  }
2671
2687
  //#endregion
2672
2688
  //#region src/ui/UI.ts
2689
+ /** 单个附件最大体积(5MB) */
2690
+ var MAX_ATTACHMENT_SIZE = 5242880;
2691
+ /** 单次任务最多允许携带的附件数量 */
2692
+ var MAX_ATTACHMENTS = 5;
2693
+ /** 支持的文本类附件扩展名(图片按 MIME 类型单独判断) */
2694
+ var TEXT_ATTACHMENT_EXTENSIONS = [
2695
+ ".txt",
2696
+ ".md",
2697
+ ".json",
2698
+ ".csv"
2699
+ ];
2673
2700
  /**
2674
2701
  * Agent 控制 UI
2675
2702
  *
@@ -2690,6 +2717,10 @@ var UI = class {
2690
2717
  #actionButton;
2691
2718
  #inputSection;
2692
2719
  #taskInput;
2720
+ #attachButton;
2721
+ #fileInput;
2722
+ #attachmentsBar;
2723
+ #attachError;
2693
2724
  #agent;
2694
2725
  #config;
2695
2726
  #isExpanded = false;
@@ -2699,6 +2730,7 @@ var UI = class {
2699
2730
  #userAnswerSignal = null;
2700
2731
  #userAnswerAbortHandler = null;
2701
2732
  #isWaitingForUserAnswer = false;
2733
+ #pendingAttachments = [];
2702
2734
  #headerUpdateTimer = null;
2703
2735
  #timers = /* @__PURE__ */ new Set();
2704
2736
  #pendingHeaderText = null;
@@ -2732,6 +2764,10 @@ var UI = class {
2732
2764
  this.#actionButton = this.#wrapper.querySelector(`.${UI_module_default.stopButton}`);
2733
2765
  this.#inputSection = this.#wrapper.querySelector(`.${UI_module_default.inputSectionWrapper}`);
2734
2766
  this.#taskInput = this.#wrapper.querySelector(`.${UI_module_default.taskInput}`);
2767
+ this.#attachButton = this.#wrapper.querySelector(`.${UI_module_default.attachButton}`);
2768
+ this.#fileInput = this.#wrapper.querySelector(`.${UI_module_default.fileInput}`);
2769
+ this.#attachmentsBar = this.#wrapper.querySelector(`.${UI_module_default.attachmentsBar}`);
2770
+ this.#attachError = this.#wrapper.querySelector(`.${UI_module_default.attachError}`);
2735
2771
  this.#agent.addEventListener("statuschange", this.#onStatusChange);
2736
2772
  this.#agent.addEventListener("historychange", this.#onHistoryChange);
2737
2773
  this.#agent.addEventListener("activity", this.#onActivity);
@@ -2930,12 +2966,20 @@ var UI = class {
2930
2966
  const input = this.#taskInput.value.trim();
2931
2967
  if (!input) return;
2932
2968
  this.#hideInputArea();
2933
- if (this.#isWaitingForUserAnswer) this.#handleUserAnswer(input);
2934
- else this.#agent.execute(input).catch((error) => {
2969
+ if (this.#isWaitingForUserAnswer) {
2970
+ this.#handleUserAnswer(input);
2971
+ return;
2972
+ }
2973
+ const attachments = this.#pendingAttachments;
2974
+ this.#pendingAttachments = [];
2975
+ this.#renderAttachments();
2976
+ this.#setAttachError("");
2977
+ const onExecuteError = (error) => {
2935
2978
  if (error?.name === "AbortError") return;
2936
2979
  console.error("[UI] Failed to execute task:", error);
2937
2980
  if (!this.#disposed) this.#showInputArea();
2938
- });
2981
+ };
2982
+ (attachments.length > 0 ? this.#agent.execute(input, attachments) : this.#agent.execute(input)).catch(onExecuteError);
2939
2983
  }
2940
2984
  /**
2941
2985
  * 处理用户回答
@@ -2953,6 +2997,8 @@ var UI = class {
2953
2997
  this.#taskInput.value = "";
2954
2998
  this.#taskInput.placeholder = placeholder || this.#i18n.t("ui.taskInput");
2955
2999
  this.#inputSection.classList.remove(UI_module_default.hidden);
3000
+ this.#attachButton.style.display = this.#isWaitingForUserAnswer ? "none" : "";
3001
+ this.#syncInputHeight();
2956
3002
  this.#setTimer(() => {
2957
3003
  this.#taskInput.focus();
2958
3004
  }, 100);
@@ -2962,6 +3008,84 @@ var UI = class {
2962
3008
  */
2963
3009
  #hideInputArea() {
2964
3010
  this.#inputSection.classList.add(UI_module_default.hidden);
3011
+ this.#inputSection.style.maxHeight = "0px";
3012
+ }
3013
+ /** 将输入区域外层容器的 max-height 同步为当前真实内容高度(含附件条、错误提示) */
3014
+ #syncInputHeight() {
3015
+ if (this.#inputSection.classList.contains(UI_module_default.hidden)) return;
3016
+ this.#inputSection.style.maxHeight = `${this.#inputSection.scrollHeight}px`;
3017
+ }
3018
+ /** 设置(或清空)附件校验错误提示,并同步输入区域高度 */
3019
+ #setAttachError(message) {
3020
+ this.#attachError.textContent = message;
3021
+ this.#syncInputHeight();
3022
+ }
3023
+ /** 判断文件是否为支持的附件类型:图片,或指定扩展名的文本文件 */
3024
+ #isSupportedAttachment(file) {
3025
+ if (file.type.startsWith("image/")) return true;
3026
+ const name = file.name.toLowerCase();
3027
+ return TEXT_ATTACHMENT_EXTENSIONS.some((ext) => name.endsWith(ext));
3028
+ }
3029
+ /** 将文件读取为 base64 data URL */
3030
+ #readFileAsDataUrl(file) {
3031
+ return new Promise((resolve, reject) => {
3032
+ const reader = new FileReader();
3033
+ reader.onload = () => resolve(reader.result);
3034
+ reader.onerror = () => reject(reader.error);
3035
+ reader.readAsDataURL(file);
3036
+ });
3037
+ }
3038
+ /**
3039
+ * 校验并读取用户选中的文件,追加为待提交附件。
3040
+ * 任一文件超出数量/大小/类型限制时,拒绝本次整批选择并提示错误。
3041
+ */
3042
+ async #handleFileSelection(files) {
3043
+ const incoming = Array.from(files);
3044
+ if (this.#pendingAttachments.length + incoming.length > MAX_ATTACHMENTS) {
3045
+ this.#setAttachError(this.#i18n.t("ui.attachTooMany", { max: MAX_ATTACHMENTS }));
3046
+ return;
3047
+ }
3048
+ for (const file of incoming) {
3049
+ if (file.size > MAX_ATTACHMENT_SIZE) {
3050
+ this.#setAttachError(this.#i18n.t("ui.attachTooLarge", {
3051
+ name: file.name,
3052
+ maxMB: MAX_ATTACHMENT_SIZE / 1048576
3053
+ }));
3054
+ return;
3055
+ }
3056
+ if (!this.#isSupportedAttachment(file)) {
3057
+ this.#setAttachError(this.#i18n.t("ui.attachUnsupportedType", { name: file.name }));
3058
+ return;
3059
+ }
3060
+ }
3061
+ this.#setAttachError("");
3062
+ const dataUrls = await Promise.all(incoming.map((file) => this.#readFileAsDataUrl(file)));
3063
+ incoming.forEach((file, index) => {
3064
+ this.#pendingAttachments.push({
3065
+ name: file.name,
3066
+ mimeType: file.type,
3067
+ dataUrl: dataUrls[index]
3068
+ });
3069
+ });
3070
+ this.#renderAttachments();
3071
+ }
3072
+ /** 重新渲染已选附件的预览条 */
3073
+ #renderAttachments() {
3074
+ this.#attachmentsBar.innerHTML = this.#pendingAttachments.map((attachment, index) => {
3075
+ const icon = attachment.mimeType.startsWith("image/") ? "🖼️" : "📄";
3076
+ return `
3077
+ <span class="${UI_module_default.attachmentChip}">
3078
+ <span>${icon} ${escapeHtml(truncate$1(attachment.name, 20))}</span>
3079
+ <button type="button" class="${UI_module_default.attachmentChipRemove}" data-remove-index="${index}">×</button>
3080
+ </span>
3081
+ `;
3082
+ }).join("");
3083
+ this.#syncInputHeight();
3084
+ }
3085
+ /** 移除一个已选附件 */
3086
+ #removeAttachment(index) {
3087
+ this.#pendingAttachments.splice(index, 1);
3088
+ this.#renderAttachments();
2965
3089
  }
2966
3090
  /**
2967
3091
  * 检查是否应显示输入区域
@@ -3010,7 +3134,21 @@ var UI = class {
3010
3134
  </div>
3011
3135
  </div>
3012
3136
  <div class="${UI_module_default.inputSectionWrapper} ${UI_module_default.hidden}">
3137
+ <div class="${UI_module_default.attachmentsBar}"></div>
3138
+ <div class="${UI_module_default.attachError}"></div>
3013
3139
  <div class="${UI_module_default.inputSection}">
3140
+ <button
3141
+ type="button"
3142
+ class="${UI_module_default.attachButton}"
3143
+ title="${this.#i18n.t("ui.attachButtonTitle")}"
3144
+ >📎</button>
3145
+ <input
3146
+ type="file"
3147
+ class="${UI_module_default.fileInput}"
3148
+ accept="image/*,${TEXT_ATTACHMENT_EXTENSIONS.join(",")}"
3149
+ multiple
3150
+ hidden
3151
+ />
3014
3152
  <textarea
3015
3153
  class="${UI_module_default.taskInput}"
3016
3154
  maxlength="${taskInputMaxLength}"
@@ -3048,6 +3186,19 @@ var UI = class {
3048
3186
  this.#inputSection.addEventListener("click", (e) => {
3049
3187
  e.stopPropagation();
3050
3188
  });
3189
+ this.#attachButton.addEventListener("click", () => {
3190
+ this.#fileInput.click();
3191
+ });
3192
+ this.#fileInput.addEventListener("change", () => {
3193
+ const files = this.#fileInput.files;
3194
+ if (files?.length) this.#handleFileSelection(files);
3195
+ this.#fileInput.value = "";
3196
+ });
3197
+ this.#attachmentsBar.addEventListener("click", (e) => {
3198
+ const removeButton = e.target.closest(`.${UI_module_default.attachmentChipRemove}`);
3199
+ if (!(removeButton instanceof HTMLElement)) return;
3200
+ this.#removeAttachment(Number(removeButton.dataset.removeIndex));
3201
+ });
3051
3202
  }
3052
3203
  /** 在展开/收起状态之间切换 */
3053
3204
  #toggle() {
@@ -3164,20 +3315,25 @@ var UI = class {
3164
3315
  this.#scrollToBottom();
3165
3316
  this.#syncHistoryHeight();
3166
3317
  }
3167
- /** 生成任务卡片 HTML */
3168
- #createTaskCard(task) {
3169
- return createCard({
3318
+ /** 生成任务卡片 HTML;若任务附带附件,附加一行文件名列表 */
3319
+ #createTaskCard(task, attachmentNames) {
3320
+ if (!attachmentNames?.length) return createCard({
3170
3321
  icon: "🎯",
3171
3322
  content: task,
3172
3323
  type: "input"
3173
3324
  });
3325
+ return createCard({
3326
+ icon: "🎯",
3327
+ content: [task, this.#i18n.t("ui.attachmentsLabel", { names: attachmentNames.join(", ") })],
3328
+ type: "input"
3329
+ });
3174
3330
  }
3175
3331
  /** 为一条历史事件生成卡片 */
3176
3332
  #createHistoryCards(event) {
3177
3333
  const cards = [];
3178
3334
  const meta = event.type === "step" && event.stepIndex !== void 0 ? this.#i18n.t("ui.step", { number: (event.stepIndex + 1).toString() }) : void 0;
3179
3335
  if (event.type === "task_start") {
3180
- if (event.task) cards.push(this.#createTaskCard(event.task));
3336
+ if (event.task) cards.push(this.#createTaskCard(event.task, event.attachmentNames));
3181
3337
  } else if (event.type === "step") {
3182
3338
  if (event.reflection) {
3183
3339
  const lines = createReflectionLines(event.reflection);
@@ -4082,6 +4238,8 @@ tools.set("execute_javascript", tool({
4082
4238
  }));
4083
4239
  //#endregion
4084
4240
  //#region src/agent/AgentRuntime.ts
4241
+ /** 文本类附件注入 prompt 时的最大字符数,避免超大文件把上下文撑爆 */
4242
+ var ATTACHMENT_TEXT_MAX_LENGTH = 2e4;
4085
4243
  /**
4086
4244
  * 用于浏览器自动化的 AI 智能体。
4087
4245
  *
@@ -4148,6 +4306,8 @@ var AgentRuntime = class extends EventTarget {
4148
4306
  #abortController = new AbortController();
4149
4307
  /** 本轮任务收集的观察消息(暂存,稍后统一写入历史) */
4150
4308
  #observations = [];
4309
+ /** 本次任务上传的附件;仅在任务第一步注入 LLM prompt,之后步骤不重复携带原文件以节省 token */
4310
+ #taskAttachments = [];
4151
4311
  /** 当前任务在 history 数组中的起始下标(用于跨任务保留历史时,把 prompt/循环检测限定在当前任务范围内) */
4152
4312
  #taskHistoryStart = 0;
4153
4313
  /** 当前一次运行完全结束时 resolve。由 `stop()` 等待。 */
@@ -4259,16 +4419,18 @@ var AgentRuntime = class extends EventTarget {
4259
4419
  * 外部错误(前置检查/配置/钩子)会直接抛出;
4260
4420
  * Agent 内部错误会被捕获并加入历史,同时返回失败结果
4261
4421
  */
4262
- async execute(task) {
4422
+ async execute(task, attachments = []) {
4263
4423
  if (this.disposed) throw new Error("PageAgent has been disposed. Create a new instance.");
4264
4424
  if (this.#status === "running") throw new Error("A task is already running.");
4265
4425
  if (!task) throw new Error("Task is required");
4266
4426
  this.task = task;
4267
4427
  this.taskId = uid();
4428
+ this.#taskAttachments = attachments;
4268
4429
  this.#taskHistoryStart = this.history.length;
4269
4430
  this.#emitHistoryChange({
4270
4431
  type: "task_start",
4271
- task
4432
+ task,
4433
+ ...attachments.length > 0 ? { attachmentNames: attachments.map((a) => a.name) } : {}
4272
4434
  });
4273
4435
  this.#observations = [];
4274
4436
  this.#states = {
@@ -4589,8 +4751,33 @@ var AgentRuntime = class extends EventTarget {
4589
4751
  prompt += pageContent + "\n";
4590
4752
  prompt += browserState.footer + "\n\n";
4591
4753
  prompt += "</browser_state>\n\n";
4754
+ if (stepCount === 0 && this.#taskAttachments.length > 0) return await this.#injectAttachments(prompt, this.#taskAttachments);
4592
4755
  return prompt;
4593
4756
  }
4757
+ /**
4758
+ * 把附件内容注入 prompt:图片转为 image_url 片段;文本类文件解码后截断拼进文字部分。
4759
+ * 仅当存在图片附件时才返回多模态数组,纯文本附件保持返回字符串(不影响不支持 vision 的模型)。
4760
+ */
4761
+ async #injectAttachments(prompt, attachments) {
4762
+ const textSegments = [prompt];
4763
+ const imageParts = [];
4764
+ for (const attachment of attachments) {
4765
+ if (attachment.mimeType.startsWith("image/")) {
4766
+ imageParts.push({
4767
+ type: "image_url",
4768
+ image_url: { url: attachment.dataUrl }
4769
+ });
4770
+ continue;
4771
+ }
4772
+ const text = await suppress(() => fetch(attachment.dataUrl).then((r) => r.text()));
4773
+ textSegments.push(`<uploaded_file name="${attachment.name}">\n${truncate(text ?? "", ATTACHMENT_TEXT_MAX_LENGTH)}\n</uploaded_file>`);
4774
+ }
4775
+ if (imageParts.length === 0) return textSegments.join("\n\n");
4776
+ return [{
4777
+ type: "text",
4778
+ text: textSegments.join("\n\n")
4779
+ }, ...imageParts];
4780
+ }
4594
4781
  /** 销毁 Agent:中止任务、释放浏览器控制器,并触发 dispose 事件(一次性) */
4595
4782
  dispose() {
4596
4783
  if (this.disposed) return;