@coolkiller007/my-page-agent 0.2.6 → 0.2.8

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
@@ -44,7 +44,7 @@ declare interface AgentConfig extends LLMConfig {
44
44
  maxSteps?: number;
45
45
  /**
46
46
  * 拼装 LLM 提示词时,最近保留完整详情的历史步数;更早的步骤会被压缩成一行。
47
- * @default 15
47
+ * @default 5
48
48
  */
49
49
  historyWindow?: number;
50
50
  /**
@@ -272,6 +272,9 @@ declare class AgentRuntime extends EventTarget {
272
272
  * 图片附件缓存(按附件名索引):所有出现过的图片附件在此登记原始 dataUrl,
273
273
  * 使原图在 prompt 里只出现一次之后,仍可被 `image_understand` 工具按名称"按需重传"重新查看,
274
274
  * 而不必每步都把原图重新塞进主对话历史(节省 token)。
275
+ * @note 跨任务持久保留(不随 `execute()` 清空):缓存本身是纯内存 Map,不进 prompt 就不占 token,
276
+ * 只有模型主动调用 `image_understand` 时才会产生一次性 token 成本。唯一真实开销是内存,
277
+ * 因此按 `MAX_CACHED_IMAGES` 做容量上限,超出淘汰最早插入的一张(见 `#cacheImageAttachment`)。
275
278
  * 非私有字段:需要被 `tools/index.ts` 中的 `image_understand` 工具从外部读取。
276
279
  */
277
280
  imageAttachmentCache: Map<string, Attachment>;
package/dist/index.js CHANGED
@@ -990,6 +990,53 @@ var dom_tree_default = (args = {
990
990
  return false;
991
991
  }
992
992
  /**
993
+ * 判断某元素(或其祖先)是否带有 data-browser-use-ignore / data-page-agent-ignore 标记。
994
+ * 这些是我们自己注入的悬浮 UI(agent 面板、模拟遮罩等),命中测试时应视为"透明"。
995
+ *
996
+ * @param {Element | null} el - 待检查元素。
997
+ * @returns {boolean} 是否应在遮挡检测中被忽略。
998
+ */
999
+ function isIgnoredForHitTest(el) {
1000
+ let current = el;
1001
+ while (current) {
1002
+ if (current.dataset && (current.dataset.browserUseIgnore === "true" || current.dataset.pageAgentIgnore === "true")) return true;
1003
+ current = current.parentElement;
1004
+ }
1005
+ return false;
1006
+ }
1007
+ /**
1008
+ * @edit 命中测试时跳过被忽略的悬浮 UI(agent 面板、模拟遮罩等)。
1009
+ * 之前只有 SimulatorMask 会在 DOM 提取期间临时把 pointerEvents 设为 none 来绕开自己,
1010
+ * 但 agent 自身的问答/历史面板(UI.ts)没有做同样处理 —— 当面板展开、在视觉上盖住
1011
+ * 弹窗等页面元素时,elementFromPoint 命中的是面板本身,导致弹窗被误判为"被遮挡"而从
1012
+ * 提交给 LLM 的可交互树中消失,造成"弹窗仍开着,agent 却认为已关闭"的错觉。
1013
+ * 这里统一在遮挡检测层面处理:命中被忽略元素时临时禁用其 pointer-events 再重新探测,
1014
+ * 探测完成后恢复,使得任何打了忽略标记的悬浮 UI 都不会影响真实页面元素的遮挡判断。
1015
+ *
1016
+ * @param {Document | ShadowRoot} doc - 用于命中测试的文档或 shadow root。
1017
+ * @param {number} x - 命中测试点的 x 坐标。
1018
+ * @param {number} y - 命中测试点的 y 坐标。
1019
+ * @returns {Element | null} 忽略悬浮 UI 后,实际命中的最上层元素。
1020
+ */
1021
+ function elementFromPointSkippingIgnored(doc, x, y) {
1022
+ /** @type {{ el: HTMLElement, prevValue: string }[]} */
1023
+ const restoreList = [];
1024
+ try {
1025
+ let topEl = doc.elementFromPoint(x, y);
1026
+ while (topEl instanceof HTMLElement && isIgnoredForHitTest(topEl)) {
1027
+ restoreList.push({
1028
+ el: topEl,
1029
+ prevValue: topEl.style.pointerEvents
1030
+ });
1031
+ topEl.style.pointerEvents = "none";
1032
+ topEl = doc.elementFromPoint(x, y);
1033
+ }
1034
+ return topEl;
1035
+ } finally {
1036
+ for (const { el, prevValue } of restoreList) el.style.pointerEvents = prevValue;
1037
+ }
1038
+ }
1039
+ /**
993
1040
  * Checks if an element is the topmost element at its position.
994
1041
  *
995
1042
  * @param {HTMLElement} element - The element to check.
@@ -1023,7 +1070,7 @@ var dom_tree_default = (args = {
1023
1070
  const centerX = rect.left + rect.width / 2;
1024
1071
  const centerY = rect.top + rect.height / 2;
1025
1072
  try {
1026
- const topEl = shadowRoot.elementFromPoint(centerX, centerY);
1073
+ const topEl = elementFromPointSkippingIgnored(shadowRoot, centerX, centerY);
1027
1074
  if (!topEl) return false;
1028
1075
  let current = topEl;
1029
1076
  while (current && current !== shadowRoot) {
@@ -1051,7 +1098,7 @@ var dom_tree_default = (args = {
1051
1098
  }
1052
1099
  ].some(({ x, y }) => {
1053
1100
  try {
1054
- const topEl = document.elementFromPoint(x, y);
1101
+ const topEl = elementFromPointSkippingIgnored(document, x, y);
1055
1102
  if (!topEl) return false;
1056
1103
  let current = topEl;
1057
1104
  while (current && current !== document.documentElement) {
@@ -2822,7 +2869,7 @@ var UI = class {
2822
2869
  if (status === "running") {
2823
2870
  this.show();
2824
2871
  this.#hideInputArea();
2825
- if (!this.#isExpanded) this.#expand();
2872
+ if (this.#isExpanded) this.#collapse();
2826
2873
  }
2827
2874
  if (status === "completed" || status === "error" || status === "stopped") {
2828
2875
  if (!this.#isExpanded) this.#expand();
@@ -4360,6 +4407,8 @@ var ATTACHMENT_IMAGE_MAX_DIMENSION = 1568;
4360
4407
  var ATTACHMENT_IMAGE_JPEG_QUALITY = .85;
4361
4408
  /** 单次注入里最多内联发送的图片张数,超出的仅登记缓存,需模型主动调用 image_understand 查看 */
4362
4409
  var MAX_INLINE_IMAGES = 4;
4410
+ /** 图片缓存跨任务最多保留的张数,超出时淘汰最早插入的一张(防止长会话内存无限增长;缓存本身不占 prompt token) */
4411
+ var MAX_CACHED_IMAGES = 20;
4363
4412
  /** 历史任务 done 文本注入 prompt 时的最大字符数 */
4364
4413
  var PREVIOUS_TASK_TEXT_MAX_LENGTH = 300;
4365
4414
  /**
@@ -4440,6 +4489,9 @@ var AgentRuntime = class extends EventTarget {
4440
4489
  * 图片附件缓存(按附件名索引):所有出现过的图片附件在此登记原始 dataUrl,
4441
4490
  * 使原图在 prompt 里只出现一次之后,仍可被 `image_understand` 工具按名称"按需重传"重新查看,
4442
4491
  * 而不必每步都把原图重新塞进主对话历史(节省 token)。
4492
+ * @note 跨任务持久保留(不随 `execute()` 清空):缓存本身是纯内存 Map,不进 prompt 就不占 token,
4493
+ * 只有模型主动调用 `image_understand` 时才会产生一次性 token 成本。唯一真实开销是内存,
4494
+ * 因此按 `MAX_CACHED_IMAGES` 做容量上限,超出淘汰最早插入的一张(见 `#cacheImageAttachment`)。
4443
4495
  * 非私有字段:需要被 `tools/index.ts` 中的 `image_understand` 工具从外部读取。
4444
4496
  */
4445
4497
  imageAttachmentCache = /* @__PURE__ */ new Map();
@@ -4562,7 +4614,6 @@ var AgentRuntime = class extends EventTarget {
4562
4614
  this.taskId = uid();
4563
4615
  this.#taskAttachments = attachments;
4564
4616
  this.pendingAnswerAttachments = [];
4565
- this.imageAttachmentCache = /* @__PURE__ */ new Map();
4566
4617
  this.#taskHistoryStart = this.history.length;
4567
4618
  this.#emitHistoryChange({
4568
4619
  type: "task_start",
@@ -4815,7 +4866,8 @@ var AgentRuntime = class extends EventTarget {
4815
4866
  /**
4816
4867
  * 汇总当前任务之前、同一 AgentRuntime 实例已完成过的历史任务,生成一段纯文字摘要。
4817
4868
  * 只带文字结论(任务描述 + done 文本 + 涉及的附件文件名),不携带图片原始数据 ——
4818
- * 图片附件与 `imageAttachmentCache` 一样按任务隔离,跨任务需要复看时应提示用户重新上传。
4869
+ * 图片附件是否仍可复看取决于是否还在 `imageAttachmentCache`(跨任务持久、有容量上限)里:
4870
+ * 还在则提示模型改用 `image_understand` 复看;已被淘汰(或本就是非图片附件)则提示用户重新上传。
4819
4871
  * 只取最近 `PREVIOUS_TASKS_SUMMARY_LIMIT` 个任务,避免任务数增多后 prompt 无限增长。
4820
4872
  */
4821
4873
  #buildPreviousTasksSummary() {
@@ -4836,12 +4888,25 @@ var AgentRuntime = class extends EventTarget {
4836
4888
  const recent = summaries.slice(-5);
4837
4889
  if (recent.length === 0) return "";
4838
4890
  return `<previous_tasks_summary>\n${recent.map((s) => {
4839
- const attachmentNote = s.attachmentNames?.length ? ` [attachments uploaded then: ${s.attachmentNames.join(", ")} — NOT available in this task, ask the user to re-upload if you need to see them again]` : "";
4891
+ const attachmentNote = s.attachmentNames?.length ? ` ${this.#describeAttachmentAvailability(s.attachmentNames)}` : "";
4840
4892
  const result = s.result ? truncate(s.result, PREVIOUS_TASK_TEXT_MAX_LENGTH) : "(no result recorded — task may have been interrupted)";
4841
4893
  return `- Task: "${s.task}"${attachmentNote}\n Result: ${result}`;
4842
4894
  }).join("\n")}\n</previous_tasks_summary>\n\n`;
4843
4895
  }
4844
4896
  /**
4897
+ * 按附件名分成"仍在 `imageAttachmentCache` 里、可 `image_understand` 复看"与
4898
+ * "已不可用、需用户重新上传"两组,拼成一句注入 prompt 的提示。
4899
+ */
4900
+ #describeAttachmentAvailability(attachmentNames) {
4901
+ const viewable = [];
4902
+ const unavailable = [];
4903
+ for (const name of attachmentNames) (this.imageAttachmentCache.has(name) ? viewable : unavailable).push(name);
4904
+ const parts = [];
4905
+ if (viewable.length > 0) parts.push(`still viewable via image_understand: ${viewable.join(", ")}`);
4906
+ if (unavailable.length > 0) parts.push(`NOT available in this task, ask the user to re-upload if you need to see them again: ${unavailable.join(", ")}`);
4907
+ return `[attachments uploaded then — ${parts.join("; ")}]`;
4908
+ }
4909
+ /**
4845
4910
  * 在每一步之前生成系统观察
4846
4911
  * @todo 控制台错误
4847
4912
  */
@@ -4936,6 +5001,17 @@ var AgentRuntime = class extends EventTarget {
4936
5001
  return prompt;
4937
5002
  }
4938
5003
  /**
5004
+ * 把图片附件登记进 `imageAttachmentCache`;超出 `MAX_CACHED_IMAGES` 容量时淘汰最早插入的一张。
5005
+ * 缓存跨任务持久,容量上限只为控内存,不影响 token(未被 `image_understand` 读取的缓存项不进 prompt)。
5006
+ */
5007
+ #cacheImageAttachment(attachment) {
5008
+ if (!this.imageAttachmentCache.has(attachment.name) && this.imageAttachmentCache.size >= MAX_CACHED_IMAGES) {
5009
+ const oldestName = this.imageAttachmentCache.keys().next().value;
5010
+ if (oldestName !== void 0) this.imageAttachmentCache.delete(oldestName);
5011
+ }
5012
+ this.imageAttachmentCache.set(attachment.name, attachment);
5013
+ }
5014
+ /**
4939
5015
  * 把附件内容注入 prompt:图片转为 image_url 片段;文本类文件解码后截断拼进文字部分。
4940
5016
  * 仅当存在图片附件时才返回多模态数组,纯文本附件保持返回字符串(不影响不支持 vision 的模型)。
4941
5017
  * 图片附件会先登记进 `imageAttachmentCache`(存原图,供 `image_understand` 复看细节用),
@@ -4949,7 +5025,7 @@ var AgentRuntime = class extends EventTarget {
4949
5025
  let inlinedImageCount = 0;
4950
5026
  for (const attachment of attachments) {
4951
5027
  if (attachment.mimeType.startsWith("image/")) {
4952
- this.imageAttachmentCache.set(attachment.name, attachment);
5028
+ this.#cacheImageAttachment(attachment);
4953
5029
  if (inlinedImageCount >= MAX_INLINE_IMAGES) {
4954
5030
  textSegments.push(`[Image attachment "${attachment.name}" was NOT shown inline (too many images in this batch). Call image_understand with attachment_name="${attachment.name}" if you need to see it.]`);
4955
5031
  continue;