agentpage 0.0.4 → 0.0.5

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/core/tool-registry.ts","../src/core/types.ts","../src/core/agent-loop/index.ts","../src/web/ref-store.ts","../src/web/page-info-tool.ts","../src/web/dom-tool.ts","../src/web/navigate-tool.ts","../src/web/wait-tool.ts","../src/web/evaluate-tool.ts","../src/web/messaging.ts","../src/web/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/core/tool-registry.ts","../src/core/types.ts","../src/core/agent-loop/index.ts","../src/web/ref-store.ts","../src/web/page-info-tool.ts","../src/web/dom-tool.ts","../src/web/navigate-tool.ts","../src/web/wait-tool.ts","../src/web/evaluate-tool.ts","../src/web/messaging.ts","../src/web/index.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -7,7 +7,7 @@ import { Type } from "@sinclair/typebox";
7
7
  * 统一集中在该文件,避免在主循环中散落“魔法数字”。
8
8
  */
9
9
  const DEFAULT_MAX_ROUNDS = 10;
10
- const DEFAULT_RECOVERY_WAIT_MS = 1e3;
10
+ const DEFAULT_RECOVERY_WAIT_MS = 300;
11
11
  const DEFAULT_ACTION_RECOVERY_ROUNDS = 5;
12
12
 
13
13
  //#endregion
