@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 +4 -1
- package/dist/index.js +83 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
-
*
|
|
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 ? `
|
|
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
|
|
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;
|