@@ -810,10 +810,13 @@ function buildSystemPrompt(params = {}) {
810
810
  * 替代 Playwright 的 click/fill/type 等操作,直接在页面上下文中执行。
811
811
  * 运行环境:浏览器 Content Script。
812
812
  *
813
- * 支持 8 种动作:
813
+ * 支持 11 种动作:
814
814
  * click — 点击元素
815
815
  * fill — 填写输入框(清空后设值)
816
816
  * type — 逐字符模拟键入
817
+ * focus — 聚焦元素
818
+ * hover — 鼠标悬停(触发 mouseenter/mouseover)
819
+ * press — 按下键盘按键(Enter/Escape/Tab/ArrowDown 等)
817
820
  * get_text — 获取元素文本内容
818
821
  * get_attr — 获取元素属性值
819
822
  * set_attr — 设置元素属性
@@ -924,13 +927,14 @@ function createDomTool() {
924
927
  name: "dom",
925
928
  description: [
926
929
  "Perform DOM operations on the current page.",
927
- "Actions: click, fill, type, get_text, get_attr, set_attr, add_class, remove_class.",
930
+ "Actions: click, fill, type, focus, hover, press, get_text, get_attr, set_attr, add_class, remove_class.",
928
931
  "Use the hash ID from DOM snapshot (e.g. #a1b2c) as selector."
929
932
  ].join(" "),
930
933
  schema: Type.Object({
931
- action: Type.String({ description: "DOM action: click | fill | type | get_text | get_attr | set_attr | add_class | remove_class" }),
934
+ action: Type.String({ description: "DOM action: click | fill | type | focus | hover | press | get_text | get_attr | set_attr | add_class | remove_class" }),
932
935
  selector: Type.String({ description: "Element ref ID from snapshot (e.g. #r0, #r5) or CSS selector" }),
933
936
  value: Type.Optional(Type.String({ description: "Value for fill/type/set_attr actions" })),
937
+ key: Type.Optional(Type.String({ description: "Key name for press action (e.g. Enter, Escape, Tab, ArrowDown, ArrowUp, Backspace, Delete, Space)" })),
934
938
  attribute: Type.Optional(Type.String({ description: "Attribute name for get_attr/set_attr actions" })),
935
939
  className: Type.Optional(Type.String({ description: "CSS class name for add_class/remove_class" })),
936
940
  waitMs: Type.Optional(Type.Number({ description: "Optional wait timeout in ms before action (default: 1000). Use 0 to disable waiting." })),
@@ -986,6 +990,39 @@ function createDomTool() {
986
990
  el.click();
987
991
  } else el.dispatchEvent(new MouseEvent("click", { bubbles: true }));
988
992
  return { content: `已点击 ${describeElement(el)}` };
993
+ case "focus":
994
+ if (el instanceof HTMLElement) el.focus();
995
+ else el.dispatchEvent(new FocusEvent("focus", { bubbles: true }));
996
+ return { content: `已聚焦 ${describeElement(el)}` };
997
+ case "hover":
998
+ el.dispatchEvent(new MouseEvent("mouseenter", {
999
+ bubbles: false,
1000
+ cancelable: true
1001
+ }));
1002
+ el.dispatchEvent(new MouseEvent("mouseover", {
1003
+ bubbles: true,
1004
+ cancelable: true
1005
+ }));
1006
+ el.dispatchEvent(new MouseEvent("mousemove", {
1007
+ bubbles: true,
1008
+ cancelable: true
1009
+ }));
1010
+ return { content: `已悬停 ${describeElement(el)}` };
1011
+ case "press": {
1012
+ const key = params.key || params.value;
1013
+ if (!key) return { content: "缺少 key 参数(如 Enter, Escape, Tab)" };
1014
+ if (el instanceof HTMLElement) el.focus();
1015
+ const eventInit = {
1016
+ key,
1017
+ code: key,
1018
+ bubbles: true,
1019
+ cancelable: true
1020
+ };
1021
+ el.dispatchEvent(new KeyboardEvent("keydown", eventInit));
1022
+ el.dispatchEvent(new KeyboardEvent("keypress", eventInit));
1023
+ el.dispatchEvent(new KeyboardEvent("keyup", eventInit));
1024
+ return { content: `已在 ${describeElement(el)} 上按下 ${key}` };
1025
+ }
989
1026
  case "fill": {
990
1027
  const value = params.value;
991
1028
  if (value === void 0) return { content: "缺少 value 参数" };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["sleep","sleep","convertMessages"],"sources":["../src/core/agent-loop/constants.ts","../src/core/agent-loop/helpers.ts","../src/core/agent-loop/index.ts","../src/core/ai-client/constants.ts","../src/core/ai-client/custom.ts","../src/core/ai-client/openai.ts","../src/core/ai-client/anthropic.ts","../src/core/ai-client/index.ts","../src/core/tool-registry.ts","../src/core/system-prompt.ts","../src/web/dom-tool.ts","../src/web/page-info-tool.ts","../src/web/navigate-tool.ts","../src/web/wait-tool.ts","../src/web/evaluate-tool.ts","../src/web/ref-store.ts","../src/web/messaging.ts","../src/web/index.ts"],"sourcesContent":["/**\n * Agent Loop 默认配置常量。\n *\n * 统一集中在该文件,避免在主循环中散落“魔法数字”。\n */\nexport const DEFAULT_MAX_ROUNDS = 10;\nexport const DEFAULT_RECOVERY_WAIT_MS = 1000;\nexport const DEFAULT_ACTION_RECOVERY_ROUNDS = 5;\n","/**\n * Agent Loop 辅助函数集合。\n *\n * 该文件只放“纯辅助逻辑”:格式化、判定、上下文读取、等待等,\n * 让 `agent-loop.ts` 专注于流程编排。\n */\nimport type { ToolCallResult } from \"../tool-registry.js\";\nimport { ToolRegistry } from \"../tool-registry.js\";\nimport {\n DEFAULT_RECOVERY_WAIT_MS,\n} from \"./constants.js\";\n\n/** 单次工具执行轨迹条目(用于恢复提示和调试展示)。 */\nexport type ToolTraceEntry = {\n round: number;\n name: string;\n input: unknown;\n result: ToolCallResult;\n marker?: string;\n};\n\n/** 异步睡眠,确保恢复重试按顺序串行执行。 */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** 将工具返回内容统一转为字符串,便于拼接进消息。 */\nexport function toContentString(content: ToolCallResult[\"content\"]): string {\n return typeof content === \"string\" ? content : JSON.stringify(content, null, 2);\n}\n\n/** 判定工具失败是否属于“元素不存在”,用于触发快照恢复。 */\nexport function isElementNotFoundResult(result: ToolCallResult): boolean {\n const details = result.details;\n if (details && typeof details === \"object\") {\n const code = (details as { code?: unknown }).code;\n if (code === \"ELEMENT_NOT_FOUND\") return true;\n }\n\n const content = toContentString(result.content);\n return content.includes(\"未找到\") && content.includes(\"元素\");\n}\n\n/** 为同一动作构造稳定 key,用于统计恢复重试次数。 */\nexport function buildToolCallKey(name: string, input: unknown): string {\n return `${name}:${JSON.stringify(input)}`;\n}\n\n/**\n * 解析恢复等待时长:\n * - 优先 `waitMs`\n * - 其次 `waitSeconds`\n * - 最后回退默认值\n */\nexport function resolveRecoveryWaitMs(input: unknown): number {\n if (!input || typeof input !== \"object\") return DEFAULT_RECOVERY_WAIT_MS;\n\n const params = input as Record<string, unknown>;\n const waitMs = params.waitMs;\n if (typeof waitMs === \"number\" && Number.isFinite(waitMs)) {\n return Math.max(0, Math.floor(waitMs));\n }\n\n const waitSeconds = params.waitSeconds;\n if (typeof waitSeconds === \"number\" && Number.isFinite(waitSeconds)) {\n return Math.max(0, Math.floor(waitSeconds * 1000));\n }\n\n return DEFAULT_RECOVERY_WAIT_MS;\n}\n\n/** 将工具输入压缩成简短文本,用于轨迹展示。 */\nfunction formatToolInputBrief(input: unknown): string {\n if (!input || typeof input !== \"object\") return \"\";\n\n const params = input as Record<string, unknown>;\n const parts: string[] = [];\n\n for (const key of [\"action\", \"selector\", \"waitMs\", \"waitSeconds\", \"url\", \"text\"]) {\n const value = params[key];\n if (value === undefined || value === null) continue;\n if (typeof value === \"string\") {\n parts.push(`${key}=${JSON.stringify(value).slice(0, 80)}`);\n } else if (typeof value === \"number\" || typeof value === \"boolean\") {\n parts.push(`${key}=${String(value)}`);\n }\n }\n\n if (parts.length === 0) return \"\";\n return ` (${parts.join(\", \")})`;\n}\n\n/**\n * 将完整轨迹格式化为可读文本。\n * 支持附加“当前步骤”用于在恢复提示中高亮失败动作。\n */\nexport function buildToolTrace(\n trace: ToolTraceEntry[],\n current?: {\n round: number;\n name: string;\n input: unknown;\n result?: ToolCallResult;\n marker?: string;\n },\n): string {\n const lines = trace.map((entry, index) => {\n const code =\n entry.result.details && typeof entry.result.details === \"object\"\n ? (entry.result.details as { code?: unknown }).code\n : undefined;\n const codeText = typeof code === \"string\" ? ` [${code}]` : \"\";\n const marker = entry.marker ? ` ${entry.marker}` : \"\";\n return `${index + 1}. [round ${entry.round}] ${entry.name}${formatToolInputBrief(entry.input)}${codeText}${marker}`;\n });\n\n if (current) {\n const code =\n current.result?.details && typeof current.result.details === \"object\"\n ? (current.result.details as { code?: unknown }).code\n : undefined;\n const codeText = typeof code === \"string\" ? ` [${code}]` : \"\";\n const marker = current.marker ? ` ${current.marker}` : \"\";\n lines.push(\n `${lines.length + 1}. [round ${current.round}] ${current.name}${formatToolInputBrief(current.input)}${codeText}${marker}`,\n );\n }\n\n return lines.length > 0 ? lines.join(\"\\n\") : \"(暂无工具执行记录)\";\n}\n\n/** 从工具参数中读取 action。 */\nexport function getToolAction(input: unknown): string | undefined {\n if (!input || typeof input !== \"object\") return undefined;\n const action = (input as Record<string, unknown>).action;\n return typeof action === \"string\" ? action : undefined;\n}\n\n/** 判定工具结果是否标记 error。 */\nexport function hasToolError(result: ToolCallResult): boolean {\n return result.details && typeof result.details === \"object\"\n ? Boolean((result.details as { error?: unknown }).error)\n : false;\n}\n\n/** 读取当前页面 URL(通过 page_info 工具)。 */\nexport async function readPageUrl(\n registry: ToolRegistry,\n): Promise<string | undefined> {\n const result = await registry.dispatch(\"page_info\", { action: \"get_url\" });\n return typeof result.content === \"string\" ? result.content : undefined;\n}\n\n/** 读取当前页面快照(通过 page_info 工具)。 */\nexport async function readPageSnapshot(\n registry: ToolRegistry,\n maxDepth = 8,\n): Promise<string> {\n const result = await registry.dispatch(\"page_info\", {\n action: \"snapshot\",\n maxDepth,\n });\n return toContentString(result.content);\n}\n","/**\n * Agent Loop — 环境无关的 AI 决策循环。\n *\n * 核心 Tool-Use Loop,纯 TypeScript 实现:\n *\n * 消息 → AI 思考 → 需要工具?──是──→ 执行工具 → 反馈结果 → 继续思考\n * │\n * 否\n * ↓\n * 返回最终回复\n *\n * 使用方:WebAgent.chat() 调用\n *\n * 依赖关系(全部环境无关):\n * - types.ts → 类型定义(import type,零运行时)\n * - tool-registry.ts → ToolRegistry 实例(注入式,非全局)\n */\nimport type { AIClient, AIMessage } from \"../types.js\";\nimport { ToolRegistry, type ToolCallResult } from \"../tool-registry.js\";\nimport {\n DEFAULT_ACTION_RECOVERY_ROUNDS,\n DEFAULT_MAX_ROUNDS,\n} from \"./constants.js\";\nimport {\n buildToolCallKey,\n buildToolTrace,\n getToolAction,\n hasToolError,\n isElementNotFoundResult,\n readPageSnapshot,\n readPageUrl,\n resolveRecoveryWaitMs,\n sleep,\n toContentString,\n type ToolTraceEntry,\n} from \"./helpers.js\";\n\n// ─── 回调接口 ───\n\n/** 工具调用事件回调 — 用于 UI 层实时展示 Agent 进度 */\nexport type AgentLoopCallbacks = {\n /** AI 返回文本回复时触发 */\n onText?: (text: string) => void;\n /** AI 请求调用工具时触发(执行前) */\n onToolCall?: (name: string, input: unknown) => void;\n /** 工具执行完成时触发 */\n onToolResult?: (name: string, result: ToolCallResult) => void;\n /** 每轮循环开始时触发(round 从 0 开始) */\n onRound?: (round: number) => void;\n /**\n * 恢复快照生成前触发(页面 URL 变化或元素定位失败时)。\n *\n * 用于 WebAgent 重置 RefStore(清空旧的 hash ID → Element 映射,\n * 用新 URL 重新生成确定性 hash),确保恢复快照中的 ID 有效。\n *\n * @param newUrl 当前页面 URL(URL 变化时传入;元素定位失败时为 undefined)\n */\n onBeforeRecoverySnapshot?: (newUrl?: string) => void;\n};\n\n// ─── 参数与结果 ───\n\nexport type AgentLoopParams = {\n /** AI 客户端实例(基于 fetch 的客户端) */\n client: AIClient;\n /** 工具注册表实例(由调用方创建并注册好工具) */\n registry: ToolRegistry;\n /** 系统提示词(由调用方构建,适配各自环境) */\n systemPrompt: string;\n /** 用户消息 */\n message: string;\n /** 历史对话消息(用于多轮记忆,按时间顺序排列) */\n history?: AIMessage[];\n /** 干运行模式:打印工具调用但不执行 */\n dryRun?: boolean;\n /** 最大工具调用轮次(默认 10) */\n maxRounds?: number;\n /** 事件回调 */\n callbacks?: AgentLoopCallbacks;\n};\n\nexport type AgentLoopResult = {\n /** AI 的最终文本回复 */\n reply: string;\n /** 所有工具调用记录 */\n toolCalls: Array<{ name: string; input: unknown; result: ToolCallResult }>;\n /** 本轮完整对话消息(含历史 + 本轮,用于多轮记忆累积) */\n messages: AIMessage[];\n};\n\ntype PageContextState = {\n currentUrl?: string;\n latestSnapshot?: string;\n needsSnapshotBeforeDom: boolean;\n};\n\n/**\n * 执行 Agent 决策循环(环境无关)。\n *\n * 完整流程:\n * 1. 获取已注册的工具列表\n * 2. 循环:发消息给 AI → 检查是否返回 tool_call → 执行 → 反馈 → 继续\n * 3. AI 不再调用工具时,返回最终回复\n */\nexport async function executeAgentLoop(\n params: AgentLoopParams,\n): Promise<AgentLoopResult> {\n const {\n client,\n registry,\n systemPrompt,\n message,\n history,\n dryRun = false,\n maxRounds = DEFAULT_MAX_ROUNDS,\n callbacks,\n } = params;\n\n const tools = registry.getDefinitions();\n // 将历史消息(如有)放在当前用户消息之前,实现多轮记忆\n const messages: AIMessage[] = [\n ...(history ?? []),\n { role: \"user\", content: message },\n ];\n const allToolCalls: AgentLoopResult[\"toolCalls\"] = [];\n const fullToolTrace: ToolTraceEntry[] = [];\n const actionRecoveryAttempts = new Map<string, number>();\n const pageContext: PageContextState = {\n needsSnapshotBeforeDom: false,\n };\n let finalReply = \"\";\n\n for (let round = 0; round < maxRounds; round++) {\n callbacks?.onRound?.(round);\n\n // 调用 AI(发送系统提示 + 对话历史 + 可用工具列表)\n const response = await client.chat({ systemPrompt, messages, tools });\n\n // 没有工具调用 → 循环结束,拿到最终回复\n if (!response.toolCalls || response.toolCalls.length === 0) {\n finalReply = response.text ?? \"\";\n if (finalReply) callbacks?.onText?.(finalReply);\n break;\n }\n\n // 有文本伴随工具调用 → 先通知\n if (response.text) callbacks?.onText?.(response.text);\n\n // ─── Dry-run 模式 ───\n if (dryRun) {\n finalReply = response.text ? response.text + \"\\n\\n\" : \"\";\n finalReply += \"🔧 AI 请求调用以下工具(dry-run 模式,未执行):\\n\";\n for (const tc of response.toolCalls) {\n callbacks?.onToolCall?.(tc.name, tc.input);\n finalReply += `\\n┌─ 工具: ${tc.name}\\n`;\n finalReply += `│ ID: ${tc.id}\\n`;\n finalReply += `│ 参数:\\n`;\n const inputStr = JSON.stringify(tc.input, null, 2);\n for (const line of inputStr.split(\"\\n\")) {\n finalReply += `│ ${line}\\n`;\n }\n finalReply += `└────────────────────\\n`;\n }\n break;\n }\n\n // ─── 执行工具调用 ───\n const toolResults: Array<{ toolCallId: string; result: string }> = [];\n\n for (const tc of response.toolCalls) {\n callbacks?.onToolCall?.(tc.name, tc.input);\n\n const latestUrl = await readPageUrl(registry);\n if (latestUrl) {\n if (!pageContext.currentUrl) {\n pageContext.currentUrl = latestUrl;\n } else if (latestUrl !== pageContext.currentUrl) {\n pageContext.currentUrl = latestUrl;\n pageContext.needsSnapshotBeforeDom = true;\n }\n }\n\n if (tc.name === \"dom\" && pageContext.needsSnapshotBeforeDom) {\n // 重置 RefStore:清空旧映射 + 更新 URL 命名空间,确保新快照 ID 有效\n callbacks?.onBeforeRecoverySnapshot?.(pageContext.currentUrl);\n const snapshotText = await readPageSnapshot(registry, 8);\n pageContext.latestSnapshot = snapshotText;\n pageContext.needsSnapshotBeforeDom = false;\n\n const result: ToolCallResult = {\n content: [\n `检测到页面 URL 变化:${pageContext.currentUrl ?? \"(未知)\"}`,\n \"已在执行 DOM 操作前生成最新快照,请基于该快照重新定位目标元素后重试当前工具调用。\",\n \"\",\n \"本次对话任务完整工具轨迹:\",\n buildToolTrace(fullToolTrace, {\n round,\n name: tc.name,\n input: tc.input,\n marker: \"[URL变化待重定位]\",\n }),\n \"\",\n \"最新页面快照:\",\n snapshotText,\n ].join(\"\\n\"),\n details: {\n error: true,\n code: \"URL_CHANGED_REQUIRE_NEW_SNAPSHOT\",\n url: pageContext.currentUrl,\n },\n };\n\n allToolCalls.push({ name: tc.name, input: tc.input, result });\n fullToolTrace.push({\n round,\n name: tc.name,\n input: tc.input,\n result,\n marker: \"[URL变化待重定位]\",\n });\n callbacks?.onToolResult?.(tc.name, result);\n toolResults.push({\n toolCallId: tc.id,\n result: toContentString(result.content),\n });\n continue;\n }\n\n let result = await registry.dispatch(tc.name, tc.input);\n\n if (tc.name === \"dom\" && isElementNotFoundResult(result)) {\n const key = buildToolCallKey(tc.name, tc.input);\n const attempts = (actionRecoveryAttempts.get(key) ?? 0) + 1;\n actionRecoveryAttempts.set(key, attempts);\n const recoveryWaitMs = resolveRecoveryWaitMs(tc.input);\n\n if (attempts <= DEFAULT_ACTION_RECOVERY_ROUNDS) {\n await sleep(recoveryWaitMs);\n\n // 重置 RefStore:清空可能失效的映射,确保恢复快照 ID 有效\n callbacks?.onBeforeRecoverySnapshot?.();\n const snapshotText = await readPageSnapshot(registry, 8);\n pageContext.latestSnapshot = snapshotText;\n const originalError = toContentString(result.content);\n const fullTrace = buildToolTrace(fullToolTrace, {\n round,\n name: tc.name,\n input: tc.input,\n result,\n marker: \"[当前失败]\",\n });\n\n result = {\n content: [\n originalError,\n \"\",\n `自动恢复 ${attempts}/${DEFAULT_ACTION_RECOVERY_ROUNDS}:等待 ${recoveryWaitMs}ms 后重新获取页面快照。`,\n \"本次对话任务完整工具轨迹(含本次失败):\",\n fullTrace,\n \"请根据下方最新快照,重新定位本次操作目标元素并再次调用工具。\",\n \"\",\n \"最新页面快照:\",\n snapshotText,\n ].join(\"\\n\"),\n details: {\n error: true,\n code: \"ELEMENT_NOT_FOUND_RECOVERY\",\n recoveryAttempt: attempts,\n recoveryMaxRounds: DEFAULT_ACTION_RECOVERY_ROUNDS,\n waitMs: recoveryWaitMs,\n },\n };\n } else {\n const originalError = toContentString(result.content);\n const fullTrace = buildToolTrace(fullToolTrace, {\n round,\n name: tc.name,\n input: tc.input,\n result,\n marker: \"[超过恢复上限]\",\n });\n result = {\n content: [\n originalError,\n \"\",\n `已达到最大自动恢复次数(${DEFAULT_ACTION_RECOVERY_ROUNDS})。请根据当前页面状态调整操作目标后重试。`,\n \"本次对话任务完整工具轨迹:\",\n fullTrace,\n ].join(\"\\n\"),\n details: {\n error: true,\n code: \"ELEMENT_NOT_FOUND_MAX_RECOVERY_REACHED\",\n recoveryAttempt: attempts,\n recoveryMaxRounds: DEFAULT_ACTION_RECOVERY_ROUNDS,\n waitMs: recoveryWaitMs,\n },\n };\n }\n }\n\n allToolCalls.push({ name: tc.name, input: tc.input, result });\n fullToolTrace.push({ round, name: tc.name, input: tc.input, result });\n\n if (tc.name === \"navigate\") {\n const action = getToolAction(tc.input);\n if (\n action === \"goto\" ||\n action === \"back\" ||\n action === \"forward\" ||\n action === \"reload\"\n ) {\n if (!hasToolError(result)) {\n pageContext.needsSnapshotBeforeDom = true;\n }\n }\n }\n\n callbacks?.onToolResult?.(tc.name, result);\n\n toolResults.push({\n toolCallId: tc.id,\n result:\n typeof result.content === \"string\"\n ? result.content\n : JSON.stringify(result.content),\n });\n }\n\n // 将 AI 回复(含 tool_call)和工具结果追加到对话历史\n messages.push({\n role: \"assistant\",\n content: response.text ?? \"\",\n toolCalls: response.toolCalls,\n });\n messages.push({\n role: \"tool\",\n content: toolResults,\n });\n // → 回到循环顶部,AI 根据工具结果继续思考\n }\n\n // 如果有最终回复但尚未作为 assistant 消息加入历史,补充进去\n if (finalReply) {\n messages.push({ role: \"assistant\", content: finalReply });\n }\n\n return { reply: finalReply, toolCalls: allToolCalls, messages };\n}\n","/**\n * AI Client 常量与共享工具函数。\n *\n * 集中管理各 Provider 的端点映射、Schema 清理等通用逻辑。\n * 被 openai.ts / anthropic.ts / 主入口 ai-client.ts 共同依赖。\n */\nimport type { AIClientConfig } from \"./index.js\";\n\n// ─── Provider 端点映射 ───\n\n/**\n * 各 Provider 的默认 API 端点。\n *\n * - openai → OpenAI 官方 API\n * - copilot → GitHub Models API(使用 OpenAI 兼容格式)\n * - anthropic → Anthropic Messages API\n */\nexport const PROVIDER_ENDPOINTS: Record<string, string> = {\n openai: \"https://api.openai.com/v1\",\n copilot: \"https://models.inference.ai.azure.com\",\n anthropic: \"https://api.anthropic.com\",\n};\n\n// ─── 共享工具函数 ───\n\n/**\n * 校验 provider 是否受支持。\n *\n * @throws 不支持的 provider 抛出 Error,附带支持列表\n */\nexport function validateProvider(provider: string): void {\n if (!PROVIDER_ENDPOINTS[provider]) {\n const supported = Object.keys(PROVIDER_ENDPOINTS).join(\", \");\n throw new Error(\n `Unknown AI provider: ${provider}. Supported: ${supported}`,\n );\n }\n}\n\n/**\n * 解析 provider 对应的 API 基础 URL。\n *\n * 优先使用用户自定义的 baseURL(如本地 Ollama),\n * 其次使用 PROVIDER_ENDPOINTS 中的默认值。\n */\nexport function resolveBaseURL(config: AIClientConfig): string {\n return config.baseURL ?? PROVIDER_ENDPOINTS[config.provider] ?? \"\";\n}\n\n/**\n * 清理 TypeBox Schema — 去除 Symbol 等不可序列化的属性。\n *\n * TypeBox 的 Type.Object() 产物包含 Symbol key(如 [Kind]、[Hint]),\n * 这些 Symbol 在 JSON.stringify 时会被忽略,但某些 AI API 端点\n * 对 JSON Schema 做严格校验时可能报错。\n *\n * 通过 JSON roundtrip(stringify → parse)清理掉所有不可序列化的属性。\n */\nexport function cleanSchema(schema: unknown): unknown {\n return JSON.parse(JSON.stringify(schema));\n}\n","/**\n * BaseAIClient — 可继承的 AI 客户端基类。\n *\n * 提供 `AIClient` 接口的类实现,用户可以:\n * 1. 直接实例化 — 传入自定义 `chatHandler` 回调,完全控制对话逻辑\n * 2. 继承扩展 — 覆盖 `chat()` 方法,实现自定义 AI 对接\n *\n * 使用场景:\n * - 对接非标准 AI API(如私有部署的模型服务)\n * - 添加请求拦截(日志、重试、缓存、限流等中间件逻辑)\n * - 对接本地模型(Ollama、llama.cpp 等)\n * - 测试 Mock(注入固定响应进行单元测试)\n *\n * 使用示例:\n *\n * ```ts\n * // 方式一:直接实例化 + chatHandler\n * const client = new BaseAIClient({\n * chatHandler: async (params) => {\n * const res = await fetch(\"https://my-api.com/chat\", {\n * method: \"POST\",\n * headers: { \"Content-Type\": \"application/json\" },\n * body: JSON.stringify(params),\n * });\n * const data = await res.json();\n * return { text: data.reply };\n * },\n * });\n *\n * // 方式二:继承扩展\n * class MyAIClient extends BaseAIClient {\n * async chat(params) {\n * // 添加自定义逻辑(日志、重试等)\n * console.log(\"Sending:\", params.messages.length, \"messages\");\n * const response = await super.chat(params);\n * console.log(\"Received:\", response.text?.length, \"chars\");\n * return response;\n * }\n * }\n *\n * // 传入 WebAgent\n * const agent = new WebAgent({ client: new MyAIClient({ chatHandler }) });\n * ```\n *\n * 文件位置:\n * ai-client/custom.ts ←── ai-client/index.ts(re-export)\n * ←── web/index.ts(WebAgent 接受 client 选项)\n */\nimport type { AIChatResponse, AIClient, AIMessage } from \"../types.js\";\nimport type { ToolDefinition } from \"../tool-registry.js\";\n\n// ─── 类型定义 ───\n\n/** chat 方法的入参(与 AIClient.chat 签名一致) */\nexport type ChatHandlerParams = {\n /** 系统提示词 */\n systemPrompt: string;\n /** 对话消息列表 */\n messages: AIMessage[];\n /** 可用工具定义列表 */\n tools?: ToolDefinition[];\n};\n\n/**\n * BaseAIClient 构造选项。\n *\n * `chatHandler` 是核心 — 用户提供的对话处理函数。\n * 它接收统一格式的入参,返回统一格式的 AIChatResponse。\n */\nexport type BaseAIClientOptions = {\n /** 对话处理函数 — 接收 ChatHandlerParams,返回 AIChatResponse */\n chatHandler: (params: ChatHandlerParams) => Promise<AIChatResponse>;\n};\n\n// ─── BaseAIClient 类 ───\n\n/**\n * 可继承的 AI 客户端基类 — 实现 AIClient 接口。\n *\n * 设计原则:\n * - 实现 `AIClient` 接口 → 可直接传入 `executeAgentLoop()` 和 `WebAgent`\n * - 构造时注入 `chatHandler` → 无需继承即可自定义对话逻辑\n * - `chat()` 方法可被子类覆盖 → 支持继承式扩展(添加中间件逻辑)\n */\nexport class BaseAIClient implements AIClient {\n /** 用户提供的对话处理函数 */\n protected chatHandler: (params: ChatHandlerParams) => Promise<AIChatResponse>;\n\n constructor(options: BaseAIClientOptions) {\n this.chatHandler = options.chatHandler;\n }\n\n /**\n * 发送对话请求并获取 AI 响应。\n *\n * 默认实现直接委托给 `chatHandler`。\n * 子类可覆盖此方法添加中间件逻辑(日志、重试、缓存等)。\n *\n * @param params - 统一格式的聊天参数\n * @returns 统一格式的 AI 响应\n */\n async chat(params: ChatHandlerParams): Promise<AIChatResponse> {\n return this.chatHandler(params);\n }\n}\n","/**\n * OpenAI / Copilot(GitHub Models)AI 客户端。\n *\n * OpenAI 和 Copilot 使用相同的 API 格式(Chat Completions API),\n * 区别仅在端点 URL 和认证方式:\n * - OpenAI → https://api.openai.com/v1/chat/completions + Bearer <API Key>\n * - Copilot → https://models.inference.ai.azure.com/chat/completions + Bearer <GitHub PAT>\n *\n * 提供两层能力:\n * - 类:OpenAIClient(继承 BaseAIClient)— 封装完整 fetch 流程\n * - 函数:buildOpenAIRequest / parseOpenAIResponse — 底层格式转换\n *\n * 继承关系:\n * BaseAIClient(custom.ts)\n * └── OpenAIClient(本文件)— 覆盖 chat(),内部调用 build → fetch → parse\n *\n * 使用方:\n * ai-client/openai.ts ←── ai-client/index.ts(主入口)\n */\nimport type { AIChatResponse, AIMessage, AIToolCall } from \"../types.js\";\nimport type { AIClientConfig, ChatParams, ChatRequestInit } from \"./index.js\";\nimport { BaseAIClient } from \"./custom.js\";\nimport type { ChatHandlerParams } from \"./custom.js\";\nimport { resolveBaseURL, cleanSchema } from \"./constants.js\";\n\n// ─── OpenAI 原始 API 响应类型 ───\n\n/** OpenAI tool_calls 中单个工具调用的原始格式 */\ntype OpenAIRawToolCall = {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n};\n\n/** OpenAI Chat Completions API 的原始 JSON 响应 */\ntype OpenAIRawResponse = {\n choices?: Array<{\n message: {\n content: string | null;\n tool_calls?: OpenAIRawToolCall[];\n };\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n };\n};\n\n// ─── OpenAIClient 类 ───\n\n/**\n * OpenAI / Copilot AI 客户端 — 继承 BaseAIClient。\n *\n * 封装完整的 OpenAI Chat Completions API 调用流程:\n * 1. buildOpenAIRequest() → 构建 HTTP 请求\n * 2. fetch() → 发送请求\n * 3. parseOpenAIResponse() → 解析响应为统一格式\n *\n * 使用示例:\n * ```ts\n * const client = new OpenAIClient({\n * provider: \"openai\",\n * model: \"gpt-4o\",\n * apiKey: \"sk-xxx\",\n * });\n * const response = await client.chat({ systemPrompt, messages, tools });\n * ```\n *\n * 也可用于 Copilot(GitHub Models):\n * ```ts\n * const client = new OpenAIClient({\n * provider: \"copilot\",\n * model: \"gpt-4o\",\n * apiKey: \"ghp_xxx\",\n * });\n * ```\n */\nexport class OpenAIClient extends BaseAIClient {\n /** AI 客户端配置(provider / model / apiKey / baseURL) */\n protected config: AIClientConfig;\n\n constructor(config: AIClientConfig) {\n // 注入 chatHandler — 实现 buildRequest → fetch → parseResponse 的完整流程\n super({\n chatHandler: async (params: ChatHandlerParams): Promise<AIChatResponse> => {\n const req = buildOpenAIRequest(this.config, params);\n\n const res = await fetch(req.url, {\n method: req.method,\n headers: req.headers,\n body: req.body,\n });\n\n if (!res.ok) {\n const errText = await res.text();\n throw new Error(`AI API ${res.status}: ${errText.slice(0, 500)}`);\n }\n\n const data = await res.json();\n return parseOpenAIResponse(data);\n },\n });\n this.config = config;\n }\n}\n\n// ─── 底层 API:请求构建 ───\n\n/**\n * 将统一格式的 ChatParams 转换为 OpenAI Chat Completions API 请求。\n *\n * 转换逻辑:\n * - system prompt → `{ role: \"system\", content }` 消息\n * - 工具定义 → `tools` 数组(function calling 格式)\n * - 工具结果 → 拆分为多条 `{ role: \"tool\", tool_call_id }` 消息\n * - AI 回复含工具调用 → `tool_calls` 字段\n *\n * 默认参数:temperature=0.3, max_tokens=8192, tool_choice=\"auto\"\n */\nexport function buildOpenAIRequest(\n config: AIClientConfig,\n params: ChatParams,\n): ChatRequestInit {\n const baseURL = resolveBaseURL(config);\n const { systemPrompt, messages, tools } = params;\n\n // 转换工具定义为 OpenAI function calling 格式\n const openaiTools = tools?.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: cleanSchema(t.schema),\n },\n }));\n\n // 转换消息为 OpenAI 格式\n const openaiMessages = convertMessages(systemPrompt, messages);\n\n // 构建请求体\n const body: Record<string, unknown> = {\n model: config.model,\n messages: openaiMessages,\n temperature: 0.3,\n max_tokens: 8192,\n };\n\n if (openaiTools && openaiTools.length > 0) {\n body.tools = openaiTools;\n body.tool_choice = \"auto\";\n }\n\n return {\n url: `${baseURL}/chat/completions`,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n };\n}\n\n// ─── 响应解析 ───\n\n/**\n * 将 OpenAI Chat Completions API 原始响应解析为统一的 AIChatResponse。\n *\n * 解析要点:\n * - 文本回复 → `choice.message.content`\n * - 工具调用 → `choice.message.tool_calls`,arguments 为 JSON 字符串需 parse\n * - Token 用量 → `usage.prompt_tokens` / `usage.completion_tokens`\n *\n * @throws 无有效 choice 时抛出 Error\n */\nexport function parseOpenAIResponse(data: unknown): AIChatResponse {\n const d = data as OpenAIRawResponse;\n const choice = d.choices?.[0];\n if (!choice) throw new Error(\"AI 未返回有效响应\");\n\n const msg = choice.message;\n\n // 解析工具调用:arguments 是 JSON 字符串,需要 parse 为对象\n const toolCalls: AIToolCall[] | undefined = msg.tool_calls?.map((tc) => ({\n id: tc.id,\n name: tc.function.name,\n input: JSON.parse(tc.function.arguments),\n }));\n\n return {\n text: msg.content || undefined,\n toolCalls: toolCalls?.length ? toolCalls : undefined,\n usage: d.usage\n ? {\n inputTokens: d.usage.prompt_tokens ?? 0,\n outputTokens: d.usage.completion_tokens ?? 0,\n }\n : undefined,\n };\n}\n\n// ─── 内部辅助函数 ───\n\n/**\n * 将统一消息格式转换为 OpenAI 消息数组。\n *\n * 三种特殊消息的处理:\n * 1. tool 消息(工具结果)→ 每个结果拆分为单独的 `role: \"tool\"` 消息\n * 2. assistant 含 toolCalls → 附带 `tool_calls` 字段\n * 3. 其他消息 → 直接映射 role + content\n */\nfunction convertMessages(\n systemPrompt: string,\n messages: AIMessage[],\n): Record<string, unknown>[] {\n const result: Record<string, unknown>[] = [\n { role: \"system\", content: systemPrompt },\n ];\n\n for (const m of messages) {\n if (m.role === \"tool\" && Array.isArray(m.content)) {\n // 工具结果 → 每个结果单独一条 tool 消息(OpenAI 要求按 tool_call_id 对应)\n for (const tc of m.content) {\n result.push({\n role: \"tool\",\n content: tc.result,\n tool_call_id: tc.toolCallId,\n });\n }\n } else if (m.role === \"assistant\" && m.toolCalls?.length) {\n // AI 回复含工具调用 → 带 tool_calls 字段\n result.push({\n role: \"assistant\",\n content: typeof m.content === \"string\" ? m.content : null,\n tool_calls: m.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\",\n function: {\n name: tc.name,\n arguments: JSON.stringify(tc.input),\n },\n })),\n });\n } else {\n // 普通消息(user / assistant 纯文本)\n result.push({\n role: m.role,\n content:\n typeof m.content === \"string\"\n ? m.content\n : JSON.stringify(m.content),\n });\n }\n }\n\n return result;\n}\n","/**\n * Anthropic Messages API 客户端。\n *\n * Anthropic 使用与 OpenAI 完全不同的 API 格式:\n * - system prompt 通过 body.system 字段传入(不在消息数组中)\n * - 工具调用使用 content block 机制(tool_use / tool_result)\n * - 工具结果作为 user 角色消息发送(而非 tool 角色)\n * - API 版本通过 `anthropic-version` 请求头指定\n *\n * 提供两层能力:\n * - 类:AnthropicClient(继承 BaseAIClient)— 封装完整 fetch 流程\n * - 函数:buildAnthropicRequest / parseAnthropicResponse — 底层格式转换\n *\n * 继承关系:\n * BaseAIClient(custom.ts)\n * └── AnthropicClient(本文件)— 覆盖 chat(),内部调用 build → fetch → parse\n *\n * 使用方:\n * ai-client/anthropic.ts ←── ai-client/index.ts(主入口)\n */\nimport type { AIChatResponse, AIMessage, AIToolCall } from \"../types.js\";\nimport type { AIClientConfig, ChatParams, ChatRequestInit } from \"./index.js\";\nimport { BaseAIClient } from \"./custom.js\";\nimport type { ChatHandlerParams } from \"./custom.js\";\nimport { resolveBaseURL, cleanSchema } from \"./constants.js\";\n\n// ─── Anthropic 原始 API 响应类型 ───\n\n/** Anthropic 文本内容块 */\ntype AnthropicTextBlock = {\n type: \"text\";\n text: string;\n};\n\n/** Anthropic 工具调用内容块 */\ntype AnthropicToolUseBlock = {\n type: \"tool_use\";\n id: string;\n name: string;\n input: unknown;\n};\n\n/** Anthropic content 数组中的元素(文本 或 工具调用) */\ntype AnthropicContentBlock = AnthropicTextBlock | AnthropicToolUseBlock;\n\n/** Anthropic Messages API 的原始 JSON 响应 */\ntype AnthropicRawResponse = {\n content?: AnthropicContentBlock[];\n usage?: {\n input_tokens: number;\n output_tokens: number;\n };\n};\n\n// ─── AnthropicClient 类 ───\n\n/**\n * Anthropic AI 客户端 — 继承 BaseAIClient。\n *\n * 封装完整的 Anthropic Messages API 调用流程:\n * 1. buildAnthropicRequest() → 构建 HTTP 请求\n * 2. fetch() → 发送请求\n * 3. parseAnthropicResponse() → 解析响应为统一格式\n *\n * 使用示例:\n * ```ts\n * const client = new AnthropicClient({\n * provider: \"anthropic\",\n * model: \"claude-sonnet-4-20250514\",\n * apiKey: \"sk-ant-xxx\",\n * });\n * const response = await client.chat({ systemPrompt, messages, tools });\n * ```\n */\nexport class AnthropicClient extends BaseAIClient {\n /** AI 客户端配置(provider / model / apiKey / baseURL) */\n protected config: AIClientConfig;\n\n constructor(config: AIClientConfig) {\n // 注入 chatHandler — 实现 buildRequest → fetch → parseResponse 的完整流程\n super({\n chatHandler: async (params: ChatHandlerParams): Promise<AIChatResponse> => {\n const req = buildAnthropicRequest(this.config, params);\n\n const res = await fetch(req.url, {\n method: req.method,\n headers: req.headers,\n body: req.body,\n });\n\n if (!res.ok) {\n const errText = await res.text();\n throw new Error(`AI API ${res.status}: ${errText.slice(0, 500)}`);\n }\n\n const data = await res.json();\n return parseAnthropicResponse(data);\n },\n });\n this.config = config;\n }\n}\n\n// ─── 底层 API:请求构建 ───\n\n/**\n * 将统一格式的 ChatParams 转换为 Anthropic Messages API 请求。\n *\n * 关键格式差异(与 OpenAI 相比):\n * - system prompt → body.system 字段(非消息数组元素)\n * - 工具定义 → input_schema(而非 parameters)\n * - 工具结果 → user 角色 + tool_result content block\n * - AI 工具调用 → assistant 角色 + tool_use content block\n *\n * max_tokens 策略:opus 模型 16384,其他模型 8192。\n * 认证头使用 `x-api-key`(而非 Authorization Bearer)。\n */\nexport function buildAnthropicRequest(\n config: AIClientConfig,\n params: ChatParams,\n): ChatRequestInit {\n const baseURL = resolveBaseURL(config);\n const { systemPrompt, messages, tools } = params;\n\n // 转换工具定义为 Anthropic 格式(input_schema 而非 parameters)\n const anthropicTools = tools?.map((t) => ({\n name: t.name,\n description: t.description,\n input_schema: cleanSchema(t.schema),\n }));\n\n // 转换消息为 Anthropic 格式(过滤掉 system 角色消息)\n const anthropicMessages = convertMessages(messages);\n\n // 构建请求体 — system 作为顶层字段\n const body: Record<string, unknown> = {\n model: config.model,\n max_tokens: config.model.includes(\"opus\") ? 16384 : 8192,\n system: systemPrompt,\n messages: anthropicMessages,\n };\n\n if (anthropicTools && anthropicTools.length > 0) {\n body.tools = anthropicTools;\n }\n\n return {\n url: `${baseURL}/v1/messages`,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": config.apiKey,\n \"anthropic-version\": \"2023-06-01\",\n },\n body: JSON.stringify(body),\n };\n}\n\n// ─── 响应解析 ───\n\n/**\n * 将 Anthropic Messages API 原始响应解析为统一的 AIChatResponse。\n *\n * Anthropic 使用 content block 数组返回多种内容:\n * - type=\"text\" → 文本回复(可能多个,合并为一个字符串)\n * - type=\"tool_use\" → 工具调用(id + name + input)\n *\n * Token 用量字段名也不同:input_tokens / output_tokens(非 prompt_tokens)。\n */\nexport function parseAnthropicResponse(data: unknown): AIChatResponse {\n const d = data as AnthropicRawResponse;\n\n // 提取所有文本块,合并为单个字符串\n const text = d.content\n ?.filter((b): b is AnthropicTextBlock => b.type === \"text\")\n .map((b) => b.text)\n .join(\"\");\n\n // 提取所有工具调用块\n const toolCalls: AIToolCall[] | undefined = d.content\n ?.filter((b): b is AnthropicToolUseBlock => b.type === \"tool_use\")\n .map((b) => ({\n id: b.id,\n name: b.name,\n input: b.input,\n }));\n\n return {\n text: text || undefined,\n toolCalls: toolCalls?.length ? toolCalls : undefined,\n usage: d.usage\n ? {\n inputTokens: d.usage.input_tokens,\n outputTokens: d.usage.output_tokens,\n }\n : undefined,\n };\n}\n\n// ─── 内部辅助函数 ───\n\n/**\n * 将统一消息格式转换为 Anthropic 消息数组。\n *\n * 关键差异处理:\n * 1. 过滤 system 消息(Anthropic 通过 body.system 传入)\n * 2. tool 角色消息 → user 角色 + tool_result content block\n * 3. assistant 含 toolCalls → text + tool_use content blocks\n */\nfunction convertMessages(\n messages: AIMessage[],\n): Record<string, unknown>[] {\n return messages\n .filter((m) => m.role !== \"system\")\n .map((m) => {\n if (m.role === \"tool\" && Array.isArray(m.content)) {\n // 工具结果 → Anthropic 用 user 角色 + tool_result content block\n return {\n role: \"user\" as const,\n content: m.content.map((tc) => ({\n type: \"tool_result\" as const,\n tool_use_id: tc.toolCallId,\n content: tc.result,\n })),\n };\n }\n if (m.role === \"assistant\" && m.toolCalls?.length) {\n // AI 回复含工具调用 → text block + tool_use blocks\n const content: Record<string, unknown>[] = [];\n if (m.content && typeof m.content === \"string\") {\n content.push({ type: \"text\", text: m.content });\n }\n for (const tc of m.toolCalls) {\n content.push({\n type: \"tool_use\",\n id: tc.id,\n name: tc.name,\n input: tc.input,\n });\n }\n return { role: \"assistant\" as const, content };\n }\n // 普通消息(user / assistant 纯文本)\n return {\n role: m.role as \"user\" | \"assistant\",\n content:\n typeof m.content === \"string\"\n ? m.content\n : JSON.stringify(m.content),\n };\n });\n}\n","/**\n * AI Client — 基于 fetch 的 AI 客户端(主入口)。\n *\n * 使用原生 fetch API,浏览器天然支持,不依赖任何 SDK,零环境耦合。\n *\n * 支持三种 provider:\n * - \"openai\" → OpenAI API (https://api.openai.com/v1)\n * - \"copilot\" → GitHub Models API (https://models.inference.ai.azure.com)\n * - \"anthropic\" → Anthropic API (https://api.anthropic.com)\n *\n * 提供两层 API:\n * - 高层:createAIClient(config) → AIClient(工厂函数,自动选择客户端类)\n * - 类:OpenAIClient / AnthropicClient / BaseAIClient(直接实例化)\n *\n * 类继承体系:\n * BaseAIClient(custom.ts)— 可继承的基类,用户自定义 AI 对接\n * ├── OpenAIClient(openai.ts)— OpenAI / Copilot 实现\n * └── AnthropicClient(anthropic.ts)— Anthropic 实现\n *\n * 文件组织:\n * ai-client/index.ts ← 主入口(本文件):类型定义 + dispatcher + re-export\n * ai-client/custom.ts ← BaseAIClient 基类(用户自定义 AI 对接)\n * ai-client/openai.ts ← OpenAIClient + OpenAI 格式转换\n * ai-client/anthropic.ts ← AnthropicClient + Anthropic 格式转换\n * ai-client/constants.ts ← 端点映射 + 共享工具函数\n *\n * 使用方:\n * core/ai-client.ts ←── web/index.ts(WebAgent)\n */\nimport type { AIClient, AIChatResponse, AIMessage } from \"../types.js\";\nimport type { ToolDefinition } from \"../tool-registry.js\";\nimport { validateProvider } from \"./constants.js\";\nimport { OpenAIClient } from \"./openai.js\";\nimport { AnthropicClient } from \"./anthropic.js\";\n\n// Re-export 类型,方便外部统一从 ai-client 导入\nexport type { AIClient, AIChatResponse, AIMessage, AIToolCall } from \"../types.js\";\n\n// Re-export 客户端类(基类 + OpenAI + Anthropic)\nexport { BaseAIClient, type BaseAIClientOptions, type ChatHandlerParams } from \"./custom.js\";\nexport { OpenAIClient } from \"./openai.js\";\nexport { AnthropicClient } from \"./anthropic.js\";\n\n// ─── 公共类型定义 ───\n\n/** AI 客户端配置 */\nexport type AIClientConfig = {\n /** AI 提供商: \"openai\" | \"copilot\" | \"anthropic\" */\n provider: string;\n /** 模型名称,如 \"gpt-4o\"、\"claude-sonnet-4-20250514\" */\n model: string;\n /** API Key / Token */\n apiKey: string;\n /** 自定义 API 基础 URL(可选,如本地 Ollama: http://localhost:11434/v1) */\n baseURL?: string;\n};\n\n/** chat 方法的统一入参 */\nexport type ChatParams = {\n /** 系统提示词 */\n systemPrompt: string;\n /** 对话消息列表 */\n messages: AIMessage[];\n /** 可用工具定义列表 */\n tools?: ToolDefinition[];\n};\n\n/**\n * 构建好的 HTTP 请求对象 — 可直接传给 fetch。\n *\n * 被 OpenAIClient / AnthropicClient 内部使用,\n * 也可通过 buildOpenAIRequest() / buildAnthropicRequest() 底层函数获取。\n */\nexport type ChatRequestInit = {\n /** 请求 URL */\n url: string;\n /** HTTP 方法 */\n method: \"POST\";\n /** 请求头 */\n headers: Record<string, string>;\n /** 请求体(JSON 字符串) */\n body: string;\n};\n\n// ─── 高层 API ───\n\n/**\n * 创建 AI 客户端(高层 API)。\n *\n * 根据 provider 自动创建对应的客户端类实例:\n * - openai / copilot → new OpenAIClient(config)\n * - anthropic → new AnthropicClient(config)\n *\n * 返回 AIClient 接口,调用 chat() 即可与 AI 对话。\n *\n * @param config - 包含 provider、model、apiKey 等配置\n * @returns AIClient 实例(OpenAIClient 或 AnthropicClient)\n */\nexport function createAIClient(config: AIClientConfig): AIClient {\n validateProvider(config.provider);\n\n switch (config.provider) {\n case \"openai\":\n case \"copilot\":\n return new OpenAIClient(config);\n case \"anthropic\":\n return new AnthropicClient(config);\n default:\n throw new Error(\n `Unknown AI provider: ${config.provider}. Supported: openai, copilot, anthropic`,\n );\n }\n}\n","/**\n * Tool Registry — 工具注册表,负责工具的注册、查询和分发。\n *\n * 实例化设计 — 每个 Agent 创建独立的 ToolRegistry,避免全局状态污染:\n *\n * // Node 端\n * const registry = new ToolRegistry();\n * registerBuiltinTools(registry); // 注册 exec, file, browser...\n * await executeAgentLoop({ registry, ... });\n *\n * // Web 端\n * const registry = new ToolRegistry();\n * registerWebTools(registry); // 注册 dom, navigate...\n * await executeAgentLoop({ registry, ... });\n *\n * 优点:\n * - 多实例安全:Node Agent 和 Web Agent 可并行运行,工具列表互不干扰\n * - 测试隔离:每个 test case 创建独立 registry,无需清理全局状态\n * - 可组合:可按需注册不同工具子集\n */\nimport type { TObject } from \"@sinclair/typebox\";\nexport { jsonResult, readNumberParam, readStringParam } from \"./tool-params.js\";\n\n/**\n * 工具执行结果 — 每个工具的 execute() 必须返回此类型。\n */\nexport type ToolCallResult = {\n /** 返回内容(字符串文本或结构化对象,最终会序列化后发给 AI) */\n content: string | Record<string, unknown>;\n /** 可选的额外细节(用于日志记录、调试等,不直接发给 AI) */\n details?: Record<string, unknown>;\n};\n\n/**\n * 工具定义 — 注册工具时需要提供的完整描述。\n *\n * 这四个字段分别告诉 AI「叫什么名字」「能做什么」「需要什么参数」「怎么执行」:\n * - name + description → AI 根据用户意图选择合适的工具\n * - schema → AI 生成符合格式的参数 JSON\n * - execute → 实际执行逻辑\n */\nexport type ToolDefinition = {\n /** 工具名称(AI 通过此名称调用,如 \"exec\"、\"file_read\") */\n name: string;\n /** 工具描述(AI 据此判断何时使用这个工具) */\n description: string;\n /** 参数的 JSON Schema(TypeBox 定义,描述工具接受哪些参数及其类型) */\n schema: TObject;\n /** 执行函数 — 接收 AI 传入的参数,返回执行结果 */\n execute: (params: Record<string, unknown>) => Promise<ToolCallResult>;\n};\n\n/**\n * 工具注册表实例 — 管理一组工具的注册、查询和分发。\n *\n * 每个 Agent 拥有独立的 ToolRegistry 实例,从而:\n * - Node Agent 的 exec/file 工具不会泄漏到 Web Agent\n * - Web Agent 的 dom/navigate 工具不会泄漏到 Node Agent\n * - 测试中不同 case 互不影响\n */\nexport class ToolRegistry {\n private tools = new Map<string, ToolDefinition>();\n\n /** 注册一个工具 */\n register(tool: ToolDefinition): void {\n this.tools.set(tool.name, tool);\n }\n\n /** 获取所有已注册的工具定义列表(发给 AI,告知可用工具) */\n getDefinitions(): ToolDefinition[] {\n return Array.from(this.tools.values());\n }\n\n /**\n * 根据工具名分发并执行工具调用。\n * - 找到工具 → 执行 execute() → 返回结果\n * - 找不到 → 返回错误信息(不抛异常,让 AI 知道工具不存在)\n * - 执行出错 → 捕获异常,返回错误信息(不中断 Agent 循环)\n */\n async dispatch(name: string, input: unknown): Promise<ToolCallResult> {\n const tool = this.tools.get(name);\n if (!tool) {\n return {\n content: `Unknown tool: ${name}`,\n details: { error: true, toolName: name },\n };\n }\n\n try {\n const params = (input ?? {}) as Record<string, unknown>;\n return await tool.execute(params);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: `Tool \"${name}\" failed: ${message}`,\n details: { error: true, toolName: name, message },\n };\n }\n }\n}\n","/**\n * 极简系统提示词 — 告诉 AI 它是谁以及有哪些工具可用。\n *\n * 纯函数,不依赖任何配置或全局状态。\n * 调用方传入工具列表即可。\n *\n * 【后续可拓展】\n * - 添加 Runtime 信息段(provider、model、date 等)\n * - 支持 extraInstructions 注入自定义指令\n * - 支持 thinkingLevel 控制思考深度\n */\nimport type { ToolDefinition } from \"./tool-registry.js\";\n\nexport type SystemPromptParams = {\n /** 已注册的工具列表(由调用方从 ToolRegistry 获取后传入) */\n tools?: ToolDefinition[];\n /** AI 思考深度 */\n thinkingLevel?: string;\n};\n\n/**\n * 构建系统提示词。\n * 由两部分组成:身份描述 + 可用工具列表。\n */\nexport function buildSystemPrompt(params: SystemPromptParams = {}): string {\n const sections: string[] = [];\n\n // 身份 + 操作规则(精简版)\n sections.push(\n \"You are AutoPilot, an AI agent embedded in the user's web page.\\n\" +\n \"You can click, fill forms, read content, navigate, and execute JavaScript.\\n\\n\" +\n \"## 操作规则\\n\\n\" +\n \"1. 快照中每个元素末尾的 `#xxxx` 是 hash ID。操作时**必须**用 `#xxxx` 作为 dom 工具的 selector 参数。\\n\" +\n \"2. **禁止**猜测 CSS 选择器,只用快照中的 hash ID。\\n\" +\n \"3. 多个相似元素时,根据层级结构、所在功能区域和用户意图判断目标。\\n\" +\n \"4. 快照看不到目标时,先滚动页面或用 snapshot 获取更深层级。\\n\" +\n \"5. 破坏性操作前先与用户确认。\"\n );\n\n // 工具列表\n const tools = params.tools ?? [];\n if (tools.length > 0) {\n const toolLines = tools.map(t => `- **${t.name}**: ${t.description}`);\n sections.push(\n \"## Available Tools\\n\\n\" +\n toolLines.join(\"\\n\") + \"\\n\\n\" +\n \"Use tools when needed to complete the user's request.\"\n );\n }\n\n return sections.join(\"\\n\\n\");\n}\n","/**\n * DOM Tool — 基于 Web API 的 DOM 操作工具。\n *\n * 替代 Playwright 的 click/fill/type 等操作,直接在页面上下文中执行。\n * 运行环境:浏览器 Content Script。\n *\n * 支持 8 种动作:\n * click — 点击元素\n * fill — 填写输入框(清空后设值)\n * type — 逐字符模拟键入\n * get_text — 获取元素文本内容\n * get_attr — 获取元素属性值\n * set_attr — 设置元素属性\n * add_class — 添加 CSS 类名\n * remove_class — 移除 CSS 类名\n */\nimport { Type } from \"@sinclair/typebox\";\nimport type { ToolDefinition, ToolCallResult } from \"../core/tool-registry.js\";\nimport type { RefStore } from \"./ref-store.js\";\n\nconst DEFAULT_WAIT_MS = 1000;\n\n/** 当前活跃的 RefStore 实例(由 WebAgent 在 chat() 时设置) */\nlet activeRefStore: RefStore | undefined;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * 安全地查询 DOM 元素。\n *\n * 支持两种定位方式(优先级从高到低):\n * - hash ID(以 \"#\" 开头且在 RefStore 中存在):确定性 hash 查找(最高效)\n * - CSS 选择器(其他):传统 querySelector\n */\nfunction queryElement(selector: string): Element | string {\n try {\n // #hashId — 优先从 RefStore 查找\n if (selector.startsWith(\"#\") && activeRefStore) {\n const id = selector.slice(1); // 去掉 #\n if (activeRefStore.has(id)) {\n const el = activeRefStore.get(id);\n if (!el) return `未找到 ref \"${selector}\" 对应的元素(可能已被移除或快照已过期)`;\n return el;\n }\n // 不在 RefStore 中 → 回退到 CSS 选择器(可能是 #some-id)\n }\n\n // CSS 选择器\n const el = document.querySelector(selector);\n if (!el) return `未找到匹配 \"${selector}\" 的元素`;\n return el;\n } catch (e) {\n return `选择器语法错误: ${selector}`;\n }\n}\n\n/**\n * 设置当前活跃的 RefStore(由 WebAgent 在 chat 开始时调用)。\n */\nexport function setActiveRefStore(store: RefStore | undefined): void {\n activeRefStore = store;\n}\n\n/** 获取当前活跃的 RefStore(供其他工具复用) */\nexport function getActiveRefStore(): RefStore | undefined {\n return activeRefStore;\n}\n\n/**\n * 在给定超时时间内轮询查找元素。\n * - 返回 Element:找到元素\n * - 返回 string:选择器语法错误\n * - 返回 null:超时未找到\n */\nasync function waitForElement(\n selector: string,\n timeoutMs: number,\n): Promise<Element | string | null> {\n const start = Date.now();\n\n while (Date.now() - start <= timeoutMs) {\n const elOrError = queryElement(selector);\n if (typeof elOrError !== \"string\") return elOrError;\n\n if (elOrError.startsWith(\"选择器语法错误\")) return elOrError;\n await sleep(100);\n }\n\n return null;\n}\n\nfunction resolveWaitMs(params: Record<string, unknown>): number {\n const waitMs = params.waitMs;\n if (typeof waitMs === \"number\" && Number.isFinite(waitMs)) {\n return Math.max(0, Math.floor(waitMs));\n }\n\n const waitSeconds = params.waitSeconds;\n if (typeof waitSeconds === \"number\" && Number.isFinite(waitSeconds)) {\n return Math.max(0, Math.floor(waitSeconds * 1000));\n }\n\n return DEFAULT_WAIT_MS;\n}\n\n/**\n * 模拟真实用户输入:触发 input、change 事件,兼容 React/Vue 等框架。\n */\nfunction dispatchInputEvents(el: HTMLInputElement | HTMLTextAreaElement): void {\n el.dispatchEvent(new Event(\"input\", { bubbles: true, cancelable: true }));\n el.dispatchEvent(new Event(\"change\", { bubbles: true, cancelable: true }));\n}\n\n/**\n * 生成元素的可读描述,用于在操作结果中展示实际命中的 DOM 节点。\n * 格式:<tag#id.class> \"文本\" [attr=val, ...]\n */\nfunction describeElement(el: Element): string {\n const tag = el.tagName.toLowerCase();\n const id = el.id ? `#${el.id}` : \"\";\n const cls = el.className && typeof el.className === \"string\"\n ? el.className.trim().split(/\\s+/).filter(Boolean).slice(0, 3).map(c => `.${c}`).join(\"\")\n : \"\";\n const text = el.textContent?.trim().slice(0, 40) ?? \"\";\n const textHint = text ? ` \"${text}\"` : \"\";\n\n // 关键属性\n const hints: string[] = [];\n for (const attr of [\"type\", \"name\", \"placeholder\", \"href\", \"role\"]) {\n const val = el.getAttribute(attr);\n if (val) hints.push(`${attr}=${val}`);\n }\n const attrHint = hints.length > 0 ? ` [${hints.join(\", \")}]` : \"\";\n\n return `<${tag}${id}${cls}>${textHint}${attrHint}`;\n}\n\nexport function createDomTool(): ToolDefinition {\n return {\n name: \"dom\",\n description: [\n \"Perform DOM operations on the current page.\",\n \"Actions: click, fill, type, get_text, get_attr, set_attr, add_class, remove_class.\",\n \"Use the hash ID from DOM snapshot (e.g. #a1b2c) as selector.\",\n ].join(\" \"),\n\n schema: Type.Object({\n action: Type.String({\n description:\n \"DOM action: click | fill | type | get_text | get_attr | set_attr | add_class | remove_class\",\n }),\n selector: Type.String({ description: \"Element ref ID from snapshot (e.g. #r0, #r5) or CSS selector\" }),\n value: Type.Optional(\n Type.String({ description: \"Value for fill/type/set_attr actions\" }),\n ),\n attribute: Type.Optional(\n Type.String({ description: \"Attribute name for get_attr/set_attr actions\" }),\n ),\n className: Type.Optional(\n Type.String({ description: \"CSS class name for add_class/remove_class\" }),\n ),\n waitMs: Type.Optional(\n Type.Number({\n description:\n \"Optional wait timeout in ms before action (default: 1000). Use 0 to disable waiting.\",\n }),\n ),\n waitSeconds: Type.Optional(\n Type.Number({\n description:\n \"Optional wait timeout in seconds before action. Used when waitMs is not provided.\",\n }),\n ),\n }),\n\n execute: async (params): Promise<ToolCallResult> => {\n const action = params.action as string;\n const selector = params.selector as string;\n const waitMs = resolveWaitMs(params);\n\n if (!selector) return { content: \"缺少 selector 参数\" };\n\n let el: Element;\n if (waitMs > 0) {\n const found = await waitForElement(selector, waitMs);\n\n if (typeof found === \"string\") {\n return {\n content: found,\n details: { error: true, code: \"INVALID_SELECTOR\", action, selector },\n };\n }\n\n if (!found) {\n return {\n content: `未找到匹配 \"${selector}\" 的元素`,\n details: {\n error: true,\n code: \"ELEMENT_NOT_FOUND\",\n action,\n selector,\n waitMs,\n },\n };\n }\n\n el = found;\n } else {\n const elOrError = queryElement(selector);\n if (typeof elOrError === \"string\") {\n const code = elOrError.startsWith(\"未找到\")\n ? \"ELEMENT_NOT_FOUND\"\n : \"INVALID_SELECTOR\";\n return {\n content: elOrError,\n details: { error: true, code, action, selector, waitMs },\n };\n }\n el = elOrError;\n }\n\n try {\n switch (action) {\n // ─── 交互类 ───\n\n case \"click\": {\n // 模拟点击:先 focus 再 click,触发完整事件链\n if (el instanceof HTMLElement) {\n el.focus();\n el.click();\n } else {\n el.dispatchEvent(new MouseEvent(\"click\", { bubbles: true }));\n }\n return { content: `已点击 ${describeElement(el)}` };\n }\n\n case \"fill\": {\n // 填写输入框:清空原有值 → 设置新值 → 触发 input/change 事件\n const value = params.value as string;\n if (value === undefined) return { content: \"缺少 value 参数\" };\n\n if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {\n el.focus();\n el.value = value;\n dispatchInputEvents(el);\n } else if (el instanceof HTMLElement && el.isContentEditable) {\n el.focus();\n el.textContent = value;\n el.dispatchEvent(new Event(\"input\", { bubbles: true }));\n } else {\n return { content: `\"${selector}\" 不是可编辑元素` };\n }\n return { content: `已填写 ${describeElement(el)}: \"${value}\"` };\n }\n\n case \"type\": {\n // 逐字符键入:每个字符触发 keydown → keypress → input → keyup\n // 适用于有实时监听键盘事件的输入框(如搜索自动补全)\n const value = params.value as string;\n if (value === undefined) return { content: \"缺少 value 参数\" };\n\n if (el instanceof HTMLElement) el.focus();\n\n for (const char of value) {\n el.dispatchEvent(\n new KeyboardEvent(\"keydown\", { key: char, bubbles: true }),\n );\n el.dispatchEvent(\n new KeyboardEvent(\"keypress\", { key: char, bubbles: true }),\n );\n if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {\n el.value += char;\n }\n el.dispatchEvent(new Event(\"input\", { bubbles: true }));\n el.dispatchEvent(\n new KeyboardEvent(\"keyup\", { key: char, bubbles: true }),\n );\n }\n return { content: `已逐字输入到 ${describeElement(el)}: \"${value}\"` };\n }\n\n // ─── 读取类 ───\n\n case \"get_text\": {\n // 获取元素的文本内容(包括子元素)\n const text = el.textContent?.trim() ?? \"\";\n return { content: `${describeElement(el)} 的文本内容:${text || \"(空)\"}` };\n }\n\n case \"get_attr\": {\n // 获取元素的指定属性值\n const attribute = params.attribute as string;\n if (!attribute) return { content: \"缺少 attribute 参数\" };\n const attrValue = el.getAttribute(attribute);\n return { content: `${describeElement(el)} 的 ${attribute} = ${attrValue ?? \"(不存在)\"}` };\n }\n\n // ─── 修改类 ───\n\n case \"set_attr\": {\n // 设置元素的属性值\n const attribute = params.attribute as string;\n const value = params.value as string;\n if (!attribute || value === undefined)\n return { content: \"缺少 attribute 或 value 参数\" };\n el.setAttribute(attribute, value);\n return { content: `已设置 ${describeElement(el)} 的 ${attribute}=\"${value}\"` };\n }\n\n case \"add_class\": {\n // 给元素添加 CSS 类名\n const className = params.className as string;\n if (!className) return { content: \"缺少 className 参数\" };\n el.classList.add(className);\n return { content: `已添加 class \"${className}\" 到 ${describeElement(el)}` };\n }\n\n case \"remove_class\": {\n // 移除元素的 CSS 类名\n const className = params.className as string;\n if (!className) return { content: \"缺少 className 参数\" };\n el.classList.remove(className);\n return { content: `已移除 ${describeElement(el)} 的 class \"${className}\"` };\n }\n\n default:\n return { content: `未知的 DOM 动作: ${action}` };\n }\n } catch (err) {\n return {\n content: `DOM 操作 \"${action}\" 失败: ${err instanceof Error ? err.message : String(err)}`,\n details: { error: true, action, selector },\n };\n }\n },\n };\n}\n","/**\n * Page Info Tool — 基于 Web API 的页面信息获取工具。\n *\n * 替代 Playwright 的 getTitle/getUrl/snapshot 等。\n * 运行环境:浏览器 Content Script。\n *\n * 支持 6 种动作:\n * get_url — 获取当前页面 URL\n * get_title — 获取页面标题\n * get_selection — 获取用户选中的文本\n * get_viewport — 获取视口尺寸和滚动位置\n * snapshot — 获取页面 DOM 结构快照(AI 可读的文本描述)\n * query_all — 查询所有匹配选择器的元素,返回摘要信息\n */\nimport { Type } from \"@sinclair/typebox\";\nimport type { ToolDefinition, ToolCallResult } from \"../core/tool-registry.js\";\nimport type { RefStore } from \"./ref-store.js\";\nimport { getActiveRefStore } from \"./dom-tool.js\";\n\n/** 快照配置选项 */\nexport type SnapshotOptions = {\n /** 最大遍历深度(默认 6) */\n maxDepth?: number;\n /**\n * 视口裁剪:只保留与视口相交的元素(默认 true)。\n * 开启后,完全在视口外的元素会被跳过,大幅减少 token 消耗。\n * 注意:祖先容器即使自身不在视口内,只要有子元素在视口内就会保留。\n */\n viewportOnly?: boolean;\n /**\n * 智能剪枝:折叠无意义的纯布局容器(默认 true)。\n * 开启后,没有文本、没有 id、没有交互属性的纯布局元素(div/span/section 等)\n * 如果自身无意义,会被折叠——子元素直接提升到父级输出,减少嵌套噪音。\n */\n pruneLayout?: boolean;\n /**\n * hash ID 映射表(可选)。\n * 传入 RefStore 实例后,每个元素使用确定性 hash ID 替代完整 XPath,\n * 大幅减少 token 消耗。dom-tool 通过 RefStore.get(id) 解析回 DOM 元素。\n */\n refStore?: RefStore;\n};\n\n/**\n * 生成页面 DOM 快照 — 将 DOM 树转为 AI 可理解的文本描述。\n *\n * 基于 Web API 实现,只遍历可见元素,跳过 script/style/svg 等无意义节点。\n * 传入 RefStore 时,每个元素生成确定性 hash ID(如 #a1b2c),\n * AI 通过 hash ID 精确定位元素,无需猜测 CSS 选择器。\n *\n * 输出格式示例:\n * [header] #k9f2a\n * [nav] #m3d7e\n * [a] \"首页\" href=\"/\" #p1c4b\n * [a] \"关于\" href=\"/about\" #q8e5f\n * [main] #r2a6d\n * [h1] \"欢迎\" #s7g3h\n * [input] type=\"text\" placeholder=\"搜索...\" #t4j8k\n * [button] \"搜索\" id=\"search-btn\" onclick #u5n2m\n *\n * @param root - 快照根元素(默认 document.body)\n * @param options - 快照选项对象,或传入数字作为 maxDepth(向后兼容)\n */\nexport function generateSnapshot(\n root: Element = document.body,\n options: SnapshotOptions | number = {},\n): string {\n // 向后兼容:数字参数视为 maxDepth\n const opts: SnapshotOptions = typeof options === \"number\"\n ? { maxDepth: options }\n : options;\n\n const maxDepth = opts.maxDepth ?? 6;\n const viewportOnly = opts.viewportOnly ?? true;\n const pruneLayout = opts.pruneLayout ?? true;\n\n const refStore = opts.refStore;\n\n const SKIP_TAGS = new Set([\n \"SCRIPT\", \"STYLE\", \"SVG\", \"NOSCRIPT\", \"LINK\", \"META\", \"BR\", \"HR\",\n ]);\n\n /** 纯布局容器标签 — 智能剪枝时可能被折叠 */\n const LAYOUT_TAGS = new Set([\n \"DIV\", \"SPAN\", \"SECTION\", \"ARTICLE\", \"ASIDE\", \"MAIN\",\n \"HEADER\", \"FOOTER\", \"NAV\", \"FIGURE\", \"FIGCAPTION\",\n ]);\n\n /** 视口尺寸(viewportOnly 开启时使用) */\n const vpWidth = viewportOnly ? window.innerWidth : 0;\n const vpHeight = viewportOnly ? window.innerHeight : 0;\n\n const INTERACTIVE_ATTRS = [\n \"href\", \"type\", \"placeholder\", \"value\", \"name\", \"role\", \"aria-label\",\n \"src\", \"alt\", \"title\", \"for\", \"action\", \"method\", \"target\", \"min\", \"max\",\n \"pattern\", \"maxlength\", \"tabindex\",\n ];\n\n /** 布尔状态属性 — 只在存在时输出(无值),如 disabled、checked */\n const BOOLEAN_ATTRS = [\n \"disabled\", \"checked\", \"readonly\", \"required\", \"selected\",\n \"hidden\", \"multiple\", \"autofocus\", \"open\",\n ];\n\n /** 内联事件属性前缀 */\n const EVENT_PREFIX = \"on\";\n\n /**\n * 计算元素在父节点中同标签兄弟里的序号(1-based,XPath 规范)。\n * 如果同标签兄弟只有一个,返回空字符串(无需索引消歧)。\n */\n function getSiblingIndex(el: Element): string {\n const parent = el.parentElement;\n if (!parent) return \"\";\n const tag = el.tagName;\n const siblings = Array.from(parent.children).filter((c) => c.tagName === tag);\n if (siblings.length <= 1) return \"\";\n return `[${siblings.indexOf(el) + 1}]`;\n }\n\n /**\n * 判断元素是否与视口相交(部分可见也算)。\n * 对根级容器(depth <= 1)始终返回 true,确保不丢失顶层结构。\n */\n function isInViewport(el: Element, depth: number): boolean {\n if (!viewportOnly) return true;\n // 根级容器始终保留(body/html 等),否则整棵树会被跳过\n if (depth <= 1) return true;\n const rect = el.getBoundingClientRect();\n // 元素完全在视口外则跳过\n if (rect.bottom < 0 || rect.top > vpHeight) return false;\n if (rect.right < 0 || rect.left > vpWidth) return false;\n // 零尺寸元素(如隐藏的 position:absolute 元素)也跳过\n if (rect.width === 0 && rect.height === 0) return false;\n return true;\n }\n\n /**\n * 判断元素是否为「无意义布局容器」(智能剪枝候选)。\n * 满足所有条件时返回 true:\n * 1. 标签是常见布局容器(div/span/section 等)\n * 2. 没有 id\n * 3. 没有交互属性(href/role/aria-label/onclick 等)\n * 4. 没有直接文本内容\n */\n function isEmptyLayoutContainer(el: Element, directText: string): boolean {\n if (!pruneLayout) return false;\n if (!LAYOUT_TAGS.has(el.tagName)) return false;\n // 有 id 的元素可能是重要锚点\n if (el.getAttribute(\"id\")) return false;\n // 有 role/aria-label 的元素有语义\n if (el.getAttribute(\"role\") || el.getAttribute(\"aria-label\")) return false;\n // 有内联事件(onclick 等)的元素有交互\n for (const attr of Array.from(el.attributes)) {\n if (attr.name.startsWith(\"on\")) return false;\n }\n // 有直接文本内容的元素有意义\n if (directText) return false;\n return true;\n }\n\n function walk(el: Element, depth: number, parentPath: string): string {\n if (depth > maxDepth) return \"\";\n if (SKIP_TAGS.has(el.tagName)) return \"\";\n\n // 跳过不可见元素\n const style = window.getComputedStyle(el);\n if (style.display === \"none\" || style.visibility === \"hidden\") return \"\";\n\n // ─── 视口裁剪 ───\n // 检查元素是否在视口内(viewportOnly 关闭时始终通过)\n if (!isInViewport(el, depth)) return \"\";\n\n const indent = \" \".repeat(depth);\n const tag = el.tagName.toLowerCase();\n\n // 构建当前元素的内部路径(用于 hash 计算,不输出到快照)\n const index = getSiblingIndex(el);\n const currentPath = `${parentPath}/${tag}${index}`;\n\n // 收集有意义的属性(精简版:只保留对 AI 操作有用的信息)\n const attrs: string[] = [];\n\n // 1. id — 最重要的标识信息\n const elId = el.getAttribute(\"id\");\n if (elId) attrs.push(`id=\"${elId}\"`);\n\n // 2. class — 只保留有语义的类名(过滤框架 hash 类,最多 2 个)\n const className = el.getAttribute(\"class\")?.trim();\n if (className) {\n const classes = className.split(/\\s+/)\n .filter(c => c && !c.startsWith(\"data-v-\") && c.length < 30)\n .slice(0, 2).join(\" \");\n if (classes) attrs.push(`class=\"${classes}\"`);\n }\n\n // 3. 交互属性(href, type, placeholder 等)\n for (const attr of INTERACTIVE_ATTRS) {\n const val = el.getAttribute(attr);\n if (val) attrs.push(`${attr}=\"${val}\"`);\n }\n\n // 4. 布尔状态属性(disabled, checked 等)\n for (const attr of BOOLEAN_ATTRS) {\n if (el.hasAttribute(attr)) attrs.push(attr);\n }\n\n // 5. 事件绑定 — 只检测有交互意义的事件(onclick, onchange)\n const events: string[] = [];\n for (const attrObj of Array.from(el.attributes)) {\n if (attrObj.name.startsWith(EVENT_PREFIX)) {\n events.push(attrObj.name);\n }\n }\n if (events.length > 0) attrs.push(`events=[${events.join(\",\")}]`);\n\n // 6. data-* 属性 — 只保留有语义价值的(跳过框架 scoped 标记如 data-v-xxx)\n const dataAttrs: string[] = [];\n for (const attrObj of Array.from(el.attributes)) {\n if (attrObj.name.startsWith(\"data-\") && !attrObj.name.match(/^data-v-/) && dataAttrs.length < 2) {\n dataAttrs.push(`${attrObj.name}=\"${attrObj.value.slice(0, 30)}\"`);\n }\n }\n if (dataAttrs.length > 0) attrs.push(...dataAttrs);\n\n // 7. 对于 input/textarea,补充当前实际 value\n if ((el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) && el.value) {\n const currentVal = el.value.slice(0, 60);\n const attrVal = el.getAttribute(\"value\");\n if (attrVal !== currentVal) {\n attrs.push(`val=\"${currentVal}\"`);\n }\n }\n\n // 获取直接文本(不含子元素文本)\n let directText = \"\";\n for (let i = 0; i < el.childNodes.length; i++) {\n const node = el.childNodes[i];\n if (node.nodeType === Node.TEXT_NODE) {\n const t = node.textContent?.trim();\n if (t) directText += t + \" \";\n }\n }\n directText = directText.trim();\n\n // ─── 智能剪枝 ───\n // 无意义布局容器:不输出自身行,直接将子元素提升到当前层级\n if (isEmptyLayoutContainer(el, directText)) {\n const childLines: string[] = [];\n for (let i = 0; i < el.children.length; i++) {\n // 子元素继承当前路径(保证 hash 计算正确),但不增加缩进\n const childResult = walk(el.children[i], depth, currentPath);\n if (childResult) childLines.push(childResult);\n }\n // 如果子树也全部为空,整个容器就被剪掉\n return childLines.join(\"\\n\");\n }\n\n // 构建当前元素描述:[标签] \"文本\" 属性 #ID\n let line = `${indent}[${tag}]`;\n if (directText) line += ` \"${directText.slice(0, 60)}\"`;\n if (attrs.length) line += ` ${attrs.join(\" \")}`;\n // 使用 hash ID(如 #a1b2c)或回退到完整 XPath\n if (refStore) {\n const hashId = refStore.set(el, currentPath);\n line += ` #${hashId}`;\n } else {\n line += ` ref=\"${currentPath}\"`;\n }\n\n const lines: string[] = [line];\n\n // 递归子元素\n for (let i = 0; i < el.children.length; i++) {\n const childResult = walk(el.children[i], depth + 1, currentPath);\n if (childResult) lines.push(childResult);\n }\n\n return lines.join(\"\\n\");\n }\n\n // 根元素自身的标签作为路径起点,walk 内部不再重复追加\n // 例如 root=body 时,parentPath=\"\",walk 中 currentPath=\"/body\"\n return walk(root, 0, \"\") || \"(空页面)\";\n}\n\n/**\n * 查询所有匹配元素并返回摘要信息(标签、文本、关键属性)。\n */\nfunction queryAllElements(selector: string, limit = 20): string {\n try {\n const elements = document.querySelectorAll(selector);\n if (elements.length === 0) return `未找到匹配 \"${selector}\" 的元素`;\n\n const results: string[] = [`找到 ${elements.length} 个元素:`];\n const count = Math.min(elements.length, limit);\n\n for (let i = 0; i < count; i++) {\n const el = elements[i];\n const tag = el.tagName.toLowerCase();\n const text = el.textContent?.trim().slice(0, 60) ?? \"\";\n const id = el.id ? `#${el.id}` : \"\";\n const cls = el.className && typeof el.className === \"string\"\n ? `.${el.className.split(\" \").filter(Boolean).join(\".\")}`\n : \"\";\n results.push(` ${i + 1}. <${tag}${id}${cls}> \"${text}\"`);\n }\n\n if (elements.length > limit) {\n results.push(` ...还有 ${elements.length - limit} 个元素`);\n }\n\n return results.join(\"\\n\");\n } catch (e) {\n return `选择器语法错误: ${selector}`;\n }\n}\n\nexport function createPageInfoTool(): ToolDefinition {\n return {\n name: \"page_info\",\n description: [\n \"Get information about the current page.\",\n \"Actions: get_url, get_title, get_selection (selected text),\",\n \"get_viewport (size & scroll), snapshot (DOM structure), query_all (find all matching elements).\",\n ].join(\" \"),\n\n schema: Type.Object({\n action: Type.String({\n description:\n \"Info action: get_url | get_title | get_selection | get_viewport | snapshot | query_all\",\n }),\n selector: Type.Optional(\n Type.String({ description: \"CSS selector for query_all action\" }),\n ),\n maxDepth: Type.Optional(\n Type.Number({ description: \"Max depth for snapshot (default: 6)\" }),\n ),\n viewportOnly: Type.Optional(\n Type.Boolean({ description: \"Only snapshot elements visible in viewport (default: true)\" }),\n ),\n pruneLayout: Type.Optional(\n Type.Boolean({ description: \"Collapse empty layout containers like div/span (default: true)\" }),\n ),\n }),\n\n execute: async (params): Promise<ToolCallResult> => {\n const action = params.action as string;\n\n try {\n switch (action) {\n case \"get_url\":\n return { content: window.location.href };\n\n case \"get_title\":\n return { content: document.title || \"(无标题)\" };\n\n case \"get_selection\": {\n // 获取用户当前选中的文本\n const selection = window.getSelection();\n const text = selection?.toString().trim() ?? \"\";\n return { content: text || \"(未选中任何文本)\" };\n }\n\n case \"get_viewport\": {\n // 获取视口和滚动信息\n const info = {\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n scrollX: window.scrollX,\n scrollY: window.scrollY,\n pageWidth: document.documentElement.scrollWidth,\n pageHeight: document.documentElement.scrollHeight,\n };\n return { content: JSON.stringify(info, null, 2) };\n }\n\n case \"snapshot\": {\n // 生成 DOM 快照 — AI 理解当前页面结构的主要方式\n const maxDepth = (params.maxDepth as number) ?? 6;\n const viewportOnly = (params.viewportOnly as boolean) ?? true;\n const pruneLayout = (params.pruneLayout as boolean) ?? true;\n const snapshot = generateSnapshot(document.body, {\n maxDepth,\n viewportOnly,\n pruneLayout,\n refStore: getActiveRefStore(),\n });\n return { content: snapshot };\n }\n\n case \"query_all\": {\n // 查询所有匹配元素\n const selector = params.selector as string;\n if (!selector) return { content: \"缺少 selector 参数\" };\n return { content: queryAllElements(selector) };\n }\n\n default:\n return { content: `未知的页面信息动作: ${action}` };\n }\n } catch (err) {\n return {\n content: `页面信息操作 \"${action}\" 失败: ${err instanceof Error ? err.message : String(err)}`,\n details: { error: true, action },\n };\n }\n },\n };\n}\n","/**\n * Navigate Tool — 基于 Web API 的页面导航工具。\n *\n * 替代 Playwright 的 goto/goBack/goForward/reload。\n * 运行环境:浏览器 Content Script。\n *\n * 支持 5 种动作:\n * goto — 跳转到指定 URL\n * back — 浏览器后退\n * forward — 浏览器前进\n * reload — 刷新当前页面\n * scroll — 滚动页面到指定位置或元素\n */\nimport { Type } from \"@sinclair/typebox\";\nimport type { ToolDefinition, ToolCallResult } from \"../core/tool-registry.js\";\n\nexport function createNavigateTool(): ToolDefinition {\n return {\n name: \"navigate\",\n description: [\n \"Navigate the current page.\",\n \"Actions: goto (open URL), back, forward, reload, scroll (to position or element).\",\n ].join(\" \"),\n\n schema: Type.Object({\n action: Type.String({\n description: \"Navigation action: goto | back | forward | reload | scroll\",\n }),\n url: Type.Optional(Type.String({ description: \"URL for goto action\" })),\n selector: Type.Optional(\n Type.String({ description: \"CSS selector for scroll action (scrolls element into view)\" }),\n ),\n x: Type.Optional(Type.Number({ description: \"Horizontal scroll position (pixels)\" })),\n y: Type.Optional(Type.Number({ description: \"Vertical scroll position (pixels)\" })),\n }),\n\n execute: async (params): Promise<ToolCallResult> => {\n const action = params.action as string;\n\n try {\n switch (action) {\n case \"goto\": {\n // 跳转到指定 URL\n const url = params.url as string;\n if (!url) return { content: \"缺少 url 参数\" };\n\n // 支持相对路径和绝对路径\n window.location.href = url;\n return { content: `正在导航到 ${url}` };\n }\n\n case \"back\": {\n // 浏览器后退(等同于点击后退按钮)\n window.history.back();\n return { content: \"已后退\" };\n }\n\n case \"forward\": {\n // 浏览器前进\n window.history.forward();\n return { content: \"已前进\" };\n }\n\n case \"reload\": {\n // 刷新当前页面\n window.location.reload();\n return { content: \"正在刷新页面\" };\n }\n\n case \"scroll\": {\n // 滚动页面:优先滚动到元素,否则滚动到坐标\n const selector = params.selector as string | undefined;\n\n if (selector) {\n const el = document.querySelector(selector);\n if (!el) return { content: `未找到元素 \"${selector}\"` };\n el.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n return { content: `已滚动到元素 \"${selector}\"` };\n }\n\n const x = (params.x as number) ?? 0;\n const y = (params.y as number) ?? 0;\n window.scrollTo({ left: x, top: y, behavior: \"smooth\" });\n return { content: `已滚动到 (${x}, ${y})` };\n }\n\n default:\n return { content: `未知的导航动作: ${action}` };\n }\n } catch (err) {\n return {\n content: `导航操作 \"${action}\" 失败: ${err instanceof Error ? err.message : String(err)}`,\n details: { error: true, action },\n };\n }\n },\n };\n}\n","/**\n * Wait Tool — 基于 MutationObserver 的元素等待工具。\n *\n * 替代 Playwright 的 waitForSelector/waitForNavigation。\n * 运行环境:浏览器 Content Script。\n *\n * 支持 3 种动作:\n * wait_for_selector — 等待匹配选择器的元素出现\n * wait_for_hidden — 等待元素消失或隐藏\n * wait_for_text — 等待页面中出现指定文本\n */\nimport { Type } from \"@sinclair/typebox\";\nimport type { ToolDefinition, ToolCallResult } from \"../core/tool-registry.js\";\n\n/** 默认超时时间(毫秒) */\nconst DEFAULT_TIMEOUT = 10_000;\n\n/**\n * 通过 MutationObserver 等待元素出现。\n * 先检查元素是否已存在,不存在则监听 DOM 变化直到出现或超时。\n */\nfunction waitForSelector(selector: string, timeoutMs: number): Promise<Element> {\n return new Promise((resolve, reject) => {\n // 先检查是否已存在\n const existing = document.querySelector(selector);\n if (existing) {\n resolve(existing);\n return;\n }\n\n const timer = setTimeout(() => {\n observer.disconnect();\n reject(new Error(`等待 \"${selector}\" 超时 (${timeoutMs}ms)`));\n }, timeoutMs);\n\n const observer = new MutationObserver(() => {\n const el = document.querySelector(selector);\n if (el) {\n clearTimeout(timer);\n observer.disconnect();\n resolve(el);\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n });\n });\n}\n\n/**\n * 等待元素消失或变为不可见。\n */\nfunction waitForHidden(selector: string, timeoutMs: number): Promise<void> {\n return new Promise((resolve, reject) => {\n // 先检查是否已不存在或隐藏\n const existing = document.querySelector(selector);\n if (!existing) {\n resolve();\n return;\n }\n const style = window.getComputedStyle(existing);\n if (style.display === \"none\" || style.visibility === \"hidden\") {\n resolve();\n return;\n }\n\n const timer = setTimeout(() => {\n observer.disconnect();\n reject(new Error(`等待 \"${selector}\" 消失超时 (${timeoutMs}ms)`));\n }, timeoutMs);\n\n const observer = new MutationObserver(() => {\n const el = document.querySelector(selector);\n if (!el) {\n clearTimeout(timer);\n observer.disconnect();\n resolve();\n return;\n }\n const s = window.getComputedStyle(el);\n if (s.display === \"none\" || s.visibility === \"hidden\") {\n clearTimeout(timer);\n observer.disconnect();\n resolve();\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"style\", \"class\", \"hidden\"],\n });\n });\n}\n\n/**\n * 等待页面中出现指定文本。\n */\nfunction waitForText(text: string, timeoutMs: number): Promise<void> {\n return new Promise((resolve, reject) => {\n // 先检查是否已包含\n if (document.body.textContent?.includes(text)) {\n resolve();\n return;\n }\n\n const timer = setTimeout(() => {\n observer.disconnect();\n reject(new Error(`等待文本 \"${text}\" 出现超时 (${timeoutMs}ms)`));\n }, timeoutMs);\n\n const observer = new MutationObserver(() => {\n if (document.body.textContent?.includes(text)) {\n clearTimeout(timer);\n observer.disconnect();\n resolve();\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n });\n}\n\nexport function createWaitTool(): ToolDefinition {\n return {\n name: \"wait\",\n description: [\n \"Wait for DOM changes on the current page.\",\n \"Actions: wait_for_selector (element appears), wait_for_hidden (element disappears),\",\n \"wait_for_text (specific text appears in page).\",\n ].join(\" \"),\n\n schema: Type.Object({\n action: Type.String({\n description: \"Wait action: wait_for_selector | wait_for_hidden | wait_for_text\",\n }),\n selector: Type.Optional(\n Type.String({ description: \"CSS selector for wait_for_selector/wait_for_hidden\" }),\n ),\n text: Type.Optional(\n Type.String({ description: \"Text to wait for in wait_for_text\" }),\n ),\n timeout: Type.Optional(\n Type.Number({ description: \"Timeout in milliseconds (default: 10000)\" }),\n ),\n }),\n\n execute: async (params): Promise<ToolCallResult> => {\n const action = params.action as string;\n const timeoutMs = (params.timeout as number) ?? DEFAULT_TIMEOUT;\n\n try {\n switch (action) {\n case \"wait_for_selector\": {\n const selector = params.selector as string;\n if (!selector) return { content: \"缺少 selector 参数\" };\n await waitForSelector(selector, timeoutMs);\n return { content: `元素 \"${selector}\" 已出现` };\n }\n\n case \"wait_for_hidden\": {\n const selector = params.selector as string;\n if (!selector) return { content: \"缺少 selector 参数\" };\n await waitForHidden(selector, timeoutMs);\n return { content: `元素 \"${selector}\" 已消失` };\n }\n\n case \"wait_for_text\": {\n const text = params.text as string;\n if (!text) return { content: \"缺少 text 参数\" };\n await waitForText(text, timeoutMs);\n return { content: `文本 \"${text}\" 已出现` };\n }\n\n default:\n return { content: `未知的等待动作: ${action}` };\n }\n } catch (err) {\n return {\n content: `等待操作 \"${action}\" 失败: ${err instanceof Error ? err.message : String(err)}`,\n details: { error: true, action },\n };\n }\n },\n };\n}\n","/**\n * Evaluate Tool — 在页面上下文中执行任意 JavaScript 表达式。\n *\n * 替代 Playwright 的 page.evaluate()。\n * 运行环境:浏览器 Content Script。\n *\n * 这是最灵活的工具 — 当其他 tools 无法满足需求时,\n * AI 可以直接编写 JS 代码来操作页面。\n *\n * 支持 2 种动作:\n * evaluate — 执行 JS 表达式并返回结果\n * evaluate_handle — 执行 JS 并返回序列化的 DOM 信息\n */\nimport { Type } from \"@sinclair/typebox\";\nimport type { ToolDefinition, ToolCallResult } from \"../core/tool-registry.js\";\n\n/**\n * 安全执行 JS 表达式,捕获错误并序列化结果。\n */\nfunction safeEvaluate(expression: string): { result?: unknown; error?: string } {\n try {\n // 使用 Function 构造器代替 eval,避免污染当前作用域\n const fn = new Function(`\"use strict\"; return (${expression});`);\n const result = fn();\n return { result };\n } catch (err) {\n // 如果作为表达式失败,尝试作为语句块执行\n try {\n const fn = new Function(`\"use strict\"; ${expression}`);\n const result = fn();\n return { result };\n } catch (err2) {\n return { error: err2 instanceof Error ? err2.message : String(err2) };\n }\n }\n}\n\n/**\n * 将执行结果序列化为字符串(处理 DOM 元素、循环引用等)。\n */\nfunction serializeResult(value: unknown): string {\n if (value === undefined) return \"undefined\";\n if (value === null) return \"null\";\n\n // DOM 元素 → 返回 outerHTML 片段\n if (value instanceof Element) {\n const tag = value.tagName.toLowerCase();\n const id = value.id ? `#${value.id}` : \"\";\n const text = value.textContent?.trim().slice(0, 100) ?? \"\";\n return `<${tag}${id}> \"${text}\"`;\n }\n\n // NodeList / HTMLCollection → 逐个序列化\n if (value instanceof NodeList || value instanceof HTMLCollection) {\n const items = Array.from(value).map((el, i) => ` ${i}: ${serializeResult(el)}`);\n return `[${value.length} elements]\\n${items.join(\"\\n\")}`;\n }\n\n // 普通值 → JSON 序列化\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n}\n\nexport function createEvaluateTool(): ToolDefinition {\n return {\n name: \"evaluate\",\n description: [\n \"Execute JavaScript code in the current page context.\",\n \"Use this when other tools cannot accomplish the task.\",\n \"Can access document, window, and all page APIs.\",\n ].join(\" \"),\n\n schema: Type.Object({\n expression: Type.String({\n description:\n \"JavaScript expression or code block to execute. Has access to document, window, etc.\",\n }),\n }),\n\n execute: async (params): Promise<ToolCallResult> => {\n const expression = params.expression as string;\n if (!expression) return { content: \"缺少 expression 参数\" };\n\n const { result, error } = safeEvaluate(expression);\n\n if (error) {\n return {\n content: `JS 执行错误: ${error}`,\n details: { error: true, expression },\n };\n }\n\n return { content: serializeResult(result) };\n },\n };\n}\n","/**\n * RefStore — 快照 hash ID 与 DOM 元素的映射表。\n *\n * 快照生成时,根据元素的 DOM 路径 + 页面 URL 生成确定性 hash ID,\n * 同时保存 ID → Element 的映射。AI 使用 hash ID 作为 selector 定位元素,\n * 免去超长 XPath 路径,大幅减少 token 消耗。\n *\n * 优势:\n * - **确定性**:同一元素无论快照顺序,始终得到相同 ID\n * - **并发安全**:多次快照不会产生 ID 冲突\n * - **跨页面隔离**:URL hash 作为命名空间,不同页面元素 ID 互不碰撞\n *\n * 生命周期:每次 WebAgent.chat() 调用时创建,对话结束后清空。\n *\n * 使用方:\n * page-info-tool.ts — generateSnapshot() 写入映射\n * dom-tool.ts — queryElement() 读取映射\n * index.ts — WebAgent 持有实例,管理生命周期\n */\n\n/**\n * FNV-1a 32-bit hash — 简单高效的字符串散列。\n * 分布均匀,碰撞率低,适合生成短 ID。\n */\nfunction fnv1a(str: string): number {\n let h = 0x811c9dc5; // FNV offset basis\n for (let i = 0; i < str.length; i++) {\n h ^= str.charCodeAt(i);\n h = Math.imul(h, 0x01000193); // FNV prime\n }\n return h >>> 0; // 转为无符号 32-bit\n}\n\n/**\n * hash ID → DOM 元素的映射存储。\n *\n * - `set(el, path)` 由快照生成时调用,返回确定性 hash ID\n * - `get(id)` 由 dom-tool 查询时调用,根据 hash ID 取回元素\n * - `has(id)` 检查 ID 是否存在(用于 selector 类型判断)\n * - `clear()` 每次对话结束后清空\n */\nexport class RefStore {\n private map = new Map<string, Element>();\n /** 页面 URL 的 hash 前缀,用于跨页面命名空间隔离 */\n private urlKey: string;\n\n /**\n * @param url 当前页面 URL(可选)。传入后作为 hash 命名空间,\n * 使不同页面的相同 DOM 路径产生不同 ID。\n */\n constructor(url?: string) {\n this.urlKey = url ?? \"\";\n }\n\n /**\n * 注册一个元素,返回确定性 hash ID。\n * 相同 URL + path 始终产生相同 ID(并发安全)。\n *\n * @param el DOM 元素引用\n * @param path 元素的 XPath-like 路径(如 \"/body/div[1]/main/button\")\n */\n set(el: Element, path: string): string {\n const baseId = fnv1a(this.urlKey + path).toString(36);\n let id = baseId;\n // 极小概率碰撞处理:不同 path 映射到相同 hash 时追加后缀\n let suffix = 2;\n while (this.map.has(id) && this.map.get(id) !== el) {\n id = baseId + suffix++;\n }\n this.map.set(id, el);\n return id;\n }\n\n /**\n * 根据 hash ID 获取 DOM 元素。\n * 返回 Element 或 undefined(ID 不存在或元素已被移除)。\n */\n get(id: string): Element | undefined {\n return this.map.get(id);\n }\n\n /** 检查 hash ID 是否存在 */\n has(id: string): boolean {\n return this.map.has(id);\n }\n\n /** 清空所有映射 */\n clear(): void {\n this.map.clear();\n }\n\n /**\n * 重置映射表:清空所有映射,并可选更新 URL 命名空间。\n *\n * 用于页面导航后刷新 RefStore:旧的 hash ID → Element 映射已失效,\n * 需要用新 URL 重新生成确定性 hash。\n *\n * @param url 新的页面 URL(不传则保持原 URL 命名空间)\n */\n reset(url?: string): void {\n this.map.clear();\n if (url !== undefined) {\n this.urlKey = url;\n }\n }\n\n /** 当前映射数量 */\n get size(): number {\n return this.map.size;\n }\n}\n","/**\n * Web Tools 消息通信桥接层。\n *\n * 解决 Chrome Extension 的作用域隔离问题:\n *\n * Service Worker (后台) Content Script (页面)\n * ┌──────────────────┐ ┌──────────────────────┐\n * │ agent-core │ │ document / window │\n * │ tool-registry │ chrome.tabs │ │\n * │ │ .sendMessage() │ DOM 操作实际执行 │\n * │ tool.execute() │ ─────────────────► │ handleToolMessage() │\n * │ ↓ │ │ ↓ │\n * │ sendToContent() │ ◄───────────────── │ 返回执行结果 │\n * └──────────────────┘ response └──────────────────────┘\n *\n * 使用方式:\n * Service Worker 端:\n * import { createProxyExecutor } from \"./messaging.js\";\n * const execute = createProxyExecutor();\n * // execute 会把调用转发到 content script\n *\n * Content Script 端:\n * import { registerToolHandler } from \"./messaging.js\";\n * registerToolHandler(actualExecutors);\n * // 监听来自 service worker 的工具调用请求\n */\n\n// ─── 消息类型定义 ───\n\n/** Service Worker → Content Script 的工具调用请求 */\nexport type ToolCallMessage = {\n type: \"AUTOPILOT_TOOL_CALL\";\n toolName: string;\n params: Record<string, unknown>;\n callId: string;\n};\n\n/** Content Script → Service Worker 的工具调用结果 */\nexport type ToolCallResponse = {\n type: \"AUTOPILOT_TOOL_RESULT\";\n callId: string;\n result: {\n content: string | Record<string, unknown>;\n details?: Record<string, unknown>;\n };\n};\n\n// ─── Service Worker 端(发送方) ───\n\n/**\n * 创建代理执行器 — 在 Service Worker 端使用。\n *\n * 它不直接执行 DOM 操作,而是通过 chrome.tabs.sendMessage\n * 把调用请求发给当前活动 tab 的 content script 执行。\n *\n * @returns execute 函数,签名与 ToolDefinition.execute 相同\n */\nexport function createProxyExecutor() {\n return async (\n toolName: string,\n params: Record<string, unknown>,\n ): Promise<{ content: string | Record<string, unknown>; details?: Record<string, unknown> }> => {\n const callId = `${toolName}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n\n // 获取当前活动 tab\n const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });\n if (!tab?.id) {\n return { content: \"错误:没有活动的浏览器标签页\" };\n }\n\n // 发送消息到 content script 并等待结果\n const message: ToolCallMessage = {\n type: \"AUTOPILOT_TOOL_CALL\",\n toolName,\n params,\n callId,\n };\n\n try {\n const response = await chrome.tabs.sendMessage(tab.id, message) as ToolCallResponse;\n return response.result;\n } catch (err) {\n return {\n content: `工具调用失败(content script 可能未加载): ${err instanceof Error ? err.message : String(err)}`,\n details: { error: true, toolName },\n };\n }\n };\n}\n\n// ─── Content Script 端(接收方) ───\n\n/** 工具执行器映射:toolName → execute 函数 */\nexport type ToolExecutorMap = Map<\n string,\n (params: Record<string, unknown>) => Promise<{\n content: string | Record<string, unknown>;\n details?: Record<string, unknown>;\n }>\n>;\n\n/**\n * 在 Content Script 端注册工具执行处理器。\n *\n * 监听来自 Service Worker 的 AUTOPILOT_TOOL_CALL 消息,\n * 根据 toolName 找到对应的执行函数,执行后返回结果。\n *\n * @param executors 工具名称 → 执行函数的映射\n */\nexport function registerToolHandler(executors: ToolExecutorMap): void {\n chrome.runtime.onMessage.addListener(\n (message: unknown, _sender: chrome.runtime.MessageSender, sendResponse: (response: ToolCallResponse) => void) => {\n // 只处理我们的消息类型\n const msg = message as ToolCallMessage;\n if (msg?.type !== \"AUTOPILOT_TOOL_CALL\") return false;\n\n const executor = executors.get(msg.toolName);\n if (!executor) {\n sendResponse({\n type: \"AUTOPILOT_TOOL_RESULT\",\n callId: msg.callId,\n result: { content: `未知工具: ${msg.toolName}` },\n });\n return true; // 同步返回 true 表示我们会异步 sendResponse\n }\n\n // 异步执行工具并返回结果\n executor(msg.params)\n .then((result) => {\n sendResponse({\n type: \"AUTOPILOT_TOOL_RESULT\",\n callId: msg.callId,\n result,\n });\n })\n .catch((err) => {\n sendResponse({\n type: \"AUTOPILOT_TOOL_RESULT\",\n callId: msg.callId,\n result: {\n content: `工具 ${msg.toolName} 执行异常: ${err instanceof Error ? err.message : String(err)}`,\n details: { error: true },\n },\n });\n });\n\n return true; // 告诉 Chrome 我们会异步调用 sendResponse\n },\n );\n}\n","/**\n * WebAgent — 浏览器端 AI Agent 类。\n *\n * 封装了完整的 Agent 能力,可在浏览器中独立运行:\n * - 对话(chat) → 发消息、获取 AI 回复\n * - 工具注册 → 注册内置 Web 工具或自定义工具\n * - 决策循环 → 复用 core/agent-loop.ts 的通用逻辑\n * - AI 连接 → 复用 core/ai-client.ts(基于 fetch,跨平台)\n *\n * 使用示例:\n * ```ts\n * const agent = new WebAgent({ token: \"ghp_xxx\", provider: \"copilot\" });\n * agent.registerTools(); // 注册内置 Web 工具\n * agent.callbacks.onText = (text) => console.log(text);\n *\n * const result = await agent.chat(\"获取页面标题\");\n * console.log(result.reply);\n * ```\n *\n * 架构位置:\n * ┌──────────────────────────────────────────────────┐\n * │ WebAgent(浏览器端入口) │\n * │ ┌──────────┐ ┌────────────┐ ┌──────────────┐ │\n * │ │ core/ │ │ core/ │ │ web/ │ │\n * │ │ ai-client│ │ agent-loop │ │ (DOM/导航等)│ │\n * │ │ (fetch) │ │ (通用循环) │ │ │ │\n * │ └──────────┘ └────────────┘ └──────────────┘ │\n * └──────────────────────────────────────────────────┘\n */\nimport {\n executeAgentLoop,\n type AgentLoopCallbacks,\n type AgentLoopResult,\n} from \"../core/agent-loop/index.js\";\nimport type { AIMessage } from \"../core/types.js\";\nimport { createAIClient } from \"../core/ai-client/index.js\";\nimport type { AIClient } from \"../core/types.js\";\nimport { ToolRegistry, type ToolDefinition } from \"../core/tool-registry.js\";\nimport { buildSystemPrompt } from \"../core/system-prompt.js\";\nimport { generateSnapshot, type SnapshotOptions } from \"./page-info-tool.js\";\nimport { createDomTool, setActiveRefStore } from \"./dom-tool.js\";\nimport { createNavigateTool } from \"./navigate-tool.js\";\nimport { createPageInfoTool } from \"./page-info-tool.js\";\nimport { createWaitTool } from \"./wait-tool.js\";\nimport { createEvaluateTool } from \"./evaluate-tool.js\";\nimport { RefStore } from \"./ref-store.js\";\n\n// ─── 回调类型 ───\n\n/** WebAgent 事件回调(扩展 AgentLoopCallbacks,增加快照事件) */\nexport type WebAgentCallbacks = AgentLoopCallbacks & {\n /** 自动快照生成完成时触发 */\n onSnapshot?: (snapshot: string) => void;\n};\n\n// ─── 配置 ───\n\nexport type WebAgentOptions = {\n /**\n * 自定义 AI 客户端实例(可选)。\n *\n * 传入后将直接使用该实例进行对话,忽略 token / provider / model / baseURL。\n * 支持 BaseAIClient 或任何实现 AIClient 接口的对象。\n *\n * ```ts\n * const client = new BaseAIClient({ chatHandler: async (params) => { ... } });\n * const agent = new WebAgent({ client });\n * ```\n */\n client?: AIClient;\n /** API 认证 Token (GitHub PAT / OpenAI key / Anthropic key) */\n token?: string;\n /** AI 提供商: \"copilot\" | \"openai\" | \"anthropic\"(默认 \"copilot\") */\n provider?: string;\n /** 模型名称(默认 \"gpt-4o\") */\n model?: string;\n /** 自定义 API 基础 URL(可选,覆盖 provider 默认值) */\n baseURL?: string;\n /** 是否启用干运行模式 */\n dryRun?: boolean;\n /** 自定义系统提示词(不传则使用默认 Web 提示词) */\n systemPrompt?: string;\n /** 最大工具调用轮次(默认 10) */\n maxRounds?: number;\n /** 是否启用多轮对话记忆(默认 false) */\n memory?: boolean;\n /** 是否在每次对话前自动生成页面快照(默认 true) */\n autoSnapshot?: boolean;\n /** 快照选项(视口裁剪、智能剪枝等,autoSnapshot 开启时生效) */\n snapshotOptions?: SnapshotOptions;\n};\n\n// ─── WebAgent 类 ───\n\nexport class WebAgent {\n /** 用户传入的自定义 AI 客户端实例(优先级高于 token/provider) */\n private client?: AIClient;\n private token: string;\n private provider: string;\n private model: string;\n private baseURL?: string;\n private dryRun: boolean;\n private maxRounds: number;\n private customSystemPrompt?: string;\n\n /** 多轮对话记忆开关 */\n private memory: boolean;\n /** 对话历史(memory 开启时自动累积) */\n private history: AIMessage[] = [];\n /** 自动快照开关 */\n private autoSnapshot: boolean;\n /** 快照选项 */\n private snapshotOptions: SnapshotOptions;\n\n /** 工具注册表实例 — 每个 WebAgent 拥有独立的工具集 */\n private registry = new ToolRegistry();\n\n /** 事件回调 — 绑定后可实时获取 Agent 进度,用于 UI 展示 */\n callbacks: WebAgentCallbacks = {};\n\n constructor(options: WebAgentOptions) {\n this.client = options.client;\n this.token = options.token || \"\";\n this.provider = options.provider ?? \"copilot\";\n this.model = options.model ?? \"gpt-4o\";\n this.baseURL = options.baseURL;\n this.dryRun = options.dryRun ?? false;\n this.maxRounds = options.maxRounds ?? 10;\n this.customSystemPrompt = options.systemPrompt;\n this.memory = options.memory ?? false;\n this.autoSnapshot = options.autoSnapshot ?? true;\n this.snapshotOptions = options.snapshotOptions ?? {};\n }\n\n // ─── 工具管理 ───\n\n /** 注册所有内置 Web 工具(dom, navigate, page_info, wait, evaluate) */\n registerTools(): void {\n this.registry.register(createDomTool());\n this.registry.register(createNavigateTool());\n this.registry.register(createPageInfoTool());\n this.registry.register(createWaitTool());\n this.registry.register(createEvaluateTool());\n }\n\n /** 注册一个自定义工具 */\n registerTool(tool: ToolDefinition): void {\n this.registry.register(tool);\n }\n\n /** 获取所有已注册的工具定义列表 */\n getTools(): ToolDefinition[] {\n return this.registry.getDefinitions();\n }\n\n // ─── 配置修改 ───\n\n /** 设置 API Token */\n setToken(token: string): void {\n this.token = token;\n }\n\n /**\n * 设置自定义 AI 客户端实例。\n *\n * 传入后将优先使用该实例进行对话,忽略 token / provider / model / baseURL。\n * 传入 undefined 可恢复使用内置客户端。\n */\n setClient(client: AIClient | undefined): void {\n this.client = client;\n }\n\n /** 设置 AI 提供商 */\n setProvider(provider: string): void {\n this.provider = provider;\n }\n\n /** 设置模型 */\n setModel(model: string): void {\n this.model = model;\n }\n\n /** 切换干运行模式 */\n setDryRun(enabled: boolean): void {\n this.dryRun = enabled;\n }\n\n /** 设置自定义系统提示词 */\n setSystemPrompt(prompt: string): void {\n this.customSystemPrompt = prompt;\n }\n\n /** 开启或关闭多轮对话记忆 */\n setMemory(enabled: boolean): void {\n this.memory = enabled;\n if (!enabled) this.history = [];\n }\n\n /** 获取当前记忆开关状态 */\n getMemory(): boolean {\n return this.memory;\n }\n\n /** 开启或关闭自动快照 */\n setAutoSnapshot(enabled: boolean): void {\n this.autoSnapshot = enabled;\n }\n\n /** 获取当前自动快照开关状态 */\n getAutoSnapshot(): boolean {\n return this.autoSnapshot;\n }\n\n /** 设置快照选项(视口裁剪、智能剪枝等) */\n setSnapshotOptions(options: SnapshotOptions): void {\n this.snapshotOptions = options;\n }\n\n /** 获取当前快照选项 */\n getSnapshotOptions(): SnapshotOptions {\n return { ...this.snapshotOptions };\n }\n\n /** 清空对话历史(不影响记忆开关) */\n clearHistory(): void {\n this.history = [];\n }\n\n // ─── 核心能力 ───\n\n /**\n * 发送消息并获取 AI 回复(含完整工具调用循环)。\n *\n * 内部流程(全部复用 core):\n * 1. createAIClient() → 创建 fetch AI 客户端\n * 2. buildSystemPrompt() → 构建系统提示词\n * 3. executeAgentLoop() → 执行决策循环\n * 4. callbacks → 实时通知 UI\n */\n async chat(message: string): Promise<AgentLoopResult> {\n // 优先使用自定义 client,否则使用内置 createAIClient\n const client = this.client ?? this.createBuiltinClient();\n\n // 复用 core/system-prompt 或使用自定义\n let systemPrompt =\n this.customSystemPrompt ??\n buildSystemPrompt({ tools: this.registry.getDefinitions() });\n\n // ─── 自动快照:注入 system prompt,不污染对话历史 ───\n // 创建本次对话的 RefStore,快照结束后保持活跃,对话结束后清空\n const refStore = new RefStore(globalThis.location?.href);\n setActiveRefStore(refStore);\n\n if (this.autoSnapshot) {\n try {\n const snapshot = generateSnapshot(document.body, {\n maxDepth: 8,\n ...this.snapshotOptions,\n refStore,\n });\n this.callbacks.onSnapshot?.(snapshot);\n\n systemPrompt += `\\n\\n## 当前页面 DOM 快照\\n\\n\\`\\`\\`\\n${snapshot}\\n\\`\\`\\``;\n } catch {\n // 快照失败不阻塞正常流程\n }\n }\n\n // 包装回调:在恢复快照前重置 RefStore,确保新快照的 hash ID 有效\n const wrappedCallbacks: WebAgentCallbacks = {\n ...this.callbacks,\n onBeforeRecoverySnapshot: (newUrl?: string) => {\n // URL 变化 → 清空映射 + 更新 URL 命名空间\n // 元素定位失败 → 仅清空可能失效的映射(URL 不变)\n if (newUrl !== undefined) {\n refStore.reset(newUrl);\n } else {\n refStore.clear();\n }\n // 转发到用户回调(如有设置)\n this.callbacks.onBeforeRecoverySnapshot?.(newUrl);\n },\n };\n\n // 复用 core/agent-loop — 同一份决策循环\n const result = await executeAgentLoop({\n client,\n registry: this.registry,\n systemPrompt,\n message,\n history: this.memory ? this.history : undefined,\n dryRun: this.dryRun,\n maxRounds: this.maxRounds,\n callbacks: wrappedCallbacks,\n });\n\n // 记忆模式:累积对话历史供下次 chat() 使用\n if (this.memory) {\n this.history = result.messages;\n }\n\n // 对话结束,清空 RefStore\n refStore.clear();\n setActiveRefStore(undefined);\n\n return result;\n }\n\n // ─── 内部方法 ───\n\n /**\n * 创建内置 AI 客户端(基于 token / provider / model 配置)。\n *\n * @throws 未设置 token 时抛出 Error\n */\n private createBuiltinClient(): AIClient {\n if (!this.token) {\n throw new Error(\"未设置 Token,请先调用 setToken() 或传入自定义 client\");\n }\n return createAIClient({\n provider: this.provider,\n model: this.model,\n apiKey: this.token,\n baseURL: this.baseURL,\n });\n }\n}\n\n// ─── Re-exports ───\n// 从入口文件统一导出所有公共 API,消费方只需 import from \"agentpage\"\n\nexport { generateSnapshot, type SnapshotOptions } from \"./page-info-tool.js\";\nexport { createDomTool } from \"./dom-tool.js\";\nexport { createNavigateTool } from \"./navigate-tool.js\";\nexport { createPageInfoTool } from \"./page-info-tool.js\";\nexport { createWaitTool } from \"./wait-tool.js\";\nexport { createEvaluateTool } from \"./evaluate-tool.js\";\nexport {\n createProxyExecutor,\n registerToolHandler,\n type ToolCallMessage,\n type ToolCallResponse,\n type ToolExecutorMap,\n} from \"./messaging.js\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["sleep","sleep","convertMessages"],"sources":["../src/core/agent-loop/constants.ts","../src/core/agent-loop/helpers.ts","../src/core/agent-loop/index.ts","../src/core/ai-client/constants.ts","../src/core/ai-client/custom.ts","../src/core/ai-client/openai.ts","../src/core/ai-client/anthropic.ts","../src/core/ai-client/index.ts","../src/core/tool-registry.ts","../src/core/system-prompt.ts","../src/web/dom-tool.ts","../src/web/page-info-tool.ts","../src/web/navigate-tool.ts","../src/web/wait-tool.ts","../src/web/evaluate-tool.ts","../src/web/ref-store.ts","../src/web/messaging.ts","../src/web/index.ts"],"sourcesContent":["/**\n * Agent Loop 默认配置常量。\n *\n * 统一集中在该文件,避免在主循环中散落“魔法数字”。\n */\nexport const DEFAULT_MAX_ROUNDS = 10;\nexport const DEFAULT_RECOVERY_WAIT_MS = 300;\nexport const DEFAULT_ACTION_RECOVERY_ROUNDS = 5;\n","/**\n * Agent Loop 辅助函数集合。\n *\n * 该文件只放“纯辅助逻辑”:格式化、判定、上下文读取、等待等,\n * 让 `agent-loop.ts` 专注于流程编排。\n */\nimport type { ToolCallResult } from \"../tool-registry.js\";\nimport { ToolRegistry } from \"../tool-registry.js\";\nimport {\n DEFAULT_RECOVERY_WAIT_MS,\n} from \"./constants.js\";\n\n/** 单次工具执行轨迹条目(用于恢复提示和调试展示)。 */\nexport type ToolTraceEntry = {\n round: number;\n name: string;\n input: unknown;\n result: ToolCallResult;\n marker?: string;\n};\n\n/** 异步睡眠,确保恢复重试按顺序串行执行。 */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** 将工具返回内容统一转为字符串,便于拼接进消息。 */\nexport function toContentString(content: ToolCallResult[\"content\"]): string {\n return typeof content === \"string\" ? content : JSON.stringify(content, null, 2);\n}\n\n/** 判定工具失败是否属于“元素不存在”,用于触发快照恢复。 */\nexport function isElementNotFoundResult(result: ToolCallResult): boolean {\n const details = result.details;\n if (details && typeof details === \"object\") {\n const code = (details as { code?: unknown }).code;\n if (code === \"ELEMENT_NOT_FOUND\") return true;\n }\n\n const content = toContentString(result.content);\n return content.includes(\"未找到\") && content.includes(\"元素\");\n}\n\n/** 为同一动作构造稳定 key,用于统计恢复重试次数。 */\nexport function buildToolCallKey(name: string, input: unknown): string {\n return `${name}:${JSON.stringify(input)}`;\n}\n\n/**\n * 解析恢复等待时长:\n * - 优先 `waitMs`\n * - 其次 `waitSeconds`\n * - 最后回退默认值\n */\nexport function resolveRecoveryWaitMs(input: unknown): number {\n if (!input || typeof input !== \"object\") return DEFAULT_RECOVERY_WAIT_MS;\n\n const params = input as Record<string, unknown>;\n const waitMs = params.waitMs;\n if (typeof waitMs === \"number\" && Number.isFinite(waitMs)) {\n return Math.max(0, Math.floor(waitMs));\n }\n\n const waitSeconds = params.waitSeconds;\n if (typeof waitSeconds === \"number\" && Number.isFinite(waitSeconds)) {\n return Math.max(0, Math.floor(waitSeconds * 1000));\n }\n\n return DEFAULT_RECOVERY_WAIT_MS;\n}\n\n/** 将工具输入压缩成简短文本,用于轨迹展示。 */\nfunction formatToolInputBrief(input: unknown): string {\n if (!input || typeof input !== \"object\") return \"\";\n\n const params = input as Record<string, unknown>;\n const parts: string[] = [];\n\n for (const key of [\"action\", \"selector\", \"waitMs\", \"waitSeconds\", \"url\", \"text\"]) {\n const value = params[key];\n if (value === undefined || value === null) continue;\n if (typeof value === \"string\") {\n parts.push(`${key}=${JSON.stringify(value).slice(0, 80)}`);\n } else if (typeof value === \"number\" || typeof value === \"boolean\") {\n parts.push(`${key}=${String(value)}`);\n }\n }\n\n if (parts.length === 0) return \"\";\n return ` (${parts.join(\", \")})`;\n}\n\n/**\n * 将完整轨迹格式化为可读文本。\n * 支持附加“当前步骤”用于在恢复提示中高亮失败动作。\n */\nexport function buildToolTrace(\n trace: ToolTraceEntry[],\n current?: {\n round: number;\n name: string;\n input: unknown;\n result?: ToolCallResult;\n marker?: string;\n },\n): string {\n const lines = trace.map((entry, index) => {\n const code =\n entry.result.details && typeof entry.result.details === \"object\"\n ? (entry.result.details as { code?: unknown }).code\n : undefined;\n const codeText = typeof code === \"string\" ? ` [${code}]` : \"\";\n const marker = entry.marker ? ` ${entry.marker}` : \"\";\n return `${index + 1}. [round ${entry.round}] ${entry.name}${formatToolInputBrief(entry.input)}${codeText}${marker}`;\n });\n\n if (current) {\n const code =\n current.result?.details && typeof current.result.details === \"object\"\n ? (current.result.details as { code?: unknown }).code\n : undefined;\n const codeText = typeof code === \"string\" ? ` [${code}]` : \"\";\n const marker = current.marker ? ` ${current.marker}` : \"\";\n lines.push(\n `${lines.length + 1}. [round ${current.round}] ${current.name}${formatToolInputBrief(current.input)}${codeText}${marker}`,\n );\n }\n\n return lines.length > 0 ? lines.join(\"\\n\") : \"(暂无工具执行记录)\";\n}\n\n/** 从工具参数中读取 action。 */\nexport function getToolAction(input: unknown): string | undefined {\n if (!input || typeof input !== \"object\") return undefined;\n const action = (input as Record<string, unknown>).action;\n return typeof action === \"string\" ? action : undefined;\n}\n\n/** 判定工具结果是否标记 error。 */\nexport function hasToolError(result: ToolCallResult): boolean {\n return result.details && typeof result.details === \"object\"\n ? Boolean((result.details as { error?: unknown }).error)\n : false;\n}\n\n/** 读取当前页面 URL(通过 page_info 工具)。 */\nexport async function readPageUrl(\n registry: ToolRegistry,\n): Promise<string | undefined> {\n const result = await registry.dispatch(\"page_info\", { action: \"get_url\" });\n return typeof result.content === \"string\" ? result.content : undefined;\n}\n\n/** 读取当前页面快照(通过 page_info 工具)。 */\nexport async function readPageSnapshot(\n registry: ToolRegistry,\n maxDepth = 8,\n): Promise<string> {\n const result = await registry.dispatch(\"page_info\", {\n action: \"snapshot\",\n maxDepth,\n });\n return toContentString(result.content);\n}\n","/**\n * Agent Loop — 环境无关的 AI 决策循环。\n *\n * 核心 Tool-Use Loop,纯 TypeScript 实现:\n *\n * 消息 → AI 思考 → 需要工具?──是──→ 执行工具 → 反馈结果 → 继续思考\n * │\n * 否\n * ↓\n * 返回最终回复\n *\n * 使用方:WebAgent.chat() 调用\n *\n * 依赖关系(全部环境无关):\n * - types.ts → 类型定义(import type,零运行时)\n * - tool-registry.ts → ToolRegistry 实例(注入式,非全局)\n */\nimport type { AIClient, AIMessage } from \"../types.js\";\nimport { ToolRegistry, type ToolCallResult } from \"../tool-registry.js\";\nimport {\n DEFAULT_ACTION_RECOVERY_ROUNDS,\n DEFAULT_MAX_ROUNDS,\n} from \"./constants.js\";\nimport {\n buildToolCallKey,\n buildToolTrace,\n getToolAction,\n hasToolError,\n isElementNotFoundResult,\n readPageSnapshot,\n readPageUrl,\n resolveRecoveryWaitMs,\n sleep,\n toContentString,\n type ToolTraceEntry,\n} from \"./helpers.js\";\n\n// ─── 回调接口 ───\n\n/** 工具调用事件回调 — 用于 UI 层实时展示 Agent 进度 */\nexport type AgentLoopCallbacks = {\n /** AI 返回文本回复时触发 */\n onText?: (text: string) => void;\n /** AI 请求调用工具时触发(执行前) */\n onToolCall?: (name: string, input: unknown) => void;\n /** 工具执行完成时触发 */\n onToolResult?: (name: string, result: ToolCallResult) => void;\n /** 每轮循环开始时触发(round 从 0 开始) */\n onRound?: (round: number) => void;\n /**\n * 恢复快照生成前触发(页面 URL 变化或元素定位失败时)。\n *\n * 用于 WebAgent 重置 RefStore(清空旧的 hash ID → Element 映射,\n * 用新 URL 重新生成确定性 hash),确保恢复快照中的 ID 有效。\n *\n * @param newUrl 当前页面 URL(URL 变化时传入;元素定位失败时为 undefined)\n */\n onBeforeRecoverySnapshot?: (newUrl?: string) => void;\n};\n\n// ─── 参数与结果 ───\n\nexport type AgentLoopParams = {\n /** AI 客户端实例(基于 fetch 的客户端) */\n client: AIClient;\n /** 工具注册表实例(由调用方创建并注册好工具) */\n registry: ToolRegistry;\n /** 系统提示词(由调用方构建,适配各自环境) */\n systemPrompt: string;\n /** 用户消息 */\n message: string;\n /** 历史对话消息(用于多轮记忆,按时间顺序排列) */\n history?: AIMessage[];\n /** 干运行模式:打印工具调用但不执行 */\n dryRun?: boolean;\n /** 最大工具调用轮次(默认 10) */\n maxRounds?: number;\n /** 事件回调 */\n callbacks?: AgentLoopCallbacks;\n};\n\nexport type AgentLoopResult = {\n /** AI 的最终文本回复 */\n reply: string;\n /** 所有工具调用记录 */\n toolCalls: Array<{ name: string; input: unknown; result: ToolCallResult }>;\n /** 本轮完整对话消息(含历史 + 本轮,用于多轮记忆累积) */\n messages: AIMessage[];\n};\n\ntype PageContextState = {\n currentUrl?: string;\n latestSnapshot?: string;\n needsSnapshotBeforeDom: boolean;\n};\n\n/**\n * 执行 Agent 决策循环(环境无关)。\n *\n * 完整流程:\n * 1. 获取已注册的工具列表\n * 2. 循环:发消息给 AI → 检查是否返回 tool_call → 执行 → 反馈 → 继续\n * 3. AI 不再调用工具时,返回最终回复\n */\nexport async function executeAgentLoop(\n params: AgentLoopParams,\n): Promise<AgentLoopResult> {\n const {\n client,\n registry,\n systemPrompt,\n message,\n history,\n dryRun = false,\n maxRounds = DEFAULT_MAX_ROUNDS,\n callbacks,\n } = params;\n\n const tools = registry.getDefinitions();\n // 将历史消息(如有)放在当前用户消息之前,实现多轮记忆\n const messages: AIMessage[] = [\n ...(history ?? []),\n { role: \"user\", content: message },\n ];\n const allToolCalls: AgentLoopResult[\"toolCalls\"] = [];\n const fullToolTrace: ToolTraceEntry[] = [];\n const actionRecoveryAttempts = new Map<string, number>();\n const pageContext: PageContextState = {\n needsSnapshotBeforeDom: false,\n };\n let finalReply = \"\";\n\n for (let round = 0; round < maxRounds; round++) {\n callbacks?.onRound?.(round);\n\n // 调用 AI(发送系统提示 + 对话历史 + 可用工具列表)\n const response = await client.chat({ systemPrompt, messages, tools });\n\n // 没有工具调用 → 循环结束,拿到最终回复\n if (!response.toolCalls || response.toolCalls.length === 0) {\n finalReply = response.text ?? \"\";\n if (finalReply) callbacks?.onText?.(finalReply);\n break;\n }\n\n // 有文本伴随工具调用 → 先通知\n if (response.text) callbacks?.onText?.(response.text);\n\n // ─── Dry-run 模式 ───\n if (dryRun) {\n finalReply = response.text ? response.text + \"\\n\\n\" : \"\";\n finalReply += \"🔧 AI 请求调用以下工具(dry-run 模式,未执行):\\n\";\n for (const tc of response.toolCalls) {\n callbacks?.onToolCall?.(tc.name, tc.input);\n finalReply += `\\n┌─ 工具: ${tc.name}\\n`;\n finalReply += `│ ID: ${tc.id}\\n`;\n finalReply += `│ 参数:\\n`;\n const inputStr = JSON.stringify(tc.input, null, 2);\n for (const line of inputStr.split(\"\\n\")) {\n finalReply += `│ ${line}\\n`;\n }\n finalReply += `└────────────────────\\n`;\n }\n break;\n }\n\n // ─── 执行工具调用 ───\n const toolResults: Array<{ toolCallId: string; result: string }> = [];\n\n for (const tc of response.toolCalls) {\n callbacks?.onToolCall?.(tc.name, tc.input);\n\n const latestUrl = await readPageUrl(registry);\n if (latestUrl) {\n if (!pageContext.currentUrl) {\n pageContext.currentUrl = latestUrl;\n } else if (latestUrl !== pageContext.currentUrl) {\n pageContext.currentUrl = latestUrl;\n pageContext.needsSnapshotBeforeDom = true;\n }\n }\n\n if (tc.name === \"dom\" && pageContext.needsSnapshotBeforeDom) {\n // 重置 RefStore:清空旧映射 + 更新 URL 命名空间,确保新快照 ID 有效\n callbacks?.onBeforeRecoverySnapshot?.(pageContext.currentUrl);\n const snapshotText = await readPageSnapshot(registry, 8);\n pageContext.latestSnapshot = snapshotText;\n pageContext.needsSnapshotBeforeDom = false;\n\n const result: ToolCallResult = {\n content: [\n `检测到页面 URL 变化:${pageContext.currentUrl ?? \"(未知)\"}`,\n \"已在执行 DOM 操作前生成最新快照,请基于该快照重新定位目标元素后重试当前工具调用。\",\n \"\",\n \"本次对话任务完整工具轨迹:\",\n buildToolTrace(fullToolTrace, {\n round,\n name: tc.name,\n input: tc.input,\n marker: \"[URL变化待重定位]\",\n }),\n \"\",\n \"最新页面快照:\",\n snapshotText,\n ].join(\"\\n\"),\n details: {\n error: true,\n code: \"URL_CHANGED_REQUIRE_NEW_SNAPSHOT\",\n url: pageContext.currentUrl,\n },\n };\n\n allToolCalls.push({ name: tc.name, input: tc.input, result });\n fullToolTrace.push({\n round,\n name: tc.name,\n input: tc.input,\n result,\n marker: \"[URL变化待重定位]\",\n });\n callbacks?.onToolResult?.(tc.name, result);\n toolResults.push({\n toolCallId: tc.id,\n result: toContentString(result.content),\n });\n continue;\n }\n\n let result = await registry.dispatch(tc.name, tc.input);\n\n if (tc.name === \"dom\" && isElementNotFoundResult(result)) {\n const key = buildToolCallKey(tc.name, tc.input);\n const attempts = (actionRecoveryAttempts.get(key) ?? 0) + 1;\n actionRecoveryAttempts.set(key, attempts);\n const recoveryWaitMs = resolveRecoveryWaitMs(tc.input);\n\n if (attempts <= DEFAULT_ACTION_RECOVERY_ROUNDS) {\n await sleep(recoveryWaitMs);\n\n // 重置 RefStore:清空可能失效的映射,确保恢复快照 ID 有效\n callbacks?.onBeforeRecoverySnapshot?.();\n const snapshotText = await readPageSnapshot(registry, 8);\n pageContext.latestSnapshot = snapshotText;\n const originalError = toContentString(result.content);\n const fullTrace = buildToolTrace(fullToolTrace, {\n round,\n name: tc.name,\n input: tc.input,\n result,\n marker: \"[当前失败]\",\n });\n\n result = {\n content: [\n originalError,\n \"\",\n `自动恢复 ${attempts}/${DEFAULT_ACTION_RECOVERY_ROUNDS}:等待 ${recoveryWaitMs}ms 后重新获取页面快照。`,\n \"本次对话任务完整工具轨迹(含本次失败):\",\n fullTrace,\n \"请根据下方最新快照,重新定位本次操作目标元素并再次调用工具。\",\n \"\",\n \"最新页面快照:\",\n snapshotText,\n ].join(\"\\n\"),\n details: {\n error: true,\n code: \"ELEMENT_NOT_FOUND_RECOVERY\",\n recoveryAttempt: attempts,\n recoveryMaxRounds: DEFAULT_ACTION_RECOVERY_ROUNDS,\n waitMs: recoveryWaitMs,\n },\n };\n } else {\n const originalError = toContentString(result.content);\n const fullTrace = buildToolTrace(fullToolTrace, {\n round,\n name: tc.name,\n input: tc.input,\n result,\n marker: \"[超过恢复上限]\",\n });\n result = {\n content: [\n originalError,\n \"\",\n `已达到最大自动恢复次数(${DEFAULT_ACTION_RECOVERY_ROUNDS})。请根据当前页面状态调整操作目标后重试。`,\n \"本次对话任务完整工具轨迹:\",\n fullTrace,\n ].join(\"\\n\"),\n details: {\n error: true,\n code: \"ELEMENT_NOT_FOUND_MAX_RECOVERY_REACHED\",\n recoveryAttempt: attempts,\n recoveryMaxRounds: DEFAULT_ACTION_RECOVERY_ROUNDS,\n waitMs: recoveryWaitMs,\n },\n };\n }\n }\n\n allToolCalls.push({ name: tc.name, input: tc.input, result });\n fullToolTrace.push({ round, name: tc.name, input: tc.input, result });\n\n if (tc.name === \"navigate\") {\n const action = getToolAction(tc.input);\n if (\n action === \"goto\" ||\n action === \"back\" ||\n action === \"forward\" ||\n action === \"reload\"\n ) {\n if (!hasToolError(result)) {\n pageContext.needsSnapshotBeforeDom = true;\n }\n }\n }\n\n callbacks?.onToolResult?.(tc.name, result);\n\n toolResults.push({\n toolCallId: tc.id,\n result:\n typeof result.content === \"string\"\n ? result.content\n : JSON.stringify(result.content),\n });\n }\n\n // 将 AI 回复(含 tool_call)和工具结果追加到对话历史\n messages.push({\n role: \"assistant\",\n content: response.text ?? \"\",\n toolCalls: response.toolCalls,\n });\n messages.push({\n role: \"tool\",\n content: toolResults,\n });\n // → 回到循环顶部,AI 根据工具结果继续思考\n }\n\n // 如果有最终回复但尚未作为 assistant 消息加入历史,补充进去\n if (finalReply) {\n messages.push({ role: \"assistant\", content: finalReply });\n }\n\n return { reply: finalReply, toolCalls: allToolCalls, messages };\n}\n","/**\n * AI Client 常量与共享工具函数。\n *\n * 集中管理各 Provider 的端点映射、Schema 清理等通用逻辑。\n * 被 openai.ts / anthropic.ts / 主入口 ai-client.ts 共同依赖。\n */\nimport type { AIClientConfig } from \"./index.js\";\n\n// ─── Provider 端点映射 ───\n\n/**\n * 各 Provider 的默认 API 端点。\n *\n * - openai → OpenAI 官方 API\n * - copilot → GitHub Models API(使用 OpenAI 兼容格式)\n * - anthropic → Anthropic Messages API\n */\nexport const PROVIDER_ENDPOINTS: Record<string, string> = {\n openai: \"https://api.openai.com/v1\",\n copilot: \"https://models.inference.ai.azure.com\",\n anthropic: \"https://api.anthropic.com\",\n};\n\n// ─── 共享工具函数 ───\n\n/**\n * 校验 provider 是否受支持。\n *\n * @throws 不支持的 provider 抛出 Error,附带支持列表\n */\nexport function validateProvider(provider: string): void {\n if (!PROVIDER_ENDPOINTS[provider]) {\n const supported = Object.keys(PROVIDER_ENDPOINTS).join(\", \");\n throw new Error(\n `Unknown AI provider: ${provider}. Supported: ${supported}`,\n );\n }\n}\n\n/**\n * 解析 provider 对应的 API 基础 URL。\n *\n * 优先使用用户自定义的 baseURL(如本地 Ollama),\n * 其次使用 PROVIDER_ENDPOINTS 中的默认值。\n */\nexport function resolveBaseURL(config: AIClientConfig): string {\n return config.baseURL ?? PROVIDER_ENDPOINTS[config.provider] ?? \"\";\n}\n\n/**\n * 清理 TypeBox Schema — 去除 Symbol 等不可序列化的属性。\n *\n * TypeBox 的 Type.Object() 产物包含 Symbol key(如 [Kind]、[Hint]),\n * 这些 Symbol 在 JSON.stringify 时会被忽略,但某些 AI API 端点\n * 对 JSON Schema 做严格校验时可能报错。\n *\n * 通过 JSON roundtrip(stringify → parse)清理掉所有不可序列化的属性。\n */\nexport function cleanSchema(schema: unknown): unknown {\n return JSON.parse(JSON.stringify(schema));\n}\n","/**\n * BaseAIClient — 可继承的 AI 客户端基类。\n *\n * 提供 `AIClient` 接口的类实现,用户可以:\n * 1. 直接实例化 — 传入自定义 `chatHandler` 回调,完全控制对话逻辑\n * 2. 继承扩展 — 覆盖 `chat()` 方法,实现自定义 AI 对接\n *\n * 使用场景:\n * - 对接非标准 AI API(如私有部署的模型服务)\n * - 添加请求拦截(日志、重试、缓存、限流等中间件逻辑)\n * - 对接本地模型(Ollama、llama.cpp 等)\n * - 测试 Mock(注入固定响应进行单元测试)\n *\n * 使用示例:\n *\n * ```ts\n * // 方式一:直接实例化 + chatHandler\n * const client = new BaseAIClient({\n * chatHandler: async (params) => {\n * const res = await fetch(\"https://my-api.com/chat\", {\n * method: \"POST\",\n * headers: { \"Content-Type\": \"application/json\" },\n * body: JSON.stringify(params),\n * });\n * const data = await res.json();\n * return { text: data.reply };\n * },\n * });\n *\n * // 方式二:继承扩展\n * class MyAIClient extends BaseAIClient {\n * async chat(params) {\n * // 添加自定义逻辑(日志、重试等)\n * console.log(\"Sending:\", params.messages.length, \"messages\");\n * const response = await super.chat(params);\n * console.log(\"Received:\", response.text?.length, \"chars\");\n * return response;\n * }\n * }\n *\n * // 传入 WebAgent\n * const agent = new WebAgent({ client: new MyAIClient({ chatHandler }) });\n * ```\n *\n * 文件位置:\n * ai-client/custom.ts ←── ai-client/index.ts(re-export)\n * ←── web/index.ts(WebAgent 接受 client 选项)\n */\nimport type { AIChatResponse, AIClient, AIMessage } from \"../types.js\";\nimport type { ToolDefinition } from \"../tool-registry.js\";\n\n// ─── 类型定义 ───\n\n/** chat 方法的入参(与 AIClient.chat 签名一致) */\nexport type ChatHandlerParams = {\n /** 系统提示词 */\n systemPrompt: string;\n /** 对话消息列表 */\n messages: AIMessage[];\n /** 可用工具定义列表 */\n tools?: ToolDefinition[];\n};\n\n/**\n * BaseAIClient 构造选项。\n *\n * `chatHandler` 是核心 — 用户提供的对话处理函数。\n * 它接收统一格式的入参,返回统一格式的 AIChatResponse。\n */\nexport type BaseAIClientOptions = {\n /** 对话处理函数 — 接收 ChatHandlerParams,返回 AIChatResponse */\n chatHandler: (params: ChatHandlerParams) => Promise<AIChatResponse>;\n};\n\n// ─── BaseAIClient 类 ───\n\n/**\n * 可继承的 AI 客户端基类 — 实现 AIClient 接口。\n *\n * 设计原则:\n * - 实现 `AIClient` 接口 → 可直接传入 `executeAgentLoop()` 和 `WebAgent`\n * - 构造时注入 `chatHandler` → 无需继承即可自定义对话逻辑\n * - `chat()` 方法可被子类覆盖 → 支持继承式扩展(添加中间件逻辑)\n */\nexport class BaseAIClient implements AIClient {\n /** 用户提供的对话处理函数 */\n protected chatHandler: (params: ChatHandlerParams) => Promise<AIChatResponse>;\n\n constructor(options: BaseAIClientOptions) {\n this.chatHandler = options.chatHandler;\n }\n\n /**\n * 发送对话请求并获取 AI 响应。\n *\n * 默认实现直接委托给 `chatHandler`。\n * 子类可覆盖此方法添加中间件逻辑(日志、重试、缓存等)。\n *\n * @param params - 统一格式的聊天参数\n * @returns 统一格式的 AI 响应\n */\n async chat(params: ChatHandlerParams): Promise<AIChatResponse> {\n return this.chatHandler(params);\n }\n}\n","/**\n * OpenAI / Copilot(GitHub Models)AI 客户端。\n *\n * OpenAI 和 Copilot 使用相同的 API 格式(Chat Completions API),\n * 区别仅在端点 URL 和认证方式:\n * - OpenAI → https://api.openai.com/v1/chat/completions + Bearer <API Key>\n * - Copilot → https://models.inference.ai.azure.com/chat/completions + Bearer <GitHub PAT>\n *\n * 提供两层能力:\n * - 类:OpenAIClient(继承 BaseAIClient)— 封装完整 fetch 流程\n * - 函数:buildOpenAIRequest / parseOpenAIResponse — 底层格式转换\n *\n * 继承关系:\n * BaseAIClient(custom.ts)\n * └── OpenAIClient(本文件)— 覆盖 chat(),内部调用 build → fetch → parse\n *\n * 使用方:\n * ai-client/openai.ts ←── ai-client/index.ts(主入口)\n */\nimport type { AIChatResponse, AIMessage, AIToolCall } from \"../types.js\";\nimport type { AIClientConfig, ChatParams, ChatRequestInit } from \"./index.js\";\nimport { BaseAIClient } from \"./custom.js\";\nimport type { ChatHandlerParams } from \"./custom.js\";\nimport { resolveBaseURL, cleanSchema } from \"./constants.js\";\n\n// ─── OpenAI 原始 API 响应类型 ───\n\n/** OpenAI tool_calls 中单个工具调用的原始格式 */\ntype OpenAIRawToolCall = {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n};\n\n/** OpenAI Chat Completions API 的原始 JSON 响应 */\ntype OpenAIRawResponse = {\n choices?: Array<{\n message: {\n content: string | null;\n tool_calls?: OpenAIRawToolCall[];\n };\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n };\n};\n\n// ─── OpenAIClient 类 ───\n\n/**\n * OpenAI / Copilot AI 客户端 — 继承 BaseAIClient。\n *\n * 封装完整的 OpenAI Chat Completions API 调用流程:\n * 1. buildOpenAIRequest() → 构建 HTTP 请求\n * 2. fetch() → 发送请求\n * 3. parseOpenAIResponse() → 解析响应为统一格式\n *\n * 使用示例:\n * ```ts\n * const client = new OpenAIClient({\n * provider: \"openai\",\n * model: \"gpt-4o\",\n * apiKey: \"sk-xxx\",\n * });\n * const response = await client.chat({ systemPrompt, messages, tools });\n * ```\n *\n * 也可用于 Copilot(GitHub Models):\n * ```ts\n * const client = new OpenAIClient({\n * provider: \"copilot\",\n * model: \"gpt-4o\",\n * apiKey: \"ghp_xxx\",\n * });\n * ```\n */\nexport class OpenAIClient extends BaseAIClient {\n /** AI 客户端配置(provider / model / apiKey / baseURL) */\n protected config: AIClientConfig;\n\n constructor(config: AIClientConfig) {\n // 注入 chatHandler — 实现 buildRequest → fetch → parseResponse 的完整流程\n super({\n chatHandler: async (params: ChatHandlerParams): Promise<AIChatResponse> => {\n const req = buildOpenAIRequest(this.config, params);\n\n const res = await fetch(req.url, {\n method: req.method,\n headers: req.headers,\n body: req.body,\n });\n\n if (!res.ok) {\n const errText = await res.text();\n throw new Error(`AI API ${res.status}: ${errText.slice(0, 500)}`);\n }\n\n const data = await res.json();\n return parseOpenAIResponse(data);\n },\n });\n this.config = config;\n }\n}\n\n// ─── 底层 API:请求构建 ───\n\n/**\n * 将统一格式的 ChatParams 转换为 OpenAI Chat Completions API 请求。\n *\n * 转换逻辑:\n * - system prompt → `{ role: \"system\", content }` 消息\n * - 工具定义 → `tools` 数组(function calling 格式)\n * - 工具结果 → 拆分为多条 `{ role: \"tool\", tool_call_id }` 消息\n * - AI 回复含工具调用 → `tool_calls` 字段\n *\n * 默认参数:temperature=0.3, max_tokens=8192, tool_choice=\"auto\"\n */\nexport function buildOpenAIRequest(\n config: AIClientConfig,\n params: ChatParams,\n): ChatRequestInit {\n const baseURL = resolveBaseURL(config);\n const { systemPrompt, messages, tools } = params;\n\n // 转换工具定义为 OpenAI function calling 格式\n const openaiTools = tools?.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: cleanSchema(t.schema),\n },\n }));\n\n // 转换消息为 OpenAI 格式\n const openaiMessages = convertMessages(systemPrompt, messages);\n\n // 构建请求体\n const body: Record<string, unknown> = {\n model: config.model,\n messages: openaiMessages,\n temperature: 0.3,\n max_tokens: 8192,\n };\n\n if (openaiTools && openaiTools.length > 0) {\n body.tools = openaiTools;\n body.tool_choice = \"auto\";\n }\n\n return {\n url: `${baseURL}/chat/completions`,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n };\n}\n\n// ─── 响应解析 ───\n\n/**\n * 将 OpenAI Chat Completions API 原始响应解析为统一的 AIChatResponse。\n *\n * 解析要点:\n * - 文本回复 → `choice.message.content`\n * - 工具调用 → `choice.message.tool_calls`,arguments 为 JSON 字符串需 parse\n * - Token 用量 → `usage.prompt_tokens` / `usage.completion_tokens`\n *\n * @throws 无有效 choice 时抛出 Error\n */\nexport function parseOpenAIResponse(data: unknown): AIChatResponse {\n const d = data as OpenAIRawResponse;\n const choice = d.choices?.[0];\n if (!choice) throw new Error(\"AI 未返回有效响应\");\n\n const msg = choice.message;\n\n // 解析工具调用:arguments 是 JSON 字符串,需要 parse 为对象\n const toolCalls: AIToolCall[] | undefined = msg.tool_calls?.map((tc) => ({\n id: tc.id,\n name: tc.function.name,\n input: JSON.parse(tc.function.arguments),\n }));\n\n return {\n text: msg.content || undefined,\n toolCalls: toolCalls?.length ? toolCalls : undefined,\n usage: d.usage\n ? {\n inputTokens: d.usage.prompt_tokens ?? 0,\n outputTokens: d.usage.completion_tokens ?? 0,\n }\n : undefined,\n };\n}\n\n// ─── 内部辅助函数 ───\n\n/**\n * 将统一消息格式转换为 OpenAI 消息数组。\n *\n * 三种特殊消息的处理:\n * 1. tool 消息(工具结果)→ 每个结果拆分为单独的 `role: \"tool\"` 消息\n * 2. assistant 含 toolCalls → 附带 `tool_calls` 字段\n * 3. 其他消息 → 直接映射 role + content\n */\nfunction convertMessages(\n systemPrompt: string,\n messages: AIMessage[],\n): Record<string, unknown>[] {\n const result: Record<string, unknown>[] = [\n { role: \"system\", content: systemPrompt },\n ];\n\n for (const m of messages) {\n if (m.role === \"tool\" && Array.isArray(m.content)) {\n // 工具结果 → 每个结果单独一条 tool 消息(OpenAI 要求按 tool_call_id 对应)\n for (const tc of m.content) {\n result.push({\n role: \"tool\",\n content: tc.result,\n tool_call_id: tc.toolCallId,\n });\n }\n } else if (m.role === \"assistant\" && m.toolCalls?.length) {\n // AI 回复含工具调用 → 带 tool_calls 字段\n result.push({\n role: \"assistant\",\n content: typeof m.content === \"string\" ? m.content : null,\n tool_calls: m.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\",\n function: {\n name: tc.name,\n arguments: JSON.stringify(tc.input),\n },\n })),\n });\n } else {\n // 普通消息(user / assistant 纯文本)\n result.push({\n role: m.role,\n content:\n typeof m.content === \"string\"\n ? m.content\n : JSON.stringify(m.content),\n });\n }\n }\n\n return result;\n}\n","/**\n * Anthropic Messages API 客户端。\n *\n * Anthropic 使用与 OpenAI 完全不同的 API 格式:\n * - system prompt 通过 body.system 字段传入(不在消息数组中)\n * - 工具调用使用 content block 机制(tool_use / tool_result)\n * - 工具结果作为 user 角色消息发送(而非 tool 角色)\n * - API 版本通过 `anthropic-version` 请求头指定\n *\n * 提供两层能力:\n * - 类:AnthropicClient(继承 BaseAIClient)— 封装完整 fetch 流程\n * - 函数:buildAnthropicRequest / parseAnthropicResponse — 底层格式转换\n *\n * 继承关系:\n * BaseAIClient(custom.ts)\n * └── AnthropicClient(本文件)— 覆盖 chat(),内部调用 build → fetch → parse\n *\n * 使用方:\n * ai-client/anthropic.ts ←── ai-client/index.ts(主入口)\n */\nimport type { AIChatResponse, AIMessage, AIToolCall } from \"../types.js\";\nimport type { AIClientConfig, ChatParams, ChatRequestInit } from \"./index.js\";\nimport { BaseAIClient } from \"./custom.js\";\nimport type { ChatHandlerParams } from \"./custom.js\";\nimport { resolveBaseURL, cleanSchema } from \"./constants.js\";\n\n// ─── Anthropic 原始 API 响应类型 ───\n\n/** Anthropic 文本内容块 */\ntype AnthropicTextBlock = {\n type: \"text\";\n text: string;\n};\n\n/** Anthropic 工具调用内容块 */\ntype AnthropicToolUseBlock = {\n type: \"tool_use\";\n id: string;\n name: string;\n input: unknown;\n};\n\n/** Anthropic content 数组中的元素(文本 或 工具调用) */\ntype AnthropicContentBlock = AnthropicTextBlock | AnthropicToolUseBlock;\n\n/** Anthropic Messages API 的原始 JSON 响应 */\ntype AnthropicRawResponse = {\n content?: AnthropicContentBlock[];\n usage?: {\n input_tokens: number;\n output_tokens: number;\n };\n};\n\n// ─── AnthropicClient 类 ───\n\n/**\n * Anthropic AI 客户端 — 继承 BaseAIClient。\n *\n * 封装完整的 Anthropic Messages API 调用流程:\n * 1. buildAnthropicRequest() → 构建 HTTP 请求\n * 2. fetch() → 发送请求\n * 3. parseAnthropicResponse() → 解析响应为统一格式\n *\n * 使用示例:\n * ```ts\n * const client = new AnthropicClient({\n * provider: \"anthropic\",\n * model: \"claude-sonnet-4-20250514\",\n * apiKey: \"sk-ant-xxx\",\n * });\n * const response = await client.chat({ systemPrompt, messages, tools });\n * ```\n */\nexport class AnthropicClient extends BaseAIClient {\n /** AI 客户端配置(provider / model / apiKey / baseURL) */\n protected config: AIClientConfig;\n\n constructor(config: AIClientConfig) {\n // 注入 chatHandler — 实现 buildRequest → fetch → parseResponse 的完整流程\n super({\n chatHandler: async (params: ChatHandlerParams): Promise<AIChatResponse> => {\n const req = buildAnthropicRequest(this.config, params);\n\n const res = await fetch(req.url, {\n method: req.method,\n headers: req.headers,\n body: req.body,\n });\n\n if (!res.ok) {\n const errText = await res.text();\n throw new Error(`AI API ${res.status}: ${errText.slice(0, 500)}`);\n }\n\n const data = await res.json();\n return parseAnthropicResponse(data);\n },\n });\n this.config = config;\n }\n}\n\n// ─── 底层 API:请求构建 ───\n\n/**\n * 将统一格式的 ChatParams 转换为 Anthropic Messages API 请求。\n *\n * 关键格式差异(与 OpenAI 相比):\n * - system prompt → body.system 字段(非消息数组元素)\n * - 工具定义 → input_schema(而非 parameters)\n * - 工具结果 → user 角色 + tool_result content block\n * - AI 工具调用 → assistant 角色 + tool_use content block\n *\n * max_tokens 策略:opus 模型 16384,其他模型 8192。\n * 认证头使用 `x-api-key`(而非 Authorization Bearer)。\n */\nexport function buildAnthropicRequest(\n config: AIClientConfig,\n params: ChatParams,\n): ChatRequestInit {\n const baseURL = resolveBaseURL(config);\n const { systemPrompt, messages, tools } = params;\n\n // 转换工具定义为 Anthropic 格式(input_schema 而非 parameters)\n const anthropicTools = tools?.map((t) => ({\n name: t.name,\n description: t.description,\n input_schema: cleanSchema(t.schema),\n }));\n\n // 转换消息为 Anthropic 格式(过滤掉 system 角色消息)\n const anthropicMessages = convertMessages(messages);\n\n // 构建请求体 — system 作为顶层字段\n const body: Record<string, unknown> = {\n model: config.model,\n max_tokens: config.model.includes(\"opus\") ? 16384 : 8192,\n system: systemPrompt,\n messages: anthropicMessages,\n };\n\n if (anthropicTools && anthropicTools.length > 0) {\n body.tools = anthropicTools;\n }\n\n return {\n url: `${baseURL}/v1/messages`,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": config.apiKey,\n \"anthropic-version\": \"2023-06-01\",\n },\n body: JSON.stringify(body),\n };\n}\n\n// ─── 响应解析 ───\n\n/**\n * 将 Anthropic Messages API 原始响应解析为统一的 AIChatResponse。\n *\n * Anthropic 使用 content block 数组返回多种内容:\n * - type=\"text\" → 文本回复(可能多个,合并为一个字符串)\n * - type=\"tool_use\" → 工具调用(id + name + input)\n *\n * Token 用量字段名也不同:input_tokens / output_tokens(非 prompt_tokens)。\n */\nexport function parseAnthropicResponse(data: unknown): AIChatResponse {\n const d = data as AnthropicRawResponse;\n\n // 提取所有文本块,合并为单个字符串\n const text = d.content\n ?.filter((b): b is AnthropicTextBlock => b.type === \"text\")\n .map((b) => b.text)\n .join(\"\");\n\n // 提取所有工具调用块\n const toolCalls: AIToolCall[] | undefined = d.content\n ?.filter((b): b is AnthropicToolUseBlock => b.type === \"tool_use\")\n .map((b) => ({\n id: b.id,\n name: b.name,\n input: b.input,\n }));\n\n return {\n text: text || undefined,\n toolCalls: toolCalls?.length ? toolCalls : undefined,\n usage: d.usage\n ? {\n inputTokens: d.usage.input_tokens,\n outputTokens: d.usage.output_tokens,\n }\n : undefined,\n };\n}\n\n// ─── 内部辅助函数 ───\n\n/**\n * 将统一消息格式转换为 Anthropic 消息数组。\n *\n * 关键差异处理:\n * 1. 过滤 system 消息(Anthropic 通过 body.system 传入)\n * 2. tool 角色消息 → user 角色 + tool_result content block\n * 3. assistant 含 toolCalls → text + tool_use content blocks\n */\nfunction convertMessages(\n messages: AIMessage[],\n): Record<string, unknown>[] {\n return messages\n .filter((m) => m.role !== \"system\")\n .map((m) => {\n if (m.role === \"tool\" && Array.isArray(m.content)) {\n // 工具结果 → Anthropic 用 user 角色 + tool_result content block\n return {\n role: \"user\" as const,\n content: m.content.map((tc) => ({\n type: \"tool_result\" as const,\n tool_use_id: tc.toolCallId,\n content: tc.result,\n })),\n };\n }\n if (m.role === \"assistant\" && m.toolCalls?.length) {\n // AI 回复含工具调用 → text block + tool_use blocks\n const content: Record<string, unknown>[] = [];\n if (m.content && typeof m.content === \"string\") {\n content.push({ type: \"text\", text: m.content });\n }\n for (const tc of m.toolCalls) {\n content.push({\n type: \"tool_use\",\n id: tc.id,\n name: tc.name,\n input: tc.input,\n });\n }\n return { role: \"assistant\" as const, content };\n }\n // 普通消息(user / assistant 纯文本)\n return {\n role: m.role as \"user\" | \"assistant\",\n content:\n typeof m.content === \"string\"\n ? m.content\n : JSON.stringify(m.content),\n };\n });\n}\n","/**\n * AI Client — 基于 fetch 的 AI 客户端(主入口)。\n *\n * 使用原生 fetch API,浏览器天然支持,不依赖任何 SDK,零环境耦合。\n *\n * 支持三种 provider:\n * - \"openai\" → OpenAI API (https://api.openai.com/v1)\n * - \"copilot\" → GitHub Models API (https://models.inference.ai.azure.com)\n * - \"anthropic\" → Anthropic API (https://api.anthropic.com)\n *\n * 提供两层 API:\n * - 高层:createAIClient(config) → AIClient(工厂函数,自动选择客户端类)\n * - 类:OpenAIClient / AnthropicClient / BaseAIClient(直接实例化)\n *\n * 类继承体系:\n * BaseAIClient(custom.ts)— 可继承的基类,用户自定义 AI 对接\n * ├── OpenAIClient(openai.ts)— OpenAI / Copilot 实现\n * └── AnthropicClient(anthropic.ts)— Anthropic 实现\n *\n * 文件组织:\n * ai-client/index.ts ← 主入口(本文件):类型定义 + dispatcher + re-export\n * ai-client/custom.ts ← BaseAIClient 基类(用户自定义 AI 对接)\n * ai-client/openai.ts ← OpenAIClient + OpenAI 格式转换\n * ai-client/anthropic.ts ← AnthropicClient + Anthropic 格式转换\n * ai-client/constants.ts ← 端点映射 + 共享工具函数\n *\n * 使用方:\n * core/ai-client.ts ←── web/index.ts(WebAgent)\n */\nimport type { AIClient, AIChatResponse, AIMessage } from \"../types.js\";\nimport type { ToolDefinition } from \"../tool-registry.js\";\nimport { validateProvider } from \"./constants.js\";\nimport { OpenAIClient } from \"./openai.js\";\nimport { AnthropicClient } from \"./anthropic.js\";\n\n// Re-export 类型,方便外部统一从 ai-client 导入\nexport type { AIClient, AIChatResponse, AIMessage, AIToolCall } from \"../types.js\";\n\n// Re-export 客户端类(基类 + OpenAI + Anthropic)\nexport { BaseAIClient, type BaseAIClientOptions, type ChatHandlerParams } from \"./custom.js\";\nexport { OpenAIClient } from \"./openai.js\";\nexport { AnthropicClient } from \"./anthropic.js\";\n\n// ─── 公共类型定义 ───\n\n/** AI 客户端配置 */\nexport type AIClientConfig = {\n /** AI 提供商: \"openai\" | \"copilot\" | \"anthropic\" */\n provider: string;\n /** 模型名称,如 \"gpt-4o\"、\"claude-sonnet-4-20250514\" */\n model: string;\n /** API Key / Token */\n apiKey: string;\n /** 自定义 API 基础 URL(可选,如本地 Ollama: http://localhost:11434/v1) */\n baseURL?: string;\n};\n\n/** chat 方法的统一入参 */\nexport type ChatParams = {\n /** 系统提示词 */\n systemPrompt: string;\n /** 对话消息列表 */\n messages: AIMessage[];\n /** 可用工具定义列表 */\n tools?: ToolDefinition[];\n};\n\n/**\n * 构建好的 HTTP 请求对象 — 可直接传给 fetch。\n *\n * 被 OpenAIClient / AnthropicClient 内部使用,\n * 也可通过 buildOpenAIRequest() / buildAnthropicRequest() 底层函数获取。\n */\nexport type ChatRequestInit = {\n /** 请求 URL */\n url: string;\n /** HTTP 方法 */\n method: \"POST\";\n /** 请求头 */\n headers: Record<string, string>;\n /** 请求体(JSON 字符串) */\n body: string;\n};\n\n// ─── 高层 API ───\n\n/**\n * 创建 AI 客户端(高层 API)。\n *\n * 根据 provider 自动创建对应的客户端类实例:\n * - openai / copilot → new OpenAIClient(config)\n * - anthropic → new AnthropicClient(config)\n *\n * 返回 AIClient 接口,调用 chat() 即可与 AI 对话。\n *\n * @param config - 包含 provider、model、apiKey 等配置\n * @returns AIClient 实例(OpenAIClient 或 AnthropicClient)\n */\nexport function createAIClient(config: AIClientConfig): AIClient {\n validateProvider(config.provider);\n\n switch (config.provider) {\n case \"openai\":\n case \"copilot\":\n return new OpenAIClient(config);\n case \"anthropic\":\n return new AnthropicClient(config);\n default:\n throw new Error(\n `Unknown AI provider: ${config.provider}. Supported: openai, copilot, anthropic`,\n );\n }\n}\n","/**\n * Tool Registry — 工具注册表,负责工具的注册、查询和分发。\n *\n * 实例化设计 — 每个 Agent 创建独立的 ToolRegistry,避免全局状态污染:\n *\n * // Node 端\n * const registry = new ToolRegistry();\n * registerBuiltinTools(registry); // 注册 exec, file, browser...\n * await executeAgentLoop({ registry, ... });\n *\n * // Web 端\n * const registry = new ToolRegistry();\n * registerWebTools(registry); // 注册 dom, navigate...\n * await executeAgentLoop({ registry, ... });\n *\n * 优点:\n * - 多实例安全:Node Agent 和 Web Agent 可并行运行,工具列表互不干扰\n * - 测试隔离:每个 test case 创建独立 registry,无需清理全局状态\n * - 可组合:可按需注册不同工具子集\n */\nimport type { TObject } from \"@sinclair/typebox\";\nexport { jsonResult, readNumberParam, readStringParam } from \"./tool-params.js\";\n\n/**\n * 工具执行结果 — 每个工具的 execute() 必须返回此类型。\n */\nexport type ToolCallResult = {\n /** 返回内容(字符串文本或结构化对象,最终会序列化后发给 AI) */\n content: string | Record<string, unknown>;\n /** 可选的额外细节(用于日志记录、调试等,不直接发给 AI) */\n details?: Record<string, unknown>;\n};\n\n/**\n * 工具定义 — 注册工具时需要提供的完整描述。\n *\n * 这四个字段分别告诉 AI「叫什么名字」「能做什么」「需要什么参数」「怎么执行」:\n * - name + description → AI 根据用户意图选择合适的工具\n * - schema → AI 生成符合格式的参数 JSON\n * - execute → 实际执行逻辑\n */\nexport type ToolDefinition = {\n /** 工具名称(AI 通过此名称调用,如 \"exec\"、\"file_read\") */\n name: string;\n /** 工具描述(AI 据此判断何时使用这个工具) */\n description: string;\n /** 参数的 JSON Schema(TypeBox 定义,描述工具接受哪些参数及其类型) */\n schema: TObject;\n /** 执行函数 — 接收 AI 传入的参数,返回执行结果 */\n execute: (params: Record<string, unknown>) => Promise<ToolCallResult>;\n};\n\n/**\n * 工具注册表实例 — 管理一组工具的注册、查询和分发。\n *\n * 每个 Agent 拥有独立的 ToolRegistry 实例,从而:\n * - Node Agent 的 exec/file 工具不会泄漏到 Web Agent\n * - Web Agent 的 dom/navigate 工具不会泄漏到 Node Agent\n * - 测试中不同 case 互不影响\n */\nexport class ToolRegistry {\n private tools = new Map<string, ToolDefinition>();\n\n /** 注册一个工具 */\n register(tool: ToolDefinition): void {\n this.tools.set(tool.name, tool);\n }\n\n /** 获取所有已注册的工具定义列表(发给 AI,告知可用工具) */\n getDefinitions(): ToolDefinition[] {\n return Array.from(this.tools.values());\n }\n\n /**\n * 根据工具名分发并执行工具调用。\n * - 找到工具 → 执行 execute() → 返回结果\n * - 找不到 → 返回错误信息(不抛异常,让 AI 知道工具不存在)\n * - 执行出错 → 捕获异常,返回错误信息(不中断 Agent 循环)\n */\n async dispatch(name: string, input: unknown): Promise<ToolCallResult> {\n const tool = this.tools.get(name);\n if (!tool) {\n return {\n content: `Unknown tool: ${name}`,\n details: { error: true, toolName: name },\n };\n }\n\n try {\n const params = (input ?? {}) as Record<string, unknown>;\n return await tool.execute(params);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: `Tool \"${name}\" failed: ${message}`,\n details: { error: true, toolName: name, message },\n };\n }\n }\n}\n","/**\n * 极简系统提示词 — 告诉 AI 它是谁以及有哪些工具可用。\n *\n * 纯函数,不依赖任何配置或全局状态。\n * 调用方传入工具列表即可。\n *\n * 【后续可拓展】\n * - 添加 Runtime 信息段(provider、model、date 等)\n * - 支持 extraInstructions 注入自定义指令\n * - 支持 thinkingLevel 控制思考深度\n */\nimport type { ToolDefinition } from \"./tool-registry.js\";\n\nexport type SystemPromptParams = {\n /** 已注册的工具列表(由调用方从 ToolRegistry 获取后传入) */\n tools?: ToolDefinition[];\n /** AI 思考深度 */\n thinkingLevel?: string;\n};\n\n/**\n * 构建系统提示词。\n * 由两部分组成:身份描述 + 可用工具列表。\n */\nexport function buildSystemPrompt(params: SystemPromptParams = {}): string {\n const sections: string[] = [];\n\n // 身份 + 操作规则(精简版)\n sections.push(\n \"You are AutoPilot, an AI agent embedded in the user's web page.\\n\" +\n \"You can click, fill forms, read content, navigate, and execute JavaScript.\\n\\n\" +\n \"## 操作规则\\n\\n\" +\n \"1. 快照中每个元素末尾的 `#xxxx` 是 hash ID。操作时**必须**用 `#xxxx` 作为 dom 工具的 selector 参数。\\n\" +\n \"2. **禁止**猜测 CSS 选择器,只用快照中的 hash ID。\\n\" +\n \"3. 多个相似元素时,根据层级结构、所在功能区域和用户意图判断目标。\\n\" +\n \"4. 快照看不到目标时,先滚动页面或用 snapshot 获取更深层级。\\n\" +\n \"5. 破坏性操作前先与用户确认。\"\n );\n\n // 工具列表\n const tools = params.tools ?? [];\n if (tools.length > 0) {\n const toolLines = tools.map(t => `- **${t.name}**: ${t.description}`);\n sections.push(\n \"## Available Tools\\n\\n\" +\n toolLines.join(\"\\n\") + \"\\n\\n\" +\n \"Use tools when needed to complete the user's request.\"\n );\n }\n\n return sections.join(\"\\n\\n\");\n}\n","/**\n * DOM Tool — 基于 Web API 的 DOM 操作工具。\n *\n * 替代 Playwright 的 click/fill/type 等操作,直接在页面上下文中执行。\n * 运行环境:浏览器 Content Script。\n *\n * 支持 11 种动作:\n * click — 点击元素\n * fill — 填写输入框(清空后设值)\n * type — 逐字符模拟键入\n * focus — 聚焦元素\n * hover — 鼠标悬停(触发 mouseenter/mouseover)\n * press — 按下键盘按键(Enter/Escape/Tab/ArrowDown 等)\n * get_text — 获取元素文本内容\n * get_attr — 获取元素属性值\n * set_attr — 设置元素属性\n * add_class — 添加 CSS 类名\n * remove_class — 移除 CSS 类名\n */\nimport { Type } from \"@sinclair/typebox\";\nimport type { ToolDefinition, ToolCallResult } from \"../core/tool-registry.js\";\nimport type { RefStore } from \"./ref-store.js\";\n\nconst DEFAULT_WAIT_MS = 1000;\n\n/** 当前活跃的 RefStore 实例(由 WebAgent 在 chat() 时设置) */\nlet activeRefStore: RefStore | undefined;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * 安全地查询 DOM 元素。\n *\n * 支持两种定位方式(优先级从高到低):\n * - hash ID(以 \"#\" 开头且在 RefStore 中存在):确定性 hash 查找(最高效)\n * - CSS 选择器(其他):传统 querySelector\n */\nfunction queryElement(selector: string): Element | string {\n try {\n // #hashId — 优先从 RefStore 查找\n if (selector.startsWith(\"#\") && activeRefStore) {\n const id = selector.slice(1); // 去掉 #\n if (activeRefStore.has(id)) {\n const el = activeRefStore.get(id);\n if (!el) return `未找到 ref \"${selector}\" 对应的元素(可能已被移除或快照已过期)`;\n return el;\n }\n // 不在 RefStore 中 → 回退到 CSS 选择器(可能是 #some-id)\n }\n\n // CSS 选择器\n const el = document.querySelector(selector);\n if (!el) return `未找到匹配 \"${selector}\" 的元素`;\n return el;\n } catch (e) {\n return `选择器语法错误: ${selector}`;\n }\n}\n\n/**\n * 设置当前活跃的 RefStore(由 WebAgent 在 chat 开始时调用)。\n */\nexport function setActiveRefStore(store: RefStore | undefined): void {\n activeRefStore = store;\n}\n\n/** 获取当前活跃的 RefStore(供其他工具复用) */\nexport function getActiveRefStore(): RefStore | undefined {\n return activeRefStore;\n}\n\n/**\n * 在给定超时时间内轮询查找元素。\n * - 返回 Element:找到元素\n * - 返回 string:选择器语法错误\n * - 返回 null:超时未找到\n */\nasync function waitForElement(\n selector: string,\n timeoutMs: number,\n): Promise<Element | string | null> {\n const start = Date.now();\n\n while (Date.now() - start <= timeoutMs) {\n const elOrError = queryElement(selector);\n if (typeof elOrError !== \"string\") return elOrError;\n\n if (elOrError.startsWith(\"选择器语法错误\")) return elOrError;\n await sleep(100);\n }\n\n return null;\n}\n\nfunction resolveWaitMs(params: Record<string, unknown>): number {\n const waitMs = params.waitMs;\n if (typeof waitMs === \"number\" && Number.isFinite(waitMs)) {\n return Math.max(0, Math.floor(waitMs));\n }\n\n const waitSeconds = params.waitSeconds;\n if (typeof waitSeconds === \"number\" && Number.isFinite(waitSeconds)) {\n return Math.max(0, Math.floor(waitSeconds * 1000));\n }\n\n return DEFAULT_WAIT_MS;\n}\n\n/**\n * 模拟真实用户输入:触发 input、change 事件,兼容 React/Vue 等框架。\n */\nfunction dispatchInputEvents(el: HTMLInputElement | HTMLTextAreaElement): void {\n el.dispatchEvent(new Event(\"input\", { bubbles: true, cancelable: true }));\n el.dispatchEvent(new Event(\"change\", { bubbles: true, cancelable: true }));\n}\n\n/**\n * 生成元素的可读描述,用于在操作结果中展示实际命中的 DOM 节点。\n * 格式:<tag#id.class> \"文本\" [attr=val, ...]\n */\nfunction describeElement(el: Element): string {\n const tag = el.tagName.toLowerCase();\n const id = el.id ? `#${el.id}` : \"\";\n const cls = el.className && typeof el.className === \"string\"\n ? el.className.trim().split(/\\s+/).filter(Boolean).slice(0, 3).map(c => `.${c}`).join(\"\")\n : \"\";\n const text = el.textContent?.trim().slice(0, 40) ?? \"\";\n const textHint = text ? ` \"${text}\"` : \"\";\n\n // 关键属性\n const hints: string[] = [];\n for (const attr of [\"type\", \"name\", \"placeholder\", \"href\", \"role\"]) {\n const val = el.getAttribute(attr);\n if (val) hints.push(`${attr}=${val}`);\n }\n const attrHint = hints.length > 0 ? ` [${hints.join(\", \")}]` : \"\";\n\n return `<${tag}${id}${cls}>${textHint}${attrHint}`;\n}\n\nexport function createDomTool(): ToolDefinition {\n return {\n name: \"dom\",\n description: [\n \"Perform DOM operations on the current page.\",\n \"Actions: click, fill, type, focus, hover, press, get_text, get_attr, set_attr, add_class, remove_class.\",\n \"Use the hash ID from DOM snapshot (e.g. #a1b2c) as selector.\",\n ].join(\" \"),\n\n schema: Type.Object({\n action: Type.String({\n description:\n \"DOM action: click | fill | type | focus | hover | press | get_text | get_attr | set_attr | add_class | remove_class\",\n }),\n selector: Type.String({ description: \"Element ref ID from snapshot (e.g. #r0, #r5) or CSS selector\" }),\n value: Type.Optional(\n Type.String({ description: \"Value for fill/type/set_attr actions\" }),\n ),\n key: Type.Optional(\n Type.String({ description: \"Key name for press action (e.g. Enter, Escape, Tab, ArrowDown, ArrowUp, Backspace, Delete, Space)\" }),\n ),\n attribute: Type.Optional(\n Type.String({ description: \"Attribute name for get_attr/set_attr actions\" }),\n ),\n className: Type.Optional(\n Type.String({ description: \"CSS class name for add_class/remove_class\" }),\n ),\n waitMs: Type.Optional(\n Type.Number({\n description:\n \"Optional wait timeout in ms before action (default: 1000). Use 0 to disable waiting.\",\n }),\n ),\n waitSeconds: Type.Optional(\n Type.Number({\n description:\n \"Optional wait timeout in seconds before action. Used when waitMs is not provided.\",\n }),\n ),\n }),\n\n execute: async (params): Promise<ToolCallResult> => {\n const action = params.action as string;\n const selector = params.selector as string;\n const waitMs = resolveWaitMs(params);\n\n if (!selector) return { content: \"缺少 selector 参数\" };\n\n let el: Element;\n if (waitMs > 0) {\n const found = await waitForElement(selector, waitMs);\n\n if (typeof found === \"string\") {\n return {\n content: found,\n details: { error: true, code: \"INVALID_SELECTOR\", action, selector },\n };\n }\n\n if (!found) {\n return {\n content: `未找到匹配 \"${selector}\" 的元素`,\n details: {\n error: true,\n code: \"ELEMENT_NOT_FOUND\",\n action,\n selector,\n waitMs,\n },\n };\n }\n\n el = found;\n } else {\n const elOrError = queryElement(selector);\n if (typeof elOrError === \"string\") {\n const code = elOrError.startsWith(\"未找到\")\n ? \"ELEMENT_NOT_FOUND\"\n : \"INVALID_SELECTOR\";\n return {\n content: elOrError,\n details: { error: true, code, action, selector, waitMs },\n };\n }\n el = elOrError;\n }\n\n try {\n switch (action) {\n // ─── 交互类 ───\n\n case \"click\": {\n // 模拟点击:先 focus 再 click,触发完整事件链\n if (el instanceof HTMLElement) {\n el.focus();\n el.click();\n } else {\n el.dispatchEvent(new MouseEvent(\"click\", { bubbles: true }));\n }\n return { content: `已点击 ${describeElement(el)}` };\n }\n\n case \"focus\": {\n // 聚焦元素\n if (el instanceof HTMLElement) {\n el.focus();\n } else {\n el.dispatchEvent(new FocusEvent(\"focus\", { bubbles: true }));\n }\n return { content: `已聚焦 ${describeElement(el)}` };\n }\n\n case \"hover\": {\n // 鼠标悬停:触发 mouseenter → mouseover 事件链\n el.dispatchEvent(new MouseEvent(\"mouseenter\", { bubbles: false, cancelable: true }));\n el.dispatchEvent(new MouseEvent(\"mouseover\", { bubbles: true, cancelable: true }));\n el.dispatchEvent(new MouseEvent(\"mousemove\", { bubbles: true, cancelable: true }));\n return { content: `已悬停 ${describeElement(el)}` };\n }\n\n case \"press\": {\n // 按下指定键:先聚焦元素,再触发 keydown → keypress → keyup 完整事件链\n const key = (params.key as string) || (params.value as string);\n if (!key) return { content: \"缺少 key 参数(如 Enter, Escape, Tab)\" };\n\n if (el instanceof HTMLElement) el.focus();\n\n const eventInit: KeyboardEventInit = {\n key,\n code: key,\n bubbles: true,\n cancelable: true,\n };\n el.dispatchEvent(new KeyboardEvent(\"keydown\", eventInit));\n el.dispatchEvent(new KeyboardEvent(\"keypress\", eventInit));\n el.dispatchEvent(new KeyboardEvent(\"keyup\", eventInit));\n return { content: `已在 ${describeElement(el)} 上按下 ${key}` };\n }\n\n case \"fill\": {\n // 填写输入框:清空原有值 → 设置新值 → 触发 input/change 事件\n const value = params.value as string;\n if (value === undefined) return { content: \"缺少 value 参数\" };\n\n if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {\n el.focus();\n el.value = value;\n dispatchInputEvents(el);\n } else if (el instanceof HTMLElement && el.isContentEditable) {\n el.focus();\n el.textContent = value;\n el.dispatchEvent(new Event(\"input\", { bubbles: true }));\n } else {\n return { content: `\"${selector}\" 不是可编辑元素` };\n }\n return { content: `已填写 ${describeElement(el)}: \"${value}\"` };\n }\n\n case \"type\": {\n // 逐字符键入:每个字符触发 keydown → keypress → input → keyup\n // 适用于有实时监听键盘事件的输入框(如搜索自动补全)\n const value = params.value as string;\n if (value === undefined) return { content: \"缺少 value 参数\" };\n\n if (el instanceof HTMLElement) el.focus();\n\n for (const char of value) {\n el.dispatchEvent(\n new KeyboardEvent(\"keydown\", { key: char, bubbles: true }),\n );\n el.dispatchEvent(\n new KeyboardEvent(\"keypress\", { key: char, bubbles: true }),\n );\n if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {\n el.value += char;\n }\n el.dispatchEvent(new Event(\"input\", { bubbles: true }));\n el.dispatchEvent(\n new KeyboardEvent(\"keyup\", { key: char, bubbles: true }),\n );\n }\n return { content: `已逐字输入到 ${describeElement(el)}: \"${value}\"` };\n }\n\n // ─── 读取类 ───\n\n case \"get_text\": {\n // 获取元素的文本内容(包括子元素)\n const text = el.textContent?.trim() ?? \"\";\n return { content: `${describeElement(el)} 的文本内容:${text || \"(空)\"}` };\n }\n\n case \"get_attr\": {\n // 获取元素的指定属性值\n const attribute = params.attribute as string;\n if (!attribute) return { content: \"缺少 attribute 参数\" };\n const attrValue = el.getAttribute(attribute);\n return { content: `${describeElement(el)} 的 ${attribute} = ${attrValue ?? \"(不存在)\"}` };\n }\n\n // ─── 修改类 ───\n\n case \"set_attr\": {\n // 设置元素的属性值\n const attribute = params.attribute as string;\n const value = params.value as string;\n if (!attribute || value === undefined)\n return { content: \"缺少 attribute 或 value 参数\" };\n el.setAttribute(attribute, value);\n return { content: `已设置 ${describeElement(el)} 的 ${attribute}=\"${value}\"` };\n }\n\n case \"add_class\": {\n // 给元素添加 CSS 类名\n const className = params.className as string;\n if (!className) return { content: \"缺少 className 参数\" };\n el.classList.add(className);\n return { content: `已添加 class \"${className}\" 到 ${describeElement(el)}` };\n }\n\n case \"remove_class\": {\n // 移除元素的 CSS 类名\n const className = params.className as string;\n if (!className) return { content: \"缺少 className 参数\" };\n el.classList.remove(className);\n return { content: `已移除 ${describeElement(el)} 的 class \"${className}\"` };\n }\n\n default:\n return { content: `未知的 DOM 动作: ${action}` };\n }\n } catch (err) {\n return {\n content: `DOM 操作 \"${action}\" 失败: ${err instanceof Error ? err.message : String(err)}`,\n details: { error: true, action, selector },\n };\n }\n },\n };\n}\n","/**\n * Page Info Tool — 基于 Web API 的页面信息获取工具。\n *\n * 替代 Playwright 的 getTitle/getUrl/snapshot 等。\n * 运行环境:浏览器 Content Script。\n *\n * 支持 6 种动作:\n * get_url — 获取当前页面 URL\n * get_title — 获取页面标题\n * get_selection — 获取用户选中的文本\n * get_viewport — 获取视口尺寸和滚动位置\n * snapshot — 获取页面 DOM 结构快照(AI 可读的文本描述)\n * query_all — 查询所有匹配选择器的元素,返回摘要信息\n */\nimport { Type } from \"@sinclair/typebox\";\nimport type { ToolDefinition, ToolCallResult } from \"../core/tool-registry.js\";\nimport type { RefStore } from \"./ref-store.js\";\nimport { getActiveRefStore } from \"./dom-tool.js\";\n\n/** 快照配置选项 */\nexport type SnapshotOptions = {\n /** 最大遍历深度(默认 6) */\n maxDepth?: number;\n /**\n * 视口裁剪:只保留与视口相交的元素(默认 true)。\n * 开启后,完全在视口外的元素会被跳过,大幅减少 token 消耗。\n * 注意:祖先容器即使自身不在视口内,只要有子元素在视口内就会保留。\n */\n viewportOnly?: boolean;\n /**\n * 智能剪枝:折叠无意义的纯布局容器(默认 true)。\n * 开启后,没有文本、没有 id、没有交互属性的纯布局元素(div/span/section 等)\n * 如果自身无意义,会被折叠——子元素直接提升到父级输出,减少嵌套噪音。\n */\n pruneLayout?: boolean;\n /**\n * hash ID 映射表(可选)。\n * 传入 RefStore 实例后,每个元素使用确定性 hash ID 替代完整 XPath,\n * 大幅减少 token 消耗。dom-tool 通过 RefStore.get(id) 解析回 DOM 元素。\n */\n refStore?: RefStore;\n};\n\n/**\n * 生成页面 DOM 快照 — 将 DOM 树转为 AI 可理解的文本描述。\n *\n * 基于 Web API 实现,只遍历可见元素,跳过 script/style/svg 等无意义节点。\n * 传入 RefStore 时,每个元素生成确定性 hash ID(如 #a1b2c),\n * AI 通过 hash ID 精确定位元素,无需猜测 CSS 选择器。\n *\n * 输出格式示例:\n * [header] #k9f2a\n * [nav] #m3d7e\n * [a] \"首页\" href=\"/\" #p1c4b\n * [a] \"关于\" href=\"/about\" #q8e5f\n * [main] #r2a6d\n * [h1] \"欢迎\" #s7g3h\n * [input] type=\"text\" placeholder=\"搜索...\" #t4j8k\n * [button] \"搜索\" id=\"search-btn\" onclick #u5n2m\n *\n * @param root - 快照根元素(默认 document.body)\n * @param options - 快照选项对象,或传入数字作为 maxDepth(向后兼容)\n */\nexport function generateSnapshot(\n root: Element = document.body,\n options: SnapshotOptions | number = {},\n): string {\n // 向后兼容:数字参数视为 maxDepth\n const opts: SnapshotOptions = typeof options === \"number\"\n ? { maxDepth: options }\n : options;\n\n const maxDepth = opts.maxDepth ?? 6;\n const viewportOnly = opts.viewportOnly ?? true;\n const pruneLayout = opts.pruneLayout ?? true;\n\n const refStore = opts.refStore;\n\n const SKIP_TAGS = new Set([\n \"SCRIPT\", \"STYLE\", \"SVG\", \"NOSCRIPT\", \"LINK\", \"META\", \"BR\", \"HR\",\n ]);\n\n /** 纯布局容器标签 — 智能剪枝时可能被折叠 */\n const LAYOUT_TAGS = new Set([\n \"DIV\", \"SPAN\", \"SECTION\", \"ARTICLE\", \"ASIDE\", \"MAIN\",\n \"HEADER\", \"FOOTER\", \"NAV\", \"FIGURE\", \"FIGCAPTION\",\n ]);\n\n /** 视口尺寸(viewportOnly 开启时使用) */\n const vpWidth = viewportOnly ? window.innerWidth : 0;\n const vpHeight = viewportOnly ? window.innerHeight : 0;\n\n const INTERACTIVE_ATTRS = [\n \"href\", \"type\", \"placeholder\", \"value\", \"name\", \"role\", \"aria-label\",\n \"src\", \"alt\", \"title\", \"for\", \"action\", \"method\", \"target\", \"min\", \"max\",\n \"pattern\", \"maxlength\", \"tabindex\",\n ];\n\n /** 布尔状态属性 — 只在存在时输出(无值),如 disabled、checked */\n const BOOLEAN_ATTRS = [\n \"disabled\", \"checked\", \"readonly\", \"required\", \"selected\",\n \"hidden\", \"multiple\", \"autofocus\", \"open\",\n ];\n\n /** 内联事件属性前缀 */\n const EVENT_PREFIX = \"on\";\n\n /**\n * 计算元素在父节点中同标签兄弟里的序号(1-based,XPath 规范)。\n * 如果同标签兄弟只有一个,返回空字符串(无需索引消歧)。\n */\n function getSiblingIndex(el: Element): string {\n const parent = el.parentElement;\n if (!parent) return \"\";\n const tag = el.tagName;\n const siblings = Array.from(parent.children).filter((c) => c.tagName === tag);\n if (siblings.length <= 1) return \"\";\n return `[${siblings.indexOf(el) + 1}]`;\n }\n\n /**\n * 判断元素是否与视口相交(部分可见也算)。\n * 对根级容器(depth <= 1)始终返回 true,确保不丢失顶层结构。\n */\n function isInViewport(el: Element, depth: number): boolean {\n if (!viewportOnly) return true;\n // 根级容器始终保留(body/html 等),否则整棵树会被跳过\n if (depth <= 1) return true;\n const rect = el.getBoundingClientRect();\n // 元素完全在视口外则跳过\n if (rect.bottom < 0 || rect.top > vpHeight) return false;\n if (rect.right < 0 || rect.left > vpWidth) return false;\n // 零尺寸元素(如隐藏的 position:absolute 元素)也跳过\n if (rect.width === 0 && rect.height === 0) return false;\n return true;\n }\n\n /**\n * 判断元素是否为「无意义布局容器」(智能剪枝候选)。\n * 满足所有条件时返回 true:\n * 1. 标签是常见布局容器(div/span/section 等)\n * 2. 没有 id\n * 3. 没有交互属性(href/role/aria-label/onclick 等)\n * 4. 没有直接文本内容\n */\n function isEmptyLayoutContainer(el: Element, directText: string): boolean {\n if (!pruneLayout) return false;\n if (!LAYOUT_TAGS.has(el.tagName)) return false;\n // 有 id 的元素可能是重要锚点\n if (el.getAttribute(\"id\")) return false;\n // 有 role/aria-label 的元素有语义\n if (el.getAttribute(\"role\") || el.getAttribute(\"aria-label\")) return false;\n // 有内联事件(onclick 等)的元素有交互\n for (const attr of Array.from(el.attributes)) {\n if (attr.name.startsWith(\"on\")) return false;\n }\n // 有直接文本内容的元素有意义\n if (directText) return false;\n return true;\n }\n\n function walk(el: Element, depth: number, parentPath: string): string {\n if (depth > maxDepth) return \"\";\n if (SKIP_TAGS.has(el.tagName)) return \"\";\n\n // 跳过不可见元素\n const style = window.getComputedStyle(el);\n if (style.display === \"none\" || style.visibility === \"hidden\") return \"\";\n\n // ─── 视口裁剪 ───\n // 检查元素是否在视口内(viewportOnly 关闭时始终通过)\n if (!isInViewport(el, depth)) return \"\";\n\n const indent = \" \".repeat(depth);\n const tag = el.tagName.toLowerCase();\n\n // 构建当前元素的内部路径(用于 hash 计算,不输出到快照)\n const index = getSiblingIndex(el);\n const currentPath = `${parentPath}/${tag}${index}`;\n\n // 收集有意义的属性(精简版:只保留对 AI 操作有用的信息)\n const attrs: string[] = [];\n\n // 1. id — 最重要的标识信息\n const elId = el.getAttribute(\"id\");\n if (elId) attrs.push(`id=\"${elId}\"`);\n\n // 2. class — 只保留有语义的类名(过滤框架 hash 类,最多 2 个)\n const className = el.getAttribute(\"class\")?.trim();\n if (className) {\n const classes = className.split(/\\s+/)\n .filter(c => c && !c.startsWith(\"data-v-\") && c.length < 30)\n .slice(0, 2).join(\" \");\n if (classes) attrs.push(`class=\"${classes}\"`);\n }\n\n // 3. 交互属性(href, type, placeholder 等)\n for (const attr of INTERACTIVE_ATTRS) {\n const val = el.getAttribute(attr);\n if (val) attrs.push(`${attr}=\"${val}\"`);\n }\n\n // 4. 布尔状态属性(disabled, checked 等)\n for (const attr of BOOLEAN_ATTRS) {\n if (el.hasAttribute(attr)) attrs.push(attr);\n }\n\n // 5. 事件绑定 — 只检测有交互意义的事件(onclick, onchange)\n const events: string[] = [];\n for (const attrObj of Array.from(el.attributes)) {\n if (attrObj.name.startsWith(EVENT_PREFIX)) {\n events.push(attrObj.name);\n }\n }\n if (events.length > 0) attrs.push(`events=[${events.join(\",\")}]`);\n\n // 6. data-* 属性 — 只保留有语义价值的(跳过框架 scoped 标记如 data-v-xxx)\n const dataAttrs: string[] = [];\n for (const attrObj of Array.from(el.attributes)) {\n if (attrObj.name.startsWith(\"data-\") && !attrObj.name.match(/^data-v-/) && dataAttrs.length < 2) {\n dataAttrs.push(`${attrObj.name}=\"${attrObj.value.slice(0, 30)}\"`);\n }\n }\n if (dataAttrs.length > 0) attrs.push(...dataAttrs);\n\n // 7. 对于 input/textarea,补充当前实际 value\n if ((el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) && el.value) {\n const currentVal = el.value.slice(0, 60);\n const attrVal = el.getAttribute(\"value\");\n if (attrVal !== currentVal) {\n attrs.push(`val=\"${currentVal}\"`);\n }\n }\n\n // 获取直接文本(不含子元素文本)\n let directText = \"\";\n for (let i = 0; i < el.childNodes.length; i++) {\n const node = el.childNodes[i];\n if (node.nodeType === Node.TEXT_NODE) {\n const t = node.textContent?.trim();\n if (t) directText += t + \" \";\n }\n }\n directText = directText.trim();\n\n // ─── 智能剪枝 ───\n // 无意义布局容器:不输出自身行,直接将子元素提升到当前层级\n if (isEmptyLayoutContainer(el, directText)) {\n const childLines: string[] = [];\n for (let i = 0; i < el.children.length; i++) {\n // 子元素继承当前路径(保证 hash 计算正确),但不增加缩进\n const childResult = walk(el.children[i], depth, currentPath);\n if (childResult) childLines.push(childResult);\n }\n // 如果子树也全部为空,整个容器就被剪掉\n return childLines.join(\"\\n\");\n }\n\n // 构建当前元素描述:[标签] \"文本\" 属性 #ID\n let line = `${indent}[${tag}]`;\n if (directText) line += ` \"${directText.slice(0, 60)}\"`;\n if (attrs.length) line += ` ${attrs.join(\" \")}`;\n // 使用 hash ID(如 #a1b2c)或回退到完整 XPath\n if (refStore) {\n const hashId = refStore.set(el, currentPath);\n line += ` #${hashId}`;\n } else {\n line += ` ref=\"${currentPath}\"`;\n }\n\n const lines: string[] = [line];\n\n // 递归子元素\n for (let i = 0; i < el.children.length; i++) {\n const childResult = walk(el.children[i], depth + 1, currentPath);\n if (childResult) lines.push(childResult);\n }\n\n return lines.join(\"\\n\");\n }\n\n // 根元素自身的标签作为路径起点,walk 内部不再重复追加\n // 例如 root=body 时,parentPath=\"\",walk 中 currentPath=\"/body\"\n return walk(root, 0, \"\") || \"(空页面)\";\n}\n\n/**\n * 查询所有匹配元素并返回摘要信息(标签、文本、关键属性)。\n */\nfunction queryAllElements(selector: string, limit = 20): string {\n try {\n const elements = document.querySelectorAll(selector);\n if (elements.length === 0) return `未找到匹配 \"${selector}\" 的元素`;\n\n const results: string[] = [`找到 ${elements.length} 个元素:`];\n const count = Math.min(elements.length, limit);\n\n for (let i = 0; i < count; i++) {\n const el = elements[i];\n const tag = el.tagName.toLowerCase();\n const text = el.textContent?.trim().slice(0, 60) ?? \"\";\n const id = el.id ? `#${el.id}` : \"\";\n const cls = el.className && typeof el.className === \"string\"\n ? `.${el.className.split(\" \").filter(Boolean).join(\".\")}`\n : \"\";\n results.push(` ${i + 1}. <${tag}${id}${cls}> \"${text}\"`);\n }\n\n if (elements.length > limit) {\n results.push(` ...还有 ${elements.length - limit} 个元素`);\n }\n\n return results.join(\"\\n\");\n } catch (e) {\n return `选择器语法错误: ${selector}`;\n }\n}\n\nexport function createPageInfoTool(): ToolDefinition {\n return {\n name: \"page_info\",\n description: [\n \"Get information about the current page.\",\n \"Actions: get_url, get_title, get_selection (selected text),\",\n \"get_viewport (size & scroll), snapshot (DOM structure), query_all (find all matching elements).\",\n ].join(\" \"),\n\n schema: Type.Object({\n action: Type.String({\n description:\n \"Info action: get_url | get_title | get_selection | get_viewport | snapshot | query_all\",\n }),\n selector: Type.Optional(\n Type.String({ description: \"CSS selector for query_all action\" }),\n ),\n maxDepth: Type.Optional(\n Type.Number({ description: \"Max depth for snapshot (default: 6)\" }),\n ),\n viewportOnly: Type.Optional(\n Type.Boolean({ description: \"Only snapshot elements visible in viewport (default: true)\" }),\n ),\n pruneLayout: Type.Optional(\n Type.Boolean({ description: \"Collapse empty layout containers like div/span (default: true)\" }),\n ),\n }),\n\n execute: async (params): Promise<ToolCallResult> => {\n const action = params.action as string;\n\n try {\n switch (action) {\n case \"get_url\":\n return { content: window.location.href };\n\n case \"get_title\":\n return { content: document.title || \"(无标题)\" };\n\n case \"get_selection\": {\n // 获取用户当前选中的文本\n const selection = window.getSelection();\n const text = selection?.toString().trim() ?? \"\";\n return { content: text || \"(未选中任何文本)\" };\n }\n\n case \"get_viewport\": {\n // 获取视口和滚动信息\n const info = {\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n scrollX: window.scrollX,\n scrollY: window.scrollY,\n pageWidth: document.documentElement.scrollWidth,\n pageHeight: document.documentElement.scrollHeight,\n };\n return { content: JSON.stringify(info, null, 2) };\n }\n\n case \"snapshot\": {\n // 生成 DOM 快照 — AI 理解当前页面结构的主要方式\n const maxDepth = (params.maxDepth as number) ?? 6;\n const viewportOnly = (params.viewportOnly as boolean) ?? true;\n const pruneLayout = (params.pruneLayout as boolean) ?? true;\n const snapshot = generateSnapshot(document.body, {\n maxDepth,\n viewportOnly,\n pruneLayout,\n refStore: getActiveRefStore(),\n });\n return { content: snapshot };\n }\n\n case \"query_all\": {\n // 查询所有匹配元素\n const selector = params.selector as string;\n if (!selector) return { content: \"缺少 selector 参数\" };\n return { content: queryAllElements(selector) };\n }\n\n default:\n return { content: `未知的页面信息动作: ${action}` };\n }\n } catch (err) {\n return {\n content: `页面信息操作 \"${action}\" 失败: ${err instanceof Error ? err.message : String(err)}`,\n details: { error: true, action },\n };\n }\n },\n };\n}\n","/**\n * Navigate Tool — 基于 Web API 的页面导航工具。\n *\n * 替代 Playwright 的 goto/goBack/goForward/reload。\n * 运行环境:浏览器 Content Script。\n *\n * 支持 5 种动作:\n * goto — 跳转到指定 URL\n * back — 浏览器后退\n * forward — 浏览器前进\n * reload — 刷新当前页面\n * scroll — 滚动页面到指定位置或元素\n */\nimport { Type } from \"@sinclair/typebox\";\nimport type { ToolDefinition, ToolCallResult } from \"../core/tool-registry.js\";\n\nexport function createNavigateTool(): ToolDefinition {\n return {\n name: \"navigate\",\n description: [\n \"Navigate the current page.\",\n \"Actions: goto (open URL), back, forward, reload, scroll (to position or element).\",\n ].join(\" \"),\n\n schema: Type.Object({\n action: Type.String({\n description: \"Navigation action: goto | back | forward | reload | scroll\",\n }),\n url: Type.Optional(Type.String({ description: \"URL for goto action\" })),\n selector: Type.Optional(\n Type.String({ description: \"CSS selector for scroll action (scrolls element into view)\" }),\n ),\n x: Type.Optional(Type.Number({ description: \"Horizontal scroll position (pixels)\" })),\n y: Type.Optional(Type.Number({ description: \"Vertical scroll position (pixels)\" })),\n }),\n\n execute: async (params): Promise<ToolCallResult> => {\n const action = params.action as string;\n\n try {\n switch (action) {\n case \"goto\": {\n // 跳转到指定 URL\n const url = params.url as string;\n if (!url) return { content: \"缺少 url 参数\" };\n\n // 支持相对路径和绝对路径\n window.location.href = url;\n return { content: `正在导航到 ${url}` };\n }\n\n case \"back\": {\n // 浏览器后退(等同于点击后退按钮)\n window.history.back();\n return { content: \"已后退\" };\n }\n\n case \"forward\": {\n // 浏览器前进\n window.history.forward();\n return { content: \"已前进\" };\n }\n\n case \"reload\": {\n // 刷新当前页面\n window.location.reload();\n return { content: \"正在刷新页面\" };\n }\n\n case \"scroll\": {\n // 滚动页面:优先滚动到元素,否则滚动到坐标\n const selector = params.selector as string | undefined;\n\n if (selector) {\n const el = document.querySelector(selector);\n if (!el) return { content: `未找到元素 \"${selector}\"` };\n el.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n return { content: `已滚动到元素 \"${selector}\"` };\n }\n\n const x = (params.x as number) ?? 0;\n const y = (params.y as number) ?? 0;\n window.scrollTo({ left: x, top: y, behavior: \"smooth\" });\n return { content: `已滚动到 (${x}, ${y})` };\n }\n\n default:\n return { content: `未知的导航动作: ${action}` };\n }\n } catch (err) {\n return {\n content: `导航操作 \"${action}\" 失败: ${err instanceof Error ? err.message : String(err)}`,\n details: { error: true, action },\n };\n }\n },\n };\n}\n","/**\n * Wait Tool — 基于 MutationObserver 的元素等待工具。\n *\n * 替代 Playwright 的 waitForSelector/waitForNavigation。\n * 运行环境:浏览器 Content Script。\n *\n * 支持 3 种动作:\n * wait_for_selector — 等待匹配选择器的元素出现\n * wait_for_hidden — 等待元素消失或隐藏\n * wait_for_text — 等待页面中出现指定文本\n */\nimport { Type } from \"@sinclair/typebox\";\nimport type { ToolDefinition, ToolCallResult } from \"../core/tool-registry.js\";\n\n/** 默认超时时间(毫秒) */\nconst DEFAULT_TIMEOUT = 10_000;\n\n/**\n * 通过 MutationObserver 等待元素出现。\n * 先检查元素是否已存在,不存在则监听 DOM 变化直到出现或超时。\n */\nfunction waitForSelector(selector: string, timeoutMs: number): Promise<Element> {\n return new Promise((resolve, reject) => {\n // 先检查是否已存在\n const existing = document.querySelector(selector);\n if (existing) {\n resolve(existing);\n return;\n }\n\n const timer = setTimeout(() => {\n observer.disconnect();\n reject(new Error(`等待 \"${selector}\" 超时 (${timeoutMs}ms)`));\n }, timeoutMs);\n\n const observer = new MutationObserver(() => {\n const el = document.querySelector(selector);\n if (el) {\n clearTimeout(timer);\n observer.disconnect();\n resolve(el);\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n });\n });\n}\n\n/**\n * 等待元素消失或变为不可见。\n */\nfunction waitForHidden(selector: string, timeoutMs: number): Promise<void> {\n return new Promise((resolve, reject) => {\n // 先检查是否已不存在或隐藏\n const existing = document.querySelector(selector);\n if (!existing) {\n resolve();\n return;\n }\n const style = window.getComputedStyle(existing);\n if (style.display === \"none\" || style.visibility === \"hidden\") {\n resolve();\n return;\n }\n\n const timer = setTimeout(() => {\n observer.disconnect();\n reject(new Error(`等待 \"${selector}\" 消失超时 (${timeoutMs}ms)`));\n }, timeoutMs);\n\n const observer = new MutationObserver(() => {\n const el = document.querySelector(selector);\n if (!el) {\n clearTimeout(timer);\n observer.disconnect();\n resolve();\n return;\n }\n const s = window.getComputedStyle(el);\n if (s.display === \"none\" || s.visibility === \"hidden\") {\n clearTimeout(timer);\n observer.disconnect();\n resolve();\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"style\", \"class\", \"hidden\"],\n });\n });\n}\n\n/**\n * 等待页面中出现指定文本。\n */\nfunction waitForText(text: string, timeoutMs: number): Promise<void> {\n return new Promise((resolve, reject) => {\n // 先检查是否已包含\n if (document.body.textContent?.includes(text)) {\n resolve();\n return;\n }\n\n const timer = setTimeout(() => {\n observer.disconnect();\n reject(new Error(`等待文本 \"${text}\" 出现超时 (${timeoutMs}ms)`));\n }, timeoutMs);\n\n const observer = new MutationObserver(() => {\n if (document.body.textContent?.includes(text)) {\n clearTimeout(timer);\n observer.disconnect();\n resolve();\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n });\n}\n\nexport function createWaitTool(): ToolDefinition {\n return {\n name: \"wait\",\n description: [\n \"Wait for DOM changes on the current page.\",\n \"Actions: wait_for_selector (element appears), wait_for_hidden (element disappears),\",\n \"wait_for_text (specific text appears in page).\",\n ].join(\" \"),\n\n schema: Type.Object({\n action: Type.String({\n description: \"Wait action: wait_for_selector | wait_for_hidden | wait_for_text\",\n }),\n selector: Type.Optional(\n Type.String({ description: \"CSS selector for wait_for_selector/wait_for_hidden\" }),\n ),\n text: Type.Optional(\n Type.String({ description: \"Text to wait for in wait_for_text\" }),\n ),\n timeout: Type.Optional(\n Type.Number({ description: \"Timeout in milliseconds (default: 10000)\" }),\n ),\n }),\n\n execute: async (params): Promise<ToolCallResult> => {\n const action = params.action as string;\n const timeoutMs = (params.timeout as number) ?? DEFAULT_TIMEOUT;\n\n try {\n switch (action) {\n case \"wait_for_selector\": {\n const selector = params.selector as string;\n if (!selector) return { content: \"缺少 selector 参数\" };\n await waitForSelector(selector, timeoutMs);\n return { content: `元素 \"${selector}\" 已出现` };\n }\n\n case \"wait_for_hidden\": {\n const selector = params.selector as string;\n if (!selector) return { content: \"缺少 selector 参数\" };\n await waitForHidden(selector, timeoutMs);\n return { content: `元素 \"${selector}\" 已消失` };\n }\n\n case \"wait_for_text\": {\n const text = params.text as string;\n if (!text) return { content: \"缺少 text 参数\" };\n await waitForText(text, timeoutMs);\n return { content: `文本 \"${text}\" 已出现` };\n }\n\n default:\n return { content: `未知的等待动作: ${action}` };\n }\n } catch (err) {\n return {\n content: `等待操作 \"${action}\" 失败: ${err instanceof Error ? err.message : String(err)}`,\n details: { error: true, action },\n };\n }\n },\n };\n}\n","/**\n * Evaluate Tool — 在页面上下文中执行任意 JavaScript 表达式。\n *\n * 替代 Playwright 的 page.evaluate()。\n * 运行环境:浏览器 Content Script。\n *\n * 这是最灵活的工具 — 当其他 tools 无法满足需求时,\n * AI 可以直接编写 JS 代码来操作页面。\n *\n * 支持 2 种动作:\n * evaluate — 执行 JS 表达式并返回结果\n * evaluate_handle — 执行 JS 并返回序列化的 DOM 信息\n */\nimport { Type } from \"@sinclair/typebox\";\nimport type { ToolDefinition, ToolCallResult } from \"../core/tool-registry.js\";\n\n/**\n * 安全执行 JS 表达式,捕获错误并序列化结果。\n */\nfunction safeEvaluate(expression: string): { result?: unknown; error?: string } {\n try {\n // 使用 Function 构造器代替 eval,避免污染当前作用域\n const fn = new Function(`\"use strict\"; return (${expression});`);\n const result = fn();\n return { result };\n } catch (err) {\n // 如果作为表达式失败,尝试作为语句块执行\n try {\n const fn = new Function(`\"use strict\"; ${expression}`);\n const result = fn();\n return { result };\n } catch (err2) {\n return { error: err2 instanceof Error ? err2.message : String(err2) };\n }\n }\n}\n\n/**\n * 将执行结果序列化为字符串(处理 DOM 元素、循环引用等)。\n */\nfunction serializeResult(value: unknown): string {\n if (value === undefined) return \"undefined\";\n if (value === null) return \"null\";\n\n // DOM 元素 → 返回 outerHTML 片段\n if (value instanceof Element) {\n const tag = value.tagName.toLowerCase();\n const id = value.id ? `#${value.id}` : \"\";\n const text = value.textContent?.trim().slice(0, 100) ?? \"\";\n return `<${tag}${id}> \"${text}\"`;\n }\n\n // NodeList / HTMLCollection → 逐个序列化\n if (value instanceof NodeList || value instanceof HTMLCollection) {\n const items = Array.from(value).map((el, i) => ` ${i}: ${serializeResult(el)}`);\n return `[${value.length} elements]\\n${items.join(\"\\n\")}`;\n }\n\n // 普通值 → JSON 序列化\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n}\n\nexport function createEvaluateTool(): ToolDefinition {\n return {\n name: \"evaluate\",\n description: [\n \"Execute JavaScript code in the current page context.\",\n \"Use this when other tools cannot accomplish the task.\",\n \"Can access document, window, and all page APIs.\",\n ].join(\" \"),\n\n schema: Type.Object({\n expression: Type.String({\n description:\n \"JavaScript expression or code block to execute. Has access to document, window, etc.\",\n }),\n }),\n\n execute: async (params): Promise<ToolCallResult> => {\n const expression = params.expression as string;\n if (!expression) return { content: \"缺少 expression 参数\" };\n\n const { result, error } = safeEvaluate(expression);\n\n if (error) {\n return {\n content: `JS 执行错误: ${error}`,\n details: { error: true, expression },\n };\n }\n\n return { content: serializeResult(result) };\n },\n };\n}\n","/**\n * RefStore — 快照 hash ID 与 DOM 元素的映射表。\n *\n * 快照生成时,根据元素的 DOM 路径 + 页面 URL 生成确定性 hash ID,\n * 同时保存 ID → Element 的映射。AI 使用 hash ID 作为 selector 定位元素,\n * 免去超长 XPath 路径,大幅减少 token 消耗。\n *\n * 优势:\n * - **确定性**:同一元素无论快照顺序,始终得到相同 ID\n * - **并发安全**:多次快照不会产生 ID 冲突\n * - **跨页面隔离**:URL hash 作为命名空间,不同页面元素 ID 互不碰撞\n *\n * 生命周期:每次 WebAgent.chat() 调用时创建,对话结束后清空。\n *\n * 使用方:\n * page-info-tool.ts — generateSnapshot() 写入映射\n * dom-tool.ts — queryElement() 读取映射\n * index.ts — WebAgent 持有实例,管理生命周期\n */\n\n/**\n * FNV-1a 32-bit hash — 简单高效的字符串散列。\n * 分布均匀,碰撞率低,适合生成短 ID。\n */\nfunction fnv1a(str: string): number {\n let h = 0x811c9dc5; // FNV offset basis\n for (let i = 0; i < str.length; i++) {\n h ^= str.charCodeAt(i);\n h = Math.imul(h, 0x01000193); // FNV prime\n }\n return h >>> 0; // 转为无符号 32-bit\n}\n\n/**\n * hash ID → DOM 元素的映射存储。\n *\n * - `set(el, path)` 由快照生成时调用,返回确定性 hash ID\n * - `get(id)` 由 dom-tool 查询时调用,根据 hash ID 取回元素\n * - `has(id)` 检查 ID 是否存在(用于 selector 类型判断)\n * - `clear()` 每次对话结束后清空\n */\nexport class RefStore {\n private map = new Map<string, Element>();\n /** 页面 URL 的 hash 前缀,用于跨页面命名空间隔离 */\n private urlKey: string;\n\n /**\n * @param url 当前页面 URL(可选)。传入后作为 hash 命名空间,\n * 使不同页面的相同 DOM 路径产生不同 ID。\n */\n constructor(url?: string) {\n this.urlKey = url ?? \"\";\n }\n\n /**\n * 注册一个元素,返回确定性 hash ID。\n * 相同 URL + path 始终产生相同 ID(并发安全)。\n *\n * @param el DOM 元素引用\n * @param path 元素的 XPath-like 路径(如 \"/body/div[1]/main/button\")\n */\n set(el: Element, path: string): string {\n const baseId = fnv1a(this.urlKey + path).toString(36);\n let id = baseId;\n // 极小概率碰撞处理:不同 path 映射到相同 hash 时追加后缀\n let suffix = 2;\n while (this.map.has(id) && this.map.get(id) !== el) {\n id = baseId + suffix++;\n }\n this.map.set(id, el);\n return id;\n }\n\n /**\n * 根据 hash ID 获取 DOM 元素。\n * 返回 Element 或 undefined(ID 不存在或元素已被移除)。\n */\n get(id: string): Element | undefined {\n return this.map.get(id);\n }\n\n /** 检查 hash ID 是否存在 */\n has(id: string): boolean {\n return this.map.has(id);\n }\n\n /** 清空所有映射 */\n clear(): void {\n this.map.clear();\n }\n\n /**\n * 重置映射表:清空所有映射,并可选更新 URL 命名空间。\n *\n * 用于页面导航后刷新 RefStore:旧的 hash ID → Element 映射已失效,\n * 需要用新 URL 重新生成确定性 hash。\n *\n * @param url 新的页面 URL(不传则保持原 URL 命名空间)\n */\n reset(url?: string): void {\n this.map.clear();\n if (url !== undefined) {\n this.urlKey = url;\n }\n }\n\n /** 当前映射数量 */\n get size(): number {\n return this.map.size;\n }\n}\n","/**\n * Web Tools 消息通信桥接层。\n *\n * 解决 Chrome Extension 的作用域隔离问题:\n *\n * Service Worker (后台) Content Script (页面)\n * ┌──────────────────┐ ┌──────────────────────┐\n * │ agent-core │ │ document / window │\n * │ tool-registry │ chrome.tabs │ │\n * │ │ .sendMessage() │ DOM 操作实际执行 │\n * │ tool.execute() │ ─────────────────► │ handleToolMessage() │\n * │ ↓ │ │ ↓ │\n * │ sendToContent() │ ◄───────────────── │ 返回执行结果 │\n * └──────────────────┘ response └──────────────────────┘\n *\n * 使用方式:\n * Service Worker 端:\n * import { createProxyExecutor } from \"./messaging.js\";\n * const execute = createProxyExecutor();\n * // execute 会把调用转发到 content script\n *\n * Content Script 端:\n * import { registerToolHandler } from \"./messaging.js\";\n * registerToolHandler(actualExecutors);\n * // 监听来自 service worker 的工具调用请求\n */\n\n// ─── 消息类型定义 ───\n\n/** Service Worker → Content Script 的工具调用请求 */\nexport type ToolCallMessage = {\n type: \"AUTOPILOT_TOOL_CALL\";\n toolName: string;\n params: Record<string, unknown>;\n callId: string;\n};\n\n/** Content Script → Service Worker 的工具调用结果 */\nexport type ToolCallResponse = {\n type: \"AUTOPILOT_TOOL_RESULT\";\n callId: string;\n result: {\n content: string | Record<string, unknown>;\n details?: Record<string, unknown>;\n };\n};\n\n// ─── Service Worker 端(发送方) ───\n\n/**\n * 创建代理执行器 — 在 Service Worker 端使用。\n *\n * 它不直接执行 DOM 操作,而是通过 chrome.tabs.sendMessage\n * 把调用请求发给当前活动 tab 的 content script 执行。\n *\n * @returns execute 函数,签名与 ToolDefinition.execute 相同\n */\nexport function createProxyExecutor() {\n return async (\n toolName: string,\n params: Record<string, unknown>,\n ): Promise<{ content: string | Record<string, unknown>; details?: Record<string, unknown> }> => {\n const callId = `${toolName}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n\n // 获取当前活动 tab\n const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });\n if (!tab?.id) {\n return { content: \"错误:没有活动的浏览器标签页\" };\n }\n\n // 发送消息到 content script 并等待结果\n const message: ToolCallMessage = {\n type: \"AUTOPILOT_TOOL_CALL\",\n toolName,\n params,\n callId,\n };\n\n try {\n const response = await chrome.tabs.sendMessage(tab.id, message) as ToolCallResponse;\n return response.result;\n } catch (err) {\n return {\n content: `工具调用失败(content script 可能未加载): ${err instanceof Error ? err.message : String(err)}`,\n details: { error: true, toolName },\n };\n }\n };\n}\n\n// ─── Content Script 端(接收方) ───\n\n/** 工具执行器映射:toolName → execute 函数 */\nexport type ToolExecutorMap = Map<\n string,\n (params: Record<string, unknown>) => Promise<{\n content: string | Record<string, unknown>;\n details?: Record<string, unknown>;\n }>\n>;\n\n/**\n * 在 Content Script 端注册工具执行处理器。\n *\n * 监听来自 Service Worker 的 AUTOPILOT_TOOL_CALL 消息,\n * 根据 toolName 找到对应的执行函数,执行后返回结果。\n *\n * @param executors 工具名称 → 执行函数的映射\n */\nexport function registerToolHandler(executors: ToolExecutorMap): void {\n chrome.runtime.onMessage.addListener(\n (message: unknown, _sender: chrome.runtime.MessageSender, sendResponse: (response: ToolCallResponse) => void) => {\n // 只处理我们的消息类型\n const msg = message as ToolCallMessage;\n if (msg?.type !== \"AUTOPILOT_TOOL_CALL\") return false;\n\n const executor = executors.get(msg.toolName);\n if (!executor) {\n sendResponse({\n type: \"AUTOPILOT_TOOL_RESULT\",\n callId: msg.callId,\n result: { content: `未知工具: ${msg.toolName}` },\n });\n return true; // 同步返回 true 表示我们会异步 sendResponse\n }\n\n // 异步执行工具并返回结果\n executor(msg.params)\n .then((result) => {\n sendResponse({\n type: \"AUTOPILOT_TOOL_RESULT\",\n callId: msg.callId,\n result,\n });\n })\n .catch((err) => {\n sendResponse({\n type: \"AUTOPILOT_TOOL_RESULT\",\n callId: msg.callId,\n result: {\n content: `工具 ${msg.toolName} 执行异常: ${err instanceof Error ? err.message : String(err)}`,\n details: { error: true },\n },\n });\n });\n\n return true; // 告诉 Chrome 我们会异步调用 sendResponse\n },\n );\n}\n","/**\n * WebAgent — 浏览器端 AI Agent 类。\n *\n * 封装了完整的 Agent 能力,可在浏览器中独立运行:\n * - 对话(chat) → 发消息、获取 AI 回复\n * - 工具注册 → 注册内置 Web 工具或自定义工具\n * - 决策循环 → 复用 core/agent-loop.ts 的通用逻辑\n * - AI 连接 → 复用 core/ai-client.ts(基于 fetch,跨平台)\n *\n * 使用示例:\n * ```ts\n * const agent = new WebAgent({ token: \"ghp_xxx\", provider: \"copilot\" });\n * agent.registerTools(); // 注册内置 Web 工具\n * agent.callbacks.onText = (text) => console.log(text);\n *\n * const result = await agent.chat(\"获取页面标题\");\n * console.log(result.reply);\n * ```\n *\n * 架构位置:\n * ┌──────────────────────────────────────────────────┐\n * │ WebAgent(浏览器端入口) │\n * │ ┌──────────┐ ┌────────────┐ ┌──────────────┐ │\n * │ │ core/ │ │ core/ │ │ web/ │ │\n * │ │ ai-client│ │ agent-loop │ │ (DOM/导航等)│ │\n * │ │ (fetch) │ │ (通用循环) │ │ │ │\n * │ └──────────┘ └────────────┘ └──────────────┘ │\n * └──────────────────────────────────────────────────┘\n */\nimport {\n executeAgentLoop,\n type AgentLoopCallbacks,\n type AgentLoopResult,\n} from \"../core/agent-loop/index.js\";\nimport type { AIMessage } from \"../core/types.js\";\nimport { createAIClient } from \"../core/ai-client/index.js\";\nimport type { AIClient } from \"../core/types.js\";\nimport { ToolRegistry, type ToolDefinition } from \"../core/tool-registry.js\";\nimport { buildSystemPrompt } from \"../core/system-prompt.js\";\nimport { generateSnapshot, type SnapshotOptions } from \"./page-info-tool.js\";\nimport { createDomTool, setActiveRefStore } from \"./dom-tool.js\";\nimport { createNavigateTool } from \"./navigate-tool.js\";\nimport { createPageInfoTool } from \"./page-info-tool.js\";\nimport { createWaitTool } from \"./wait-tool.js\";\nimport { createEvaluateTool } from \"./evaluate-tool.js\";\nimport { RefStore } from \"./ref-store.js\";\n\n// ─── 回调类型 ───\n\n/** WebAgent 事件回调(扩展 AgentLoopCallbacks,增加快照事件) */\nexport type WebAgentCallbacks = AgentLoopCallbacks & {\n /** 自动快照生成完成时触发 */\n onSnapshot?: (snapshot: string) => void;\n};\n\n// ─── 配置 ───\n\nexport type WebAgentOptions = {\n /**\n * 自定义 AI 客户端实例(可选)。\n *\n * 传入后将直接使用该实例进行对话,忽略 token / provider / model / baseURL。\n * 支持 BaseAIClient 或任何实现 AIClient 接口的对象。\n *\n * ```ts\n * const client = new BaseAIClient({ chatHandler: async (params) => { ... } });\n * const agent = new WebAgent({ client });\n * ```\n */\n client?: AIClient;\n /** API 认证 Token (GitHub PAT / OpenAI key / Anthropic key) */\n token?: string;\n /** AI 提供商: \"copilot\" | \"openai\" | \"anthropic\"(默认 \"copilot\") */\n provider?: string;\n /** 模型名称(默认 \"gpt-4o\") */\n model?: string;\n /** 自定义 API 基础 URL(可选,覆盖 provider 默认值) */\n baseURL?: string;\n /** 是否启用干运行模式 */\n dryRun?: boolean;\n /** 自定义系统提示词(不传则使用默认 Web 提示词) */\n systemPrompt?: string;\n /** 最大工具调用轮次(默认 10) */\n maxRounds?: number;\n /** 是否启用多轮对话记忆(默认 false) */\n memory?: boolean;\n /** 是否在每次对话前自动生成页面快照(默认 true) */\n autoSnapshot?: boolean;\n /** 快照选项(视口裁剪、智能剪枝等,autoSnapshot 开启时生效) */\n snapshotOptions?: SnapshotOptions;\n};\n\n// ─── WebAgent 类 ───\n\nexport class WebAgent {\n /** 用户传入的自定义 AI 客户端实例(优先级高于 token/provider) */\n private client?: AIClient;\n private token: string;\n private provider: string;\n private model: string;\n private baseURL?: string;\n private dryRun: boolean;\n private maxRounds: number;\n private customSystemPrompt?: string;\n\n /** 多轮对话记忆开关 */\n private memory: boolean;\n /** 对话历史(memory 开启时自动累积) */\n private history: AIMessage[] = [];\n /** 自动快照开关 */\n private autoSnapshot: boolean;\n /** 快照选项 */\n private snapshotOptions: SnapshotOptions;\n\n /** 工具注册表实例 — 每个 WebAgent 拥有独立的工具集 */\n private registry = new ToolRegistry();\n\n /** 事件回调 — 绑定后可实时获取 Agent 进度,用于 UI 展示 */\n callbacks: WebAgentCallbacks = {};\n\n constructor(options: WebAgentOptions) {\n this.client = options.client;\n this.token = options.token || \"\";\n this.provider = options.provider ?? \"copilot\";\n this.model = options.model ?? \"gpt-4o\";\n this.baseURL = options.baseURL;\n this.dryRun = options.dryRun ?? false;\n this.maxRounds = options.maxRounds ?? 10;\n this.customSystemPrompt = options.systemPrompt;\n this.memory = options.memory ?? false;\n this.autoSnapshot = options.autoSnapshot ?? true;\n this.snapshotOptions = options.snapshotOptions ?? {};\n }\n\n // ─── 工具管理 ───\n\n /** 注册所有内置 Web 工具(dom, navigate, page_info, wait, evaluate) */\n registerTools(): void {\n this.registry.register(createDomTool());\n this.registry.register(createNavigateTool());\n this.registry.register(createPageInfoTool());\n this.registry.register(createWaitTool());\n this.registry.register(createEvaluateTool());\n }\n\n /** 注册一个自定义工具 */\n registerTool(tool: ToolDefinition): void {\n this.registry.register(tool);\n }\n\n /** 获取所有已注册的工具定义列表 */\n getTools(): ToolDefinition[] {\n return this.registry.getDefinitions();\n }\n\n // ─── 配置修改 ───\n\n /** 设置 API Token */\n setToken(token: string): void {\n this.token = token;\n }\n\n /**\n * 设置自定义 AI 客户端实例。\n *\n * 传入后将优先使用该实例进行对话,忽略 token / provider / model / baseURL。\n * 传入 undefined 可恢复使用内置客户端。\n */\n setClient(client: AIClient | undefined): void {\n this.client = client;\n }\n\n /** 设置 AI 提供商 */\n setProvider(provider: string): void {\n this.provider = provider;\n }\n\n /** 设置模型 */\n setModel(model: string): void {\n this.model = model;\n }\n\n /** 切换干运行模式 */\n setDryRun(enabled: boolean): void {\n this.dryRun = enabled;\n }\n\n /** 设置自定义系统提示词 */\n setSystemPrompt(prompt: string): void {\n this.customSystemPrompt = prompt;\n }\n\n /** 开启或关闭多轮对话记忆 */\n setMemory(enabled: boolean): void {\n this.memory = enabled;\n if (!enabled) this.history = [];\n }\n\n /** 获取当前记忆开关状态 */\n getMemory(): boolean {\n return this.memory;\n }\n\n /** 开启或关闭自动快照 */\n setAutoSnapshot(enabled: boolean): void {\n this.autoSnapshot = enabled;\n }\n\n /** 获取当前自动快照开关状态 */\n getAutoSnapshot(): boolean {\n return this.autoSnapshot;\n }\n\n /** 设置快照选项(视口裁剪、智能剪枝等) */\n setSnapshotOptions(options: SnapshotOptions): void {\n this.snapshotOptions = options;\n }\n\n /** 获取当前快照选项 */\n getSnapshotOptions(): SnapshotOptions {\n return { ...this.snapshotOptions };\n }\n\n /** 清空对话历史(不影响记忆开关) */\n clearHistory(): void {\n this.history = [];\n }\n\n // ─── 核心能力 ───\n\n /**\n * 发送消息并获取 AI 回复(含完整工具调用循环)。\n *\n * 内部流程(全部复用 core):\n * 1. createAIClient() → 创建 fetch AI 客户端\n * 2. buildSystemPrompt() → 构建系统提示词\n * 3. executeAgentLoop() → 执行决策循环\n * 4. callbacks → 实时通知 UI\n */\n async chat(message: string): Promise<AgentLoopResult> {\n // 优先使用自定义 client,否则使用内置 createAIClient\n const client = this.client ?? this.createBuiltinClient();\n\n // 复用 core/system-prompt 或使用自定义\n let systemPrompt =\n this.customSystemPrompt ??\n buildSystemPrompt({ tools: this.registry.getDefinitions() });\n\n // ─── 自动快照:注入 system prompt,不污染对话历史 ───\n // 创建本次对话的 RefStore,快照结束后保持活跃,对话结束后清空\n const refStore = new RefStore(globalThis.location?.href);\n setActiveRefStore(refStore);\n\n if (this.autoSnapshot) {\n try {\n const snapshot = generateSnapshot(document.body, {\n maxDepth: 8,\n ...this.snapshotOptions,\n refStore,\n });\n this.callbacks.onSnapshot?.(snapshot);\n\n systemPrompt += `\\n\\n## 当前页面 DOM 快照\\n\\n\\`\\`\\`\\n${snapshot}\\n\\`\\`\\``;\n } catch {\n // 快照失败不阻塞正常流程\n }\n }\n\n // 包装回调:在恢复快照前重置 RefStore,确保新快照的 hash ID 有效\n const wrappedCallbacks: WebAgentCallbacks = {\n ...this.callbacks,\n onBeforeRecoverySnapshot: (newUrl?: string) => {\n // URL 变化 → 清空映射 + 更新 URL 命名空间\n // 元素定位失败 → 仅清空可能失效的映射(URL 不变)\n if (newUrl !== undefined) {\n refStore.reset(newUrl);\n } else {\n refStore.clear();\n }\n // 转发到用户回调(如有设置)\n this.callbacks.onBeforeRecoverySnapshot?.(newUrl);\n },\n };\n\n // 复用 core/agent-loop — 同一份决策循环\n const result = await executeAgentLoop({\n client,\n registry: this.registry,\n systemPrompt,\n message,\n history: this.memory ? this.history : undefined,\n dryRun: this.dryRun,\n maxRounds: this.maxRounds,\n callbacks: wrappedCallbacks,\n });\n\n // 记忆模式:累积对话历史供下次 chat() 使用\n if (this.memory) {\n this.history = result.messages;\n }\n\n // 对话结束,清空 RefStore\n refStore.clear();\n setActiveRefStore(undefined);\n\n return result;\n }\n\n // ─── 内部方法 ───\n\n /**\n * 创建内置 AI 客户端(基于 token / provider / model 配置)。\n *\n * @throws 未设置 token 时抛出 Error\n */\n private createBuiltinClient(): AIClient {\n if (!this.token) {\n throw new Error(\"未设置 Token,请先调用 setToken() 或传入自定义 client\");\n }\n return createAIClient({\n provider: this.provider,\n model: this.model,\n apiKey: this.token,\n baseURL: this.baseURL,\n });\n }\n}\n\n// ─── Re-exports ───\n// 从入口文件统一导出所有公共 API,消费方只需 import from \"agentpage\"\n\nexport { generateSnapshot, type SnapshotOptions } from \"./page-info-tool.js\";\nexport { createDomTool } from \"./dom-tool.js\";\nexport { createNavigateTool } from \"./navigate-tool.js\";\nexport { createPageInfoTool } from \"./page-info-tool.js\";\nexport { createWaitTool } from \"./wait-tool.js\";\nexport { createEvaluateTool } from \"./evaluate-tool.js\";\nexport {\n createProxyExecutor,\n registerToolHandler,\n type ToolCallMessage,\n type ToolCallResponse,\n type ToolExecutorMap,\n} from \"./messaging.js\";\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentpage",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Embeddable AI Agent SDK for browsers — let AI operate your web pages via tool-calling",
5
5
  "keywords": [
6
6
  "ai",