Skip to content

Commit 94b533d

Browse files
CodFrmclaudecyfung1031
authored
💥 脚本猫 AI Agent (#1324)
* ✅ 修复 e2e 测试 service worker 超时并优化等待策略 - gm-api.spec.ts: Phase 2 重启 context 后等待 service worker 注册完成 再交给 fixtures,避免 extensionId fixture 用 10s 全局超时等待失败 - gm-api.spec.ts: 用事件驱动 Promise 替换 500ms 轮询循环, console 结果一出现立即继续 - utils.ts: installScriptByCode 用 DOM 事件等待替代固定延迟: click 后等光标出现,粘贴后等 .view-lines 内容变化 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat: 添加 Agent 聊天功能 新增 AI Agent 聊天模块,包括聊天界面、服务层、数据存储和多语言支持 * ✨ feat: Agent API 通过 GM API 权限系统鉴权 将 Agent API 请求路由到 runtime/gmApi 通道,通过 @PermissionVerify.API 装饰器实现权限验证,脚本首次调用时弹窗确认,防止未授权消耗 API Token。 * ✨ feat: 添加 OPFS 文件浏览器和优化 Agent 工具注册 - 新增 OPFS 文件浏览器页面,支持目录浏览、文件预览和删除操作 - 默认进入 agents/ 目录,支持面包屑导航到根目录 - 支持在 conversation.create 时注册工具,简化多轮对话的工具传递 - 添加 8 种语言的 i18n 翻译 * 🐛 fix: 修复 Agent tool calling 多个问题 - 修复 callLLM 提前转发 done 事件导致客户端过早 resolve 的问题 - 修复 WindowMessage.connect 未使用 "*" targetOrigin 导致沙箱消息被丢弃 - 修复 tool calling 循环中 assistant 消息缺少 tool_calls 字段 - 支持 OpenAI 和 Anthropic 格式的 tool_calls 消息构建 - UI 合并 tool 结果到 assistant 的 ToolCallBlock 展示,过滤 tool/system 消息 - 提取 buildInstance 函数解决装饰器 this 绑定问题,使用 uuidv4 生成 ID * 🎨 style: 统一代码格式化 * ✨ feat: 添加 Agent 消息元数据展示和操作工具条 - ChatMessage 类型新增 usage/durationMs/firstTokenMs 字段 - Service Worker 测量 LLM 调用总耗时并通过 done 事件返回 - UI 层捕获首 token 延迟和 token 用量,持久化到消息中 - 新增 MessageToolbar 组件:左侧操作按钮(复制/重新回答/删除),右侧元数据信息(token用量/耗时/TTFT/工具调用数) - 流式期间显示实时计时动画,完成后切换为最终数据 - 删除操作支持二次确认 - 新增 deleteMessages 批量删除辅助函数 - 添加 i18n 多语言支持(8个locale) * ✨ feat: 添加 CATTool 系统和修复沙箱上下文注入 - 新增 CATTool 元数据解析、存储、注册和执行完整链路 - 新增 CAT.agent.tools GM API(install/remove/list/call) - 修复 CATTool 沙箱执行时 args 未定义的问题:使用 compileScriptCodeByResource 包裹代码以启用 with 上下文绑定 - 优化 Agent Chat UI 组件样式 * ♻️ refactor: 拆分 Install Page 为 hooks + 独立视图组件 将 App.tsx (~1130行) 按职责拆分为: - utils.ts: 纯工具函数和常量 - hooks.tsx: useInstallData() 自定义 hook - components/CATToolInstallView.tsx: CATTool 安装视图 - components/ScriptInstallView.tsx: UserScript/Subscribe 安装视图 - App.tsx: 精简为路由分发 (~57行) * 📝 docs: 添加 CATTool 示例和使用文档 新增 example/agents/tools/ 目录: - hello_world.js: 最简 CATTool 示例 - text_processor.js: 多参数和 enum 类型 - json_formatter.js: JSON 处理和错误处理 - weather_query.js: GM_xmlhttpRequest 网络请求 - use_cattool.js: 通过脚本 API 安装和调用 CATTool - README.md: 格式说明和测试方法 * 🐛 fix: 修复 Agent 对话上下文丢失和流式错误处理 - 修复历史消息未携带 toolCalls 字段导致多轮 tool calling 上下文丢失 - 添加 OpenAI 流式 API 错误响应处理 - 添加 /new 命令和 clearMessages 接口用于清空对话上下文 - UI 发送消息时正确传递 toolCallId 和 toolCalls * ♻️ refactor: 统一 UI/脚本聊天通道,规范字段命名,补充测试 - 删除独立的 handleChat,UI 和 Sandbox 统一走 conversationChat 通道 - 持久化责任从 UI 移交到 SW,简化前端逻辑 - 字段命名统一:createdAt/updatedAt → createtime/updatetime - 支持 UI 动态切换 modelId - 大幅补充 Agent 相关测试(流式解析、tool calling 循环、CATTool 边界等) * ✨ feat: 添加对话命令系统、修复删除弹框消失和会话 URL 同步 - 对话实例支持 / 命令拦截机制,内置 /new 命令清空对话 - 脚本可通过 commands 选项注册自定义命令并覆盖内置命令 - 修复会话列表删除确认弹框因鼠标移开导致隐藏的问题 - 会话 ID 同步到 URL 参数,刷新页面保持当前会话选中 - 新增命令机制 8 个单元测试 * ✨ feat: CATTool 安装流程改进、模型配置存储迁移 - CATTool 安装改为打开安装页面确认,支持脚本来源追踪和更新提示 - CATToolRepo 索引/数据分离(tools.json 索引 + data/<id>.json 完整记录) - CATTool UUID 由 SW 统一生成,通过全局映射支持 GM API 权限验证 - AgentModelConfig 从 SystemConfig 迁移到独立的 AgentModelRepo - AgentModelRepo.getDefaultModelId 使用 chrome.storage.local 直接存取 - cancelCATToolInstall 统一为 try/finally 模式 - openCATToolInstallPage 增加 tab.id 空值检查 * 🔧 fix: 修复 lint 错误和 JSX 字面字符串警告 * 🔧 style: prettier 格式化 * ✨ feat: 实现 CAT.agent.dom 页面 DOM 操作能力 实现 Agent 框架的 DOM 操作 API,支持两种模式: - 默认模式:通过 chrome.scripting.executeScript 操作 - trusted 模式:通过 chrome.debugger CDP 实现真实用户输入 新增 8 个 Agent 工具:dom_list_tabs, dom_navigate, dom_read_page, dom_screenshot, dom_click, dom_fill, dom_scroll, dom_wait_for readPage 支持 summary/detail 分层读取,控制上下文大小; 操作后自动返回 ActionResult(跳转/新 tab/dialog 检测); debugger 权限放在 optional_permissions 动态申请。 * ✨ feat: 添加 Skill 系统 — 提示词 + CATTool + 参考资料的组合能力 - 新增 Skill 类型定义(SkillSummary, SkillRecord, SkillApiRequest 等) - SKILL.md 解析器(YAML frontmatter + markdown body) - SkillRepo OPFS 存储(registry + scripts + references) - AgentService 集成:loadSkills, installSkill, resolveSkills - 2 个 meta-tool:execute_skill_tool, read_reference - GM API: CAT.agent.skills(list/get/install/remove) - UI: ChatInput 增加 Skills 选择器 * ✅ 添加 Agent 核心逻辑 E2E 测试 - agent-fixtures.ts: Mock LLM 基础设施(context.route 拦截 + OpenAI SSE 响应) - agent-conversation.spec.ts: 基础对话、Tool Calling、多轮上下文保持 - agent-cattool.spec.ts: CATTool 安装/调用/删除、CATTool + 对话联动 * ♻️ refactor: Skill 系统三层渐进加载 — system prompt 只注入摘要,按需加载完整提示词和工具 resolveSkills 不再将完整 SKILL.md 正文和 CATTool schema 一次性注入 system message, 改为三层渐进加载:1) 摘要列表 2) load_skill 按需获取 prompt 3) execute_skill_tool/read_reference 按需执行。 execute_skill_tool 新增 skill_name 参数确定工具作用域,CATTool 脚本改为执行时按需加载。 新增 14 个 Skill 系统单元测试。 * ✨ feat: 实现临时会话(ephemeral conversation)支持 ephemeral 模式下会话不持久化到 OPFS、不加载内置工具和 Skills, 工具完全由脚本提供,消息历史由脚本端内存管理。 SW 端无状态处理,仅解析 model 配置调用 LLM。 * ✨ feat: 实现 MCP Client — Streamable HTTP transport + Tools/Resources/Prompts 实现完整的 MCP (Model Context Protocol) 客户端: - MCPClient: JSON-RPC 2.0 over HTTP POST,Session ID 管理,认证头 - MCPService: 连接池管理,懒连接,MCP 工具自动注册到 ToolRegistry - MCPServerRepo: chrome.storage 持久化服务器配置 - GM API: CAT.agent.mcp(SW + Content 双侧),权限验证 - UI: AgentMcp 页面 — 服务器 CRUD、启用/禁用、测试连接 - 单元测试覆盖 MCPClient、MCPToolExecutor、MCPService * 🐛 fix: listModels 过滤 __default__ key 避免 ModelCard 崩溃 agent_model:__default__ 存储的是默认模型 ID(字符串),被 find() 当作 AgentModelConfig 返回,导致 UI 访问 model.apiKey.length 时报 TypeError。 * 🐛 fix: OpenAI provider 支持 reasoning_content 思考过程展示并修复 usage/choices 处理顺序 - 解析 SSE delta 中的 reasoning_content 字段,发出 thinking_delta 事件(兼容 deepseek/o-series) - 将 usage 检查移到 choices 处理之后,修复最后一个 chunk 同时包含 tool_call 增量和 usage 时丢失数据的问题 - Service Worker callLLM() 收集 thinking 内容并持久化到 assistant 消息 - 修复 vitest.config.ts 中重复 exclude 导致 e2e 测试被误执行的问题 * ✨ feat: Skill 管理页面 — 列表/安装/编辑/卸载 + 消息通道 + 单元测试 - 注册 installSkill/removeSkill 消息处理到 AgentService.init() - AgentClient 新增 installSkill/removeSkill 方法 - 新建 AgentSkills.tsx:Skill 列表卡片、详情编辑弹窗、URL/粘贴安装弹窗 - 替换 /agent/skills 路由为实际管理页面 - 8 个语言文件新增 19 个 agent_skills_* 翻译 key - 新增 9 个单元测试覆盖安装/卸载/消息注册核心流程 * 🧹 chore: 移除未使用的 ComingSoon 组件 * ✨ feat: Skill ZIP 包一键安装支持 新增 parseSkillZip 解析 ZIP 包(SKILL.md + tools/*.js + references/*), UI 增加 ZIP 上传 Tab,支持嵌套目录结构,含完整单元测试和端到端集成测试。 * 🐛 fix: trusted 模式 debugger 权限通过 confirm 页面请求 chrome.permissions.request() 在 MV3 Service Worker 中无法调用(需要用户手势), 改为打开 confirm.html 确认页面,用户点击授权按钮后通过 runtime.sendMessage 通知 SW。 * ♻️ refactor: 移除 execute_skill_tool,load_skill 时按需注册 CATTool 为独立 LLM tool 将三层 meta-tool 设计(load_skill → execute_skill_tool → CATTool)简化为两层: - load_skill 调用时动态注册该 skill 的 CATTool 为独立工具({skill}__{tool} 格式) - callLLMWithToolLoop 每轮重新获取工具定义以发现新注册的工具 - 对话结束后清理动态注册的工具和 meta-tools - 新增 prefixToolDefinition 辅助函数 * ✨ feat: MCP 服务器详情面板 + 测试连接 loading 优化 - 新增 ServerDetailDrawer:展示 Tools/Resources/Prompts 三大能力 - 卡片操作栏增加"详情"按钮,通过 MCPClient 直接查询服务器能力 - 测试连接按钮增加 loading 状态 - 编辑 Modal 底部增加测试连接按钮,填完表单可直接测试 - 补充 8 个 locale 文件的 i18n key * ✨ feat: Skill 安装优化 — 仅 ZIP 包 + 拖拽 + 独立安装页面 - AgentService 新增 prepareSkillInstall/getSkillInstallData/completeSkillInstall/cancelSkillInstall 缓存接口 - install.html 支持 ?skill=<uuid> 参数,解析 ZIP 并展示 Skill 详情 - 新增 SkillInstallView 组件(显示 prompt/tools/references/安全警告) - MainLayout 拖拽支持 .zip 文件,自动走 Skill 安装流程 - AgentSkills 页面移除 InstallSkillModal,改为文件选择器直接打开安装页面 * ✨ feat: 流式事件增强 + 淘宝 Skill 示例 + 输入法修复 - 新增 tool_call_complete / new_message 流式事件,支持工具调用后新建 assistant 消息 - 新增淘宝网页助手 Skill 示例(搜索结果/商品详情/评价提取) - 修复中文输入法回车误触发消息发送(isComposing 判断) - 代码格式化(Prettier) * 🧹 chore: 修复 prettier lint 错误 + eslint 忽略 .claude 目录 - eslint.config.mjs ignores 新增 .claude/,避免 lint worktree 残留文件 - 自动修复多个文件的 prettier 格式问题 * 🐛 fix: 修复测试和类型检查问题 - confirm/App.tsx: 提取 PermissionConfirmRequest 组件,修复倒计时使用 useEffect - ChromePermissionRequest.test.tsx: 动态 import 改为静态 import,修复 nodenext 模块解析错误和 isolate=false 下的元素重复 - agent_dom_cdp.test.ts: vi.mock 改为 vi.spyOn,修复 isolate=false 下 mock 失效;超时测试提前 catch 避免 unhandled rejection - gm_api.test.ts: 补充 CAT.agent.dom 和 CAT.agent.mcp 沙盒可访问性测试 * feat: 会话附件支持 — 工具产出图片/文件展示 - 新增 Attachment/AttachmentData/ToolResultWithAttachments 类型 - AgentChatRepo 增加附件 OPFS 二进制存储(支持 base64 和 Blob) - ToolRegistry 检测结构化返回值,自动保存附件到 OPFS - dom_screenshot 返回图片附件而非 base64 文本 - MCPToolExecutor 检测 MCP image content 并转换为附件 - CATTool 脚本可通过返回 ToolResultWithAttachments 产出文件附件 - AgentService 传播附件元数据到 UI 事件和持久化消息 - ToolCallBlock 新增 AttachmentImage(懒加载+全屏预览)和 AttachmentFile(下载)组件 - ChatArea 处理 tool_call_complete 和 new_message 流式事件 - 附件数据不发送给 LLM,仅在 UI 中展示 - 新增 25 个单元测试覆盖核心逻辑 * fix: skill CATTool 权限验证失败 — UUID map 直接携带 grants skill 的 CATTool 通过 load_skill 动态注册,不在 catToolRepo 中, 导致 gm_api 通过 agentService.getCATToolGrants 查不到权限。 改为在 cattoolUuidMap 中直接携带 grants,parseRequest 从 map 获取, 绕过 repo 查询。 * feat: 移除 MCP 脚本 API + Tools/Skills 写操作权限强化 - 移除 CAT.agent.mcp 脚本 API(安全风险高,脚本可随意添加外部服务器) - MCP 管理功能本身保留,UI 页面仍可正常管理 - Tools/Skills 写操作(install/remove)每次弹窗确认,除非有持久化授权 - 读操作(list/get/call)保持缓存逻辑不变 - 权限系统新增 persistentOnly 标志,跳过临时缓存仅查 DB * fix: 修复代码块高亮渲染显示 [object Object] + 暗色模式适配 - MarkdownRenderer: children 直接传递 ReactNode 而非 String() 转换,修复 rehype-highlight 生成的高亮元素被序列化为 [object Object] 的问题 - 新增 extractText 辅助函数,从 React 节点树递归提取纯文本用于复制功能 - styles.css: 添加 body[arco-theme="dark"] 作用域下的 GitHub Dark 代码高亮主题 * fix: debugger 权限改为安装时授予,移除动态请求流程 Chrome 不允许 debugger 作为可选权限动态请求,chrome.permissions.request 直接抛异常被 catch 吞掉导致始终返回 denied。将 debugger 移入 permissions, 删除 ensureDebuggerPermission/hasDebuggerPermission 及确认页面相关代码。 * feat: 用户消息编辑重新发送 + 修复工具调用展开内容截断 - 用户消息 hover 显示编辑按钮,支持编辑后重新发送 - 编辑容器适配亮色/深色模式(边框、背景、阴影) - 添加 8 种语言的 i18n 翻译 - 修复 ToolCallBlock 流式加载期间展开后 result 显示不全, 改用 ResizeObserver 监听内容尺寸变化自动更新 max-height * fix: MessageToolbar 移除 hasMetadata 提前返回,确保操作按钮始终可用 * feat: agent DOM monitor + CDP 重构 + browser_automation skill 示例 - 新增 startMonitor/stopMonitor API,监控 dialog 和 DOM 节点变化 - CDP agent_dom 重构:优化截图、导航、脚本执行等逻辑 - ReadPage 新增 removeTags 选项 - 新增 browser_automation skill 示例(browser_action/smart_fill/wait_for_navigation) * feat: Anthropic/OpenAI prompt caching 支持 + 缓存用量展示 - Anthropic: system 消息和工具定义添加 cache_control,解析 message_start 中的 cache token 用量 - OpenAI: 解析 prompt_tokens_details.cached_tokens - UI: MessageToolbar 展示 cache read/write token 信息 - 补充缓存相关单元测试 * feat: 系统提示词模块化 + 聊天逻辑重构 + DOM 监控优化 - 提取 buildSystemPrompt() 支持三层组合(内置/用户/Skill) - 提取 chat_utils 模块(消息合并、分组、重新生成计算),含完整测试 - click_and_wait 改为直接 trusted 点击,新增 peekMonitor 轮询 API - debugger 连接复用优化,避免重复 attach/detach - OpenAI 流式解析兼容 Grok(每 chunk 附带 usage) - ephemeral 会话禁用 prompt cache,AgentModelConfig 支持可选 maxTokens - MCP 服务启动时自动连接已启用的服务器 - 用户消息新增复制/重新生成按钮,Select 下拉容器修复 * fix: 修复 isolate:false 下 UUID mock 冲突导致的测试失败 * 🐛 修复和优化 ScriptEditor 问题 (#1258) * 优化 ScriptEditor * Update ScriptEditor.tsx * Update ScriptEditor.tsx * ✨ 调整隐藏编辑框侧边栏位置 #1185 (#1254) * ✨ 调整隐藏编辑框侧边栏位置 #1185 * 图标展示反了 * 调整 `confirm_leave_page` / `script_modified_leave_confirm` 提示避免按「新增脚本」时弹出提示 * Update ScriptEditor.tsx * css 布局修正 * 统一 .focus 为 delayedEditorFocus * 🐛 修复 ScriptEditor review 问题 - 将模块级 cid 变量改为 useRef,避免多实例共享 timer - hotKeys.current.length = 0 改为 hotKeys.current = [],避免原地修改影响旧引用 - handleDeleteEditor 在 setEditors 回调内重新计算 index,修复竞态问题 - 移除冗余的 scriptList.find 查找 - 修复「袑始化」笔误为「初始化」 - 移除重复的 position: absolute 声明 --------- Co-authored-by: wangyizhi <yz@ggnb.top> * chore: bump version to 1.4.0-beta * fix: 构建时将 prerelease 版本号转换为 Chrome 兼容格式 rspack build 阶段未转换 manifest.json 中的版本号,导致 "1.4.0-beta" 等非法版本使扩展无法加载,E2E 测试全部失败。 复用 pack.js 中的转换逻辑,在 CopyRspackPlugin transform 中将版本号转为纯数字格式(如 1.4.0.1100)。 * refactor: 提取 toChromeVersion 为公共函数 将 rspack.config.ts 和 scripts/pack.js 中重复的版本转换逻辑 提取到 scripts/version.js 中复用。 * feat: 新增 CATTool 管理页面 - 新增 AgentCATool.tsx 页面,支持查看已安装 CATTool 的详情(参数、权限、代码、来源脚本)及删除操作 - 在 service_worker/agent.ts 注册 removeCATTool group handler,删除时同步注销 toolRegistry - 在 AgentClient 添加 removeCATTool() 方法 - 在侧边栏 Agent 子菜单新增 CATTool 入口,菜单顺序调整为 CATTool → Skills → MCP - 为全部 8 个语言包添加 agent_catool_* 翻译键 * fix: 修复重新生成用户消息时内容重复的 bug 点击用户消息的"重新生成"时,Service Worker 会将已存在于 storage 中的用户消息再次 appendMessage,导致 loadMessages() 后界面出现两条相同的用户消息,同时 LLM 上下文也包含重复的 user message。 新增 skipSaveUserMessage 参数:前端将 skipUserMessage 标志 透传给 Service Worker,收到后跳过用户消息的 LLM 上下文追加 和 storage 持久化,因为消息已在 existingMessages 中存在。 同步新增单元测试覆盖默认行为与 bug 回归场景。 * feat: 错误处理三件套(超时、重试、错误分类)及单元测试 - CATTool 执行超时:Promise.race + 30s 限制,超时抛 errorCode="tool_timeout" - LLM API 指数退避重试:withRetry,429/5xx/network 可重试,最多 3 次 - 明确错误类型:ChatStreamEvent / StreamChunk 携带 errorCode,cat_agent.ts 透传到 Error 对象 - 新增 35 个单元测试覆盖上述核心逻辑 * refactor: 删除内置 DOM Tools,转为 browser-automation Skill 中的 CATTool - 删除 registerDomTools() 及 dom_tools.ts/dom_tools.test.ts - 新增 5 个基础 CATTool:dom_list_tabs、dom_navigate、dom_screenshot、dom_scroll、dom_wait_for - 优化 smart_fill:去掉两步策略,直接使用 trusted 模式填充 - 完善 click_and_wait/browser_action 错误处理:每个异步操作 try-catch,降级策略 - 更新 SKILL.md 文档,补充所有工具说明和使用示例 * refactor: 优化 Agent 系统提示词,解耦工具实现细节 - 重写系统提示词:移除所有硬编码的工具名和实现细节(DOM 工具、trusted 模式、30s 超时等), 改为通用行为准则,具体工具知识由 tool description 和 skill 提示词承担 - 新增 Planning 模块:复杂任务先提出计划等用户确认,执行中偏航时暂停并更新计划 - 新增 Safety 模块:不可逆操作确认、不编造敏感数据、不绕过网站安全机制 - 提取 SKILL_SUFFIX_HEADER 常量:skill 摘要模板从 agent.ts 硬编码迁移到 system_prompt.ts 统一管理 - 精简 Loop Detection 和 Escalation 规则 * refactor: 优化 browser-automation Skill 工具命名和提示词 - 去掉 dom_ 前缀:dom_list_tabs → list_tabs, dom_navigate → navigate 等 - 工具 @description 改为英文,更具体地描述返回值和使用场景 - SKILL.md 重写:工具分类更清晰,示例更自然,scenario 技巧独立成节 * feat: ChatMessage.content 升级为 string | ContentBlock[] 支持多模态内容 将 Agent 消息内容从纯文本升级为结构化的 ContentBlock 数组,支持 text/image/file/audio 四种类型的图文混排,运行时向后兼容旧数据。 - 新增 ContentBlock 类型体系(TextBlock/ImageBlock/FileBlock/AudioBlock) - 新增 content_utils.ts 工具函数(getTextContent/normalizeContent/isContentBlocks) - Provider 层支持 ContentBlock[] → Anthropic/OpenAI 原生格式转换 - Agent 服务层新增 resolveAttachments() 批量预解析 OPFS 附件为 base64 - 新增 ContentBlockRenderer 和 AttachmentRenderers UI 组件 - 流式处理支持 content_block_start/content_block_complete 事件 - 存储清理扫描 ContentBlock[] 中的 attachmentId - 脚本 API(cat_agent.ts + scriptcat.d.ts)同步更新 * test: 补充 Agent 核心路径单元测试和 E2E 测试 单元测试(+40 tests): - agent.test.ts: callLLM 流式解析(OpenAI/Anthropic/错误/重试/abort)、 callLLMWithToolLoop(单轮/多轮/maxIterations/附件回写)、 handleConversationChat(标题更新/ephemeral/modelId覆盖/skill预加载) - mcp_client.test.ts: callTool 无参数/JSON-RPC错误/空content、close后操作、通知失败 - cattool_executor.test.ts: boolean/number 边界值转换、空params过滤 E2E 测试(+4 tests): - agent-error-handling: LLM 500重试、401认证错误、对话abort - agent-skill: Skill安装→load_skill→动态CATTool调用全链路 * feat: CATTool 支持 @require 外部库加载 复用 userscript 已有的 ResourceService 下载/缓存机制,为 CATTool 增加 @require 元数据支持。安装时自动下载并缓存外部 JS 库,执行时通过 compileScriptCodeByResource 注入,使 CATTool 可使用 SheetJS、docx 等 第三方库。 * fix: 修复 screenshot CATTool 截图失败的问题 CAT.agent.dom.screenshot() 返回的是 data URL 字符串, 而非 { dataUrl, mediaType } 对象,导致 result.dataUrl 为 undefined, 始终走入"未获取到图片数据"的错误分支。 * feat: Skill 管理支持查看工具代码和刷新缓存 - SkillDetailModal 中工具 Tag 可点击查看源代码(ToolCodeModal) - SkillCard 新增刷新按钮,从 OPFS 重新加载 Skill 到 service worker 缓存 - AgentService/AgentClient 新增 refreshSkill 方法 - 补充 8 个 locale 文件的 i18n key * fix: Skill 更新时清理旧的 scripts/references 文件 saveSkill() 更新 Skill 时只写入新文件但不删除旧文件,导致 getSkillScripts() 遍历目录时读到残留的旧 CATTool 数据。 修复:写入前先 removeDirectory 清空子目录再重新写入。 * feat: Agent 定时任务系统(internal/event 双模式) 支持 cron 定时触发 Agent 任务: - internal 模式:SW 自主执行 LLM 调用,结果存入对话历史 - event 模式:通过 EmitEvent 链路通知脚本,脚本自行处理执行 - 脚本 API:CAT.agent.task.create/list/get/update/remove/runNow/addListener - UI 管理页面:卡片列表、创建编辑、运行历史 - chrome.alarms 每分钟 tick 调度,runningTasks 防并发 * fix: cdpClick 增加元素遮挡检测,避免点击到覆盖层 点击前使用 elementFromPoint 检查目标坐标处的实际元素, 如果被 modal/header 等遮挡则抛出包含遮挡元素描述的错误。 * fix: 补全 Agent GM API 注册和 ESLint grant 白名单 - AgentService 添加 handleDomApi 代理方法,转发到 domService - gm_api.ts 补充 GMAgentDomApi import 触发装饰器注册 - compat-grant.js 添加 CAT.agent.dom 和 CAT.agent.task 到 grant 白名单 - 补充 GM API 注册完整性测试和 handleDomApi 单元测试 * refactor: 迁移 Agent 示例到独立 skills 仓库 Skills、CATTools、示例脚本已迁移到 github.com/scriptscat/skills * feat: Skill Config 系统 — 支持 SKILL.md 声明配置字段并在沙箱中注入 CAT_CONFIG 在 SKILL.md frontmatter 中通过 config 块声明配置字段(text/number/select/switch), 用户在 Skills 管理 UI 填写值(支持 secret 遮蔽),CATTool 执行时以 CAT_CONFIG 全局 对象注入沙箱,使脚本可安全访问 API Key 等敏感配置。 主要改动: - 用 yaml 包重写 frontmatter 解析器,支持嵌套 config 块 - 新增 SkillConfigField 类型,扩展 SkillMetadata/Record/Summary - CATToolExecutor → offscreen → sandbox 全链路透传 configValues - SkillRepo 新增 config_values.json 读写 - Agent Service 注册 getSkillConfigValues/saveSkillConfig handler - UI: SkillConfigModal 配置表单 + SkillInstallView 配置预览 - scriptcat.d.ts 声明 CAT_CONFIG 全局变量 * docs: 同步 scriptcat.d.ts 类型声明并添加中文版 - scriptcat.d.ts: 全量同步实际实现,所有 GM_*/CAT_*/CAT.agent API 添加英文 JSDoc - scriptcat.zh-CN.d.ts: 新增全量中文版类型声明(含 GM_*/CAT_*/CAT.agent 所有 API) - CAT_CONFIG 类型改为 Readonly<Record<string, unknown>> 与 Object.freeze 实现一致 * refactor: 明确 Agent GM API 返回值类型,消除 unknown/any - 新增 JsonValue 通用类型用于 CATTool 动态返回值 - cat_agent_tools.ts: install→CATToolRecord, remove→boolean, list→CATToolSummary[], call→JsonValue - cat_agent_dom.ts: 所有 DOM 操作方法使用具体返回类型(TabInfo[], ActionResult 等) - cat_agent_skills.ts: list→SkillSummary[], get→SkillRecord|null, install→SkillRecord, remove→boolean - agent.ts: handleToolsApi/handleSkillsApi/callCATTool 返回类型同步更新 - cattool_executor.ts: execute 返回 JsonValue * fix: 修正 scriptcat.d.ts 类型声明与源码实现的差异 - stopMonitor 返回类型 Promise<void> → Promise<MonitorResult>,新增 MonitorResult 接口 - tools.list() 返回类型 CATToolRecord[] → CATToolSummary[](不含 code) - SkillSummary 新增 hasConfig 字段 - SkillRecord 新增 config 字段及 SkillConfigField 接口 - 同步更新中文版 scriptcat.zh-CN.d.ts * fix: 修复 E2E 测试 flaky 问题 - agent-fixtures: 复用 Phase 1 的 extensionId 避免 service worker 已终止时 waitForEvent 永久挂起,主动导航触发 service worker 启动 - options.spec: 用 .menu-tools 精确定位"工具"菜单项,避免匹配 CATool 子菜单 - playwright.config: 本地也启用 1 次重试,容错 Chrome headless 偶发启动问题 * feat: 允许 CATTool 通过 @timeout 元数据自定义超时时间 默认仍为 30 秒,支持在 ==CATTool== 头中声明 @timeout(秒)覆盖默认值, 适用于网络爬取、大文件处理等耗时工具场景。 * fix: 修复 ESLint 警告并优化 E2E service worker 就绪逻辑 - 消除 react-hooks/exhaustive-deps 警告,添加 eslint-disable 注释 - JSX 中字符串字面量用花括号包裹,修复 jsx-no-literals 规则 - E2E fixtures 优化 Phase 2 service worker 竞态处理 * fix: 修复亮色模式下用户消息气泡字体颜色问题 - 亮色模式使用 var(--color-text-1) 深色文字,暗色模式使用白色 - 用户消息改用 getTextContent 纯文本渲染 - 版本号升级至 1.5.0-alpha * feat: 将 prompt cache 控制权交给调用方,默认启用缓存 ephemeral 路径不再硬编码 cache: false,改为透传调用方的 cache 参数。 ConversationCreateOptions 新增 cache 字段,CAT API 全链路支持。 * ⚡️ CI 支持 feature/* 分支构建、Agent 错误持久化及 UI 优化 - build.yaml/test.yaml 增加 feature/* 分支触发构建和测试 - Agent 错误消息持久化到 OPFS,刷新后仍可见 - 修复主布局全屏溢出问题 - 优化错误消息展示样式,支持暗色主题 * ⚡️ Anthropic max_tokens 必填修复及模型配置 UI 支持自定义 max tokens - Anthropic API 要求 max_tokens 必传,未配置时默认 16384 - OpenAI 仅在用户配置时传 max_tokens - 模型配置弹窗新增 Max Output Tokens 输入框 * ⚡️ CAT.agent.model 只读 API 及多 tool_call 并行修复 - 新增 CAT.agent.model API(list/get/getDefault),隐藏 apiKey - 添加 AgentModelSafeConfig 类型和 ModelApiRequest 类型 - Content/ServiceWorker 两层 GMApi 注册(cat_agent_model.ts / gm_agent_model.ts) - 修复 callLLMWithToolLoop 同一轮多个 tool_call 丢失问题 - 补充 scriptcat.d.ts 类型声明(CATAgentModel namespace) - 完整测试覆盖(100 tests passed) * 🎨 Prettier 格式化修正 * ♻️ 统一 Model/MCP 数据加载方式,UI 页面通过 SW 消息通道获取数据 将 AgentProvider、AgentChat、AgentTasks、AgentMcp 四个 UI 页面从直接 实例化 Repo/Client 改为通过 AgentClient 消息通道调用 SW,与脚本列表的 ScriptClient 模式保持一致。 * ✨ 支持图片输入与模型图片生成 - ChatInput: 支持粘贴/拖放/文件选择器添加图片,预览缩略图 - ChatArea: 发送前将附件保存到 OPFS - MessageItem: 用户消息渲染图片附件 - OpenAI Provider: 解析 GPT-4o 图片生成响应(数组格式 delta.content) - agent.ts: callLLM 收集生成图片并保存到 OPFS,callLLMWithToolLoop 持久化 ContentBlock[] * ✨ Provider 图标识别、模型能力检测及模型服务卡片布局修复 - 新增 model_utils 模块:按模型名/URL 自动检测 provider、视觉输入和图片输出能力 - 新增 ProviderIcon 组件:为各 provider 显示对应 SVG 图标 - 模型服务页卡片恢复一排两个布局,修复分组导致每组独占一行的问题 - Anthropic provider 默认 max_tokens 设为 16384 并同步更新测试 - 模型选择器按 provider 分组显示,编辑时恢复已缓存的可用模型列表 - MCP 服务器编辑格式化修正 * 🐛 修复 Gemini 图片输出检测:仅 Gemini 2.0 Flash 支持 收紧 supportsImageOutputByModelId 判断条件,排除 gemini-1.5-pro、 gemini-3-flash-preview、gemini-2.0-flash-lite 等不支持图片生成的模型 * ✨ 支持用户手动标记模型视觉输入和图片输出能力 自动检测可能对未知模型或第三方兼容 API 不准确,新增 supportsVision / supportsImageOutput 字段允许用户在模型编辑中手动覆盖自动检测结果。同时补充 Gemini 3+ image 模型的自动检测规则。 * 🐛 修复 --isolate=false 下 vi.mock 不稳定导致的测试偶发失败 MCPService 改为依赖注入(clientFactory + repo),测试不再依赖 vi.mock; tool_registry 测试移除不可靠的 uuid mock,改为检查 id 非空。 * ✨ 支持禁用 tools 及 Markdown 图片预览增强 - 新增 enableTools 开关,允许禁用工具调用(图片生成模型场景) - 提取 LLM 回复中 markdown 内联 base64 图片为附件 - MarkdownRenderer 支持 data:image URL、图片点击预览、memo 优化 - ChatInput 增加工具启用/禁用切换按钮 * 处理e2e问题加上缓存 * ♻️ 合并 CATTool 到 Skill 系统,统一为 execute_skill_script 将独立的 CATTool/Tools 概念合并进 Skill 系统: - 重命名 CATTool 类型为 SkillScript(CATToolRecord → SkillScriptRecord 等) - 用 execute_skill_script meta-tool 替代动态注册 skillname__toolname 模式 - 删除 CAT.agent.tools GM API,统一到 CAT.agent.skills - 新增 CAT.agent.skills.call() 用于脚本内直接调用 skill script - 删除 CATTool 独立安装流程、UI 页面和相关存储 - 更新 system prompt 描述 execute_skill_script 使用方式 * 🐛 修复 CI 测试失败:navigator mock 污染和 e2e fixture 两阶段启动不可靠 - agent_chat.test.ts: 改用 Object.defineProperty 只 mock navigator.storage, 避免 spread 操作丢失 getter 属性(userAgent)导致 react-dom 初始化崩溃 - agent-fixtures.ts: 重构为单 context 方案,去掉关闭→重启浏览器的两阶段流程, 避免 CI 上 Linux headless Chrome profile 持久化不可靠导致扩展加载失败 * 🐛 修复 CI 测试失败:navigator mock 污染和 e2e fixture 两阶段启动不可靠 * ♻️ 重命名 ==CATTool== 为 ==SkillScript==,清除所有 CATTool 残留 - ==CATTool== / ==/CATTool== 头格式改为 ==SkillScript== / ==/SkillScript== - UUID 前缀从 cattool- 改为 skillscript- - .cattool.js 文件扩展名改为 .skill.js - ScriptInfo.cattool 字段改为 skillScript - 变量/函数名中所有 cattool/CATTool 引用统一为 skillScript * ♻️ 重构 Agent E2E 测试:两阶段启动 + 共享 fixture + 修复 Repo 缓存 bug - 提取两阶段启动逻辑到 fixtures.ts (testWithUserScripts) - 提取共享工具函数到 utils.ts (patchScriptCode, autoApprovePermissions, runTestScript, runInlineTestScript) - agent-fixtures 在 Phase 1 写入 mock model 配置,修复 Repo enableCache() 导致的 "No model configured" 问题 - 简化 gm-api.spec.ts,复用共享 fixture 和工具函数 * ✨ 实现 Agent 内置工具:web_fetch、web_search、ask_user、agent、task 管理 新增 8 个开箱即用的内置工具,为 Agent 提供基础能力层: - web_fetch: 抓取网页/JSON,Offscreen DOM 解析提取正文 - web_search: DuckDuckGo HTML 搜索 + Google Custom Search API - ask_user: 向用户提问并等待回复,5 分钟超时 - agent: 启动子代理执行独立子任务,排除 ask_user/agent 防止嵌套 - create_task/get_task/update_task/list_tasks: 会话内任务跟踪 基础设施: - Offscreen HTML 提取器(DOM 解析去骨架 + 搜索结果解析) - 搜索引擎配置 repo(chrome.storage) - ChatStreamEvent 新增 ask_user/sub_agent_event 类型 - System prompt 添加内置工具说明 - AskUserBlock UI 组件(提问展示 + 输入回复) * 🐛 修复 Agent 工具测试:替换失效的 vi.mock 别名路径为 mockSender 方案 * ✨ 实现 OPFS 工作区文件系统工具及 CAT.agent.opfs 用户脚本 API - 新增 opfs_write/opfs_read/opfs_list/opfs_delete 四个 Agent 内置工具 - 所有操作限制在 agents/workspace/ 下,sanitizePath 禁止路径穿越 - 新增 CAT.agent.opfs 用户脚本 API(Content + SW 双层),写操作需持久化授权 - 新增 CATAgentOPFS 类型声明及 ESLint grant 兼容 - 新增 27 个单元测试覆盖工具核心、API 注入、Service 路由三层 * ✨ 实现 /compact 命令和自动 compact 机制 - 新增模型上下文窗口映射表(model_context.ts),支持主流模型家族前缀匹配 - 新增 compact 摘要 prompt(compact_prompt.ts),参考 Claude Code 的 analysis + summary 结构 - AgentModelConfig 新增 contextWindow 字段,UI 表单支持配置 - handleConversationChat 新增 compact 分支,支持 /compact [指令] 手动压缩 - callLLMWithToolLoop 中当 inputTokens/contextWindow >= 80% 时自动触发 compact - ChatStreamEvent 新增 compact_done 事件类型 - 新增 27 个单元测试覆盖映射表、摘要提取、手动/自动 compact 逻辑 * ✨ 新增 execute_script 内置工具:支持 page/sandbox 双模式执行 JS - 新建 execute_script 工具,支持 page(DOM 操作)和 sandbox(隔离计算)两种执行环境 - AgentDomService.executeScript 支持 world 参数(MAIN/ISOLATED),返回 { result, tabId } - ExecuteScriptOptions 类型增加 world 字段 - 在 AgentService 中注册为临时内置工具 - 30s 超时保护,带自动清理避免 unhandled rejection * ✨ 实现 Tab 操作工具:get_tab_content、list_tabs、open_tab、close_tab、activate_tab 核心功能:通过 chrome.scripting.executeScript 注入脚本读取客户端渲染后的完整 DOM, 经 Offscreen extractHtmlWithSelectors 转为带 CSS selector 标注的 markdown, 支持 selector 精确提取和 LLM prompt 摘要。 * ✨ 增强截图和 OPFS 能力:区域截图、saveTo 持久化、bloburl 读取 - screenshot 支持 selector 参数,通过 CDP clip 实现指定元素区域截图 - screenshot 支持 saveTo 参数,将截图二进制直接保存到 OPFS workspace - screenshot 返回类型升级为 ScreenshotResult(dataUrl + path + size) - opfs_read 支持 format: "bloburl",通过 Offscreen 创建 blob URL - 提取 opfs_helpers.ts 公共模块,供 opfs_tools 和 agent_dom 复用 - GM API(CAT.agent.dom.screenshot / CAT.agent.opfs.read)同步更新 * ✨ CAT.agent.opfs.write 支持 Blob 和 data URL 二进制写入 - writeWorkspaceFile 支持 Uint8Array、Blob、data URL 字符串三种输入 - data URL 自动检测并解码为原始二进制存储 - OPFSApiRequest write content 类型扩展为 string | Blob - GM API CAT.agent.opfs.write 参数同步更新 * ✨ web_fetch 增加 prompt 摘要功能 & 模型配置支持复制 - web_fetch 工具新增 prompt 参数,支持通过 LLM 对抓取内容进行摘要/提取 - 模型配置页面增加复制按钮,方便基于已有配置快速创建新模型 * ✨ ask_user 工具支持结构化选项(单选/多选) - tool 定义增加 options 和 multiple 参数 - ChatStreamEvent ask_user 事件传递选项字段 - UI 支持 Radio 单选(点击即提交)和 Checkbox 多选(确认提交) - 所有模式下保留文本输入框供自定义回答 - 新增 2 个单元测试覆盖选项传递 * ✨ 新增 Bing/百度搜索引擎 & Agent 设置页面 - 扩展 SearchEngineConfig 支持 bing/baidu,默认引擎改为 Bing - web_search 新增 searchBing/searchBaidu,html_extractor 新增对应解析方法 - 新增摘要模型配置(getSummaryModelId/setSummaryModelId),summarizeContent 优先使用摘要模型 - 新建 AgentSettings 页面:摘要模型选择 + 搜索引擎配置 - 侧边栏/路由注册、8 个 locale 文件新增 i18n keys - 新增 Bing/百度搜索测试用例 * 🐛 修复 Agent 设置与类型定义问题 - 修复 Bing 搜索 URL 移除无效的 count 参数 - 修复 getSummaryModelId 未设置时报错问题 - 修复新会话应使用默认模型而非当前会话模型 - Agent 设置页面添加搜索引擎提示信息和保存成功反馈 - 补全 scriptcat.d.ts 类型定义(Attachment、ScreenshotResult 等) - 修复 NotificationDetails.text 字段类型错误 * ✨ 附件扩展名、readAttachment API、缓存用量追踪与 UI 优化 - 附件 ID 统一追加文件扩展名,便于类型识别 - 新增 CAT.agent.opfs.readAttachment API 读取内部附件存储 - usage 增加 cacheCreationInputTokens/cacheReadInputTokens 追踪 - OPFS 浏览器支持图片预览、文件下载、图标区分 - AskUserBlock 交互卡片 UI 重构(渐变条、pill 选项、内联输入) - ConversationInstance 收集模型生成的图片/文件 content blocks - 模型配置自动检测 supportsVision/supportsImageOutput * 🐛 修复 readAttachment 返回类型断言 * ✨ 支持 Agent 会话后台运行模式 UI 断开后会话继续执行,重新连接后通过 sync 快照恢复进度。 - 新增 RunningConversation 注册表与 broadcastEvent/updateStreamingState 机制 - handleConversationChat 支持 background 参数,断开只移除 listener 不 abort - 新增 attachToConversation 处理器,重连时发送 sync 快照 - UI hooks 新增 attachToConversation、useRunningConversations - ChatArea 自动 attach 运行中会话,ChatInput 增加后台模式开关 - 会话列表显示运行中指示器 - Script API (cat_agent) 支持 background 参数和 attach() 方法 - 26 个单元测试覆盖核心逻辑 * ✨ 任务工具持久化 & OPFS Blob 跨上下文传输修复 - 任务工具精简为 3 个(create/update/list),新增持久化存储和 UI 实时推送 - 新增 TaskListBlock 组件展示任务进度 - 修复 chrome.runtime sendResponse 不支持 Blob 的问题,改用 blobUrl + CAT_fetchBlob 模式 - 简化 readAttachment API,统一返回 Blob 对象 - 新增 read blob 格式支持 - 更新系统提示词与类型定义 * 🐛 修复 agent.test.ts mockRepo 缺少 getAttachment 方法 * 🐛 Offscreen→SW 消息通道改用 postMessage 支持 Blob 传输 Offscreen 转发 GM API 请求到 ServiceWorker 时,原先走 ExtensionMessage (chrome.runtime, JSON 序列化),导致 Blob 等结构化数据丢失。 改为通过 postMessage 通道(结构化克隆)双向传输,所有 GM API 自动受益。 * ♻️ 移除 Offscreen 对 ExtensionMessage 的依赖 所有 Offscreen→SW 通信统一走 ServiceWorkerClientMessage(postMessage), 不再需要 ExtensionMessage(chrome.runtime)。 * 🐛 修复 offscreen 页面 navigator.serviceWorker.controller 为 null 的问题 扩展 offscreen 页面的 controller 通常为 null,改用 navigator.serviceWorker.ready 获取 registration.active 作为 SW 引用。 * ✨ OPFS API 适配 postMessage 通道 & 修复 Blob 传输 - handleOPFSApi 根据 sender 判断通道类型:postMessage 直传 Blob,chrome.runtime 走 blobUrl 中转 - content script middleware 拦截 write Blob 转 blobUrl - offscreen 新增 fetchBlob handler - cat_agent_opfs 客户端兼容两种通道返回 - opfs_read Agent 工具一律返回 blobUrl,避免文件内容进入 LLM 上下文 - 移除 bloburl format,GM API read 只保留 text/blob - saveAttachments 支持无 data 的附件引用(已保存的 imageBlock) - tool_registry 添加错误日志输出 * fix: executeScript 改用 MAIN world,DOM 操作改用 ISOLATED world - executeScript: 动态代码固定 MAIN world(ISOLATED 下 new Function 被 MV3 CSP 拦截) - click/fill/scroll/waitFor/readPage: 静态函数改为 ISOLATED world(纯 DOM 操作无需 eval) - execute_script tool: 移除 world 参数,不再暴露给用户 - ExecuteScriptOptions: 移除 world 字段 - scriptcat.d.ts / scriptcat.zh-CN.d.ts: 同步更新类型定义 * ✨ Agent 多项改进:附件路径迁移、LLM 重试机制、系统提示词优化 - 附件存储从 conversations/attachments 迁移到 workspace/uploads,LLM 可通过 OPFS 路径访问 - Provider 非图片附件改用 OPFS 路径引用,减少 context 占用 - LLM API 调用增加重试机制(最多 5 次,递增延迟),UI 显示倒计时 - 系统提示词优化:强化 loop detection、ask early 策略、工具调用预算 - get_tab_content/web_fetch 必须提供 prompt 参数,引导高效使用 - ToolRegistry 错误信息包含可用工具列表,帮助 LLM 自我纠正 - 编辑消息支持附件增删和粘贴,停止生成时正确标记 tool call 状态 - 修复 SenderRuntime null safety、stopGeneration 竞态、base64 编码性能 - 新增 CAT.agent.model.getSummary API、后台模式 tooltip i18n * 📝 完善 Agent 系统提示词和工具描述 优化内置工具列表说明、子代理使用指引、OPFS 工作区文档, 明确 execute_script 的 MAIN world 限制和 blob URL 访问规则 * ✨ 新增 get_task/delete_task 工具,优化 Agent 工具提示词分层 - task_tools: 新增 get_task(获取完整详情)和 delete_task(删除任务) - 工具 description 精简为能力描述,行为指导移入系统提示词 - 系统提示词 Task Management 重构为 When/When NOT/Workflow/Tips 结构 - ask_user description 精简,推荐选项规范移入系统提示词 - 新增 6 个 task_tools 测试用例 * ✨ 子代理类型系统、Compact 提示词优化与 UI 改进 - 新增 sub_agent_types 模块,支持子代理类型定义与提示词生成 - 重构 compact 提示词,改用结构化 8 段摘要格式提升上下文延续质量 - 优化系统提示词分层架构,支持动态工具描述注入 - 改进子代理 UI 展示(折叠/展开、状态指示、工具调用详情) - 增强 agent 服务端子代理管理与消息流转 * ✨ 工具调用防护、斜杠命令菜单与子代理详情持久化 - 新增 tool_call_guard:检测重复/循环工具调用并注入系统警告 - ChatInput 新增斜杠命令弹出菜单(/ 触发,键盘导航) - 子代理执行详情(消息历史、用量)持久化到 ToolCall 并在 UI 展示 - SubAgentBlock / MessageItem UI 改进 * ✨ 工具调用防护 startIndex 避免重复警告、Task/SubAgent UI 优化与连接测试改进 * ✨ 新增会话导出为 Markdown 功能 会话列表新增下载按钮,点击可将会话导出为 .md 文件, 支持用户/助手/系统消息、thinking 块、工具调用、子代理详情等。 * 🔧 prettier 自动格式化 * 🐛 修复 20 个单元测试失败和 e2e 问题 - agent_dom.test: readPage/executeScript world 参数期望与实现对齐 - execute_script.test: 删除不支持的 world 参数测试用例 - opfs_tools.test: 添加 setCreateBlobUrlFn 初始化,content→blobUrl - service_worker/agent.test: handleOPFSApi 添加 sender 参数, handleModelApi 添加 supportsVision/supportsImageOutput, callLLM 流式测试用 fake timers,offscreen mock 改为委托 sender - agent/agent.test: callLLMWithToolLoop 错误测试改为 mock callLLM - gm-api.spec: unwrap 测试 test→testWithUserScripts - agent-error-handling.spec: 删除 401 超时 e2e 测试(单元测试已覆盖) * ✨ opfs_read 支持文本内容读取、搜索提取失败提示、正则校验 - opfs_read 自动检测文本/二进制:文本文件直接返回内容(支持 offset/limit 分页),二进制返回 blob URL - web_search 区分"无结果"和"提取失败",返回 warning 引导 agent 切换引擎 - tab_tools 对无效正则参数抛出明确错误 * ✨ 新增智谱(Zhipu)AI Provider 支持 * ⏪ 还原 md/example 文件的 prettier 格式化,添加 prettierignore 规则 * ⏪ 还原 tsconfig.json/.prettierrc/bug_report 模板的 prettier 格式化 * 💄 格式化合并后的代码(prettier 自动调整) * 🔀 解决 release/v1.4 合并冲突,修复 MainLayout 测试 - 解决 package.json/manifest.json/pnpm-lock.yaml 版本号冲突(保留 1.5.0-alpha) - 补充 MainLayout 测试缺失的 agentClient mock,修复测试超时问题 - 合并 release/v1.4 的变更:navigation_handle、crontab once 示例、pre-commit 改进等 * 🐛 修复 CI 测试失败:移除 vi.resetModules() 避免模块缓存污染 - navigation_handle.test.ts: 改用 resetAttachedForTest() 重置单例, 不再用 vi.resetModules() 清空全局模块缓存(导致 react-dom/chrome mock 重新加载出错) - MainLayout.test.tsx: 移除多余的 waitFor 避免 500ms 超时 * 🐛 E2E: 设置菜单项点击前先滚动到可视区域 Agent 子菜单项增多后,"设置"可能在小窗口中被挤出可视区域 * 🐛 E2E: 用 .menu-setting 精确定位设置菜单项 /setting|设置/ 正则会先匹配到 Agent 子菜单中的 agent_settings(折叠不可见), 改用 .menu-setting class 选择器精确定位 * 🐛 修复测试全局变量污染导致 CI 随机失败 - agent_dom_cdp.test.ts: 保存/恢复 chrome 全局(之前覆盖了完整的 chrome mock, 导致后续测试访问 chrome.tabs.onRemoved 时为 undefined) - opfs_tools.test.ts: stub navigator 时保留 ...globalThis.navigator (之前只有 storage,丢失了 userAgent,导致 react-dom 初始化时 navigator.userAgent.indexOf() 崩溃) * 🔧 修复测试文件 prettier 格式错误 * 🐛 移除 isolate=false,启用测试隔离避免全局状态污染 * 🔧 通用基础设施改进 - CI: build/test 流水线支持 feature/* 分支触发 - vitest: 修正 exclude 模式,确保子目录 node_modules/.claude 被排除 - message/server: SenderRuntime.getExtMessageSender 添加 null 检查, 防止 postMessage 通道(Offscreen→SW)因无 RuntimeMessageSender 崩溃 - message/window_message: connect() 使用 WindowPostMessage 包装, 确保 sandbox(origin:null)→offscreen 的消息不被丢弃 * 🐛 通用修复与代码规范化 - confirm/App.tsx: 定时器从 setTimeout 递归改为 useEffect+setInterval, 修复内存泄漏和渲染循环;提取 PermissionConfirmRequest 组件 - utils.ts: sourceMapTo 添加 chrome.runtime?.getURL 防御, sandbox 环境中 chrome.runtime 不可用时降级为脚本名 - 5 处 eslint-disable react-hooks/exhaustive-deps 注释 - prettier 格式化:CSS/HTML/JSON 文件统一缩进和引号 - .prettierignore: 忽略 *.md 和 example/ - manifest.json: 纯格式化(prettier) - playwright.config.ts: 本地也启用 retry * ⏪ 撤回 eslint-disable 注释和 playwright retry 改动 - 移除 6 处 react-hooks/exhaustive-deps 的 eslint-disable(仅 warn 无需抑制) - 还原 playwright retries 为 CI ? 1 : 0 * ⏪ 还原格式化改动,保持 release/v1.4 原有格式 CSS/HTML/JSON/manifest 等文件还原为 release/v1.4 原始格式 * 🧪 补充 message 包单测:sender 兜底 + connect targetOrigin 根据 PR #1328 Copilot 评审意见,补充两个单测: - SenderRuntime.getExtMessageSender() 在 sender 为 null/undefined/空对象时不崩溃并返回默认值 - WindowMessage.connect() 返回的连接 sendMessage 带 "*" targetOrigin * 🐛 修复权限确认页 beforeunload 监听器泄漏 - 拆分 useEffect:beforeunload 注册与 getPermissionInfo 请求独立 - 提取命名 handler,在 cleanup 中 removeEventListener - beforeunload effect 只依赖 [uuid],避免语言切换时重复注册 - .gitignore 忽略 .omc 本地开发目录 根据 PR #1328 @cyfung1031 评审意见处理 * ⏪ 还原 Prettier 格式化与 eslint-disable 注释 还原与 agent 功能无关的格式化改动,保持 release/v1.4 原有风格: - CSS/HTML/JSON/YAML:还原 prettier 缩进、引号、数组单行化 - src/index.css: 仅保留 --un-default-border-color(UnoCSS 暗色边框修复) - src/manifest.json: 仅保留 "debugger" 权限(Agent CDP 需要) - workflows: 仅保留 feature/* 分支触发 移除未实际需要的代码抑制: - 5 处 react-hooks/exhaustive-deps 的 eslint-disable-next-line(仅 warn 无需抑制) - playwright 本地 retries 还原为 0 * 🐛 修复 UnoCSS 暗色模式默认边框色 暗色模式下 UnoCSS 的 --un-default-border-color 默认使用浅色值, 导致 border-* 类的边框在暗色主题下与背景对比不足。 将其绑定到 Arco CSS 变量 --color-border-2,让边框色随主题自适应。 * 🧪 navigation_handle: 用 resetAttachedForTest 替代 vi.resetModules vi.resetModules() 会清空全局模块缓存,导致后续测试重新加载 LoggerCore、 chrome mock 等模块时出错,在 CI 上表现为偶发失败。 改为导出 resetAttachedForTest() 重置模块级 attached 单例, 测试无需动态 import 即可验证多次调用的幂等性。 * 🐛 Agent Bug 与资源泄漏修复合集 - B3+B4: LLM 调用 4xx 错误不重试 + abort listener 清理 - B5: Promise.race 定时器清理(新增 withTimeout utility) - B6: task_scheduler runningTasks 资源清理包裹在 try/finally - DOM 操作增加 URL 黑名单守卫(chrome://, about://, devtools:// 等) * ♻️ Agent 架构重构前置改进 为后续 AgentService 拆分铺路: - LLMProvider 抽象接口 + registry 机制(解耦 OpenAI/Anthropic) - ToolRegistry 引入来源追踪(builtin/mcp/skill/script)+ scoped 注册 - HTML 搜索引擎插件化(html_extractor 396 → 332 行) - 拆分 3296 行 agent.test.ts 为 8 个主题测试文件 * ♻️ AgentService 拆分与 Agent 目录按上下文分层 将 2534 行的 AgentService 上帝对象拆分为 11 个专用服务 (AgentService 472 行,-81%),并重组 Agent 代码目录。 提取的服务: - SkillService: ZIP 安装/刷新/解析 - AgentTaskService: 定时任务 CRUD + 执行 - AgentModelService: 模型配置与选择 - AgentOPFSService: OPFS 文件访问 - SubAgentService: 子代理会话管理 - BackgroundSessionManager: 后台会话状态 - CompactService: 对话压缩摘要 - LLMClient: LLM 调用封装 - ToolLoopOrchestrator: 工具循环编排 - ChatService: 对话处理(ephemeral/compact/persistent) - resolveAttachments utility 抽取 目录重组: - src/app/service/agent/core/ 跨上下文共享核心 - src/app/service/agent/service_worker/ SW 端有状态服务 - 清理文件 agent 前缀(agent_dom.ts → dom.ts 等) * ♻️ AgentTaskRunRepo 改用 OPFS 存储 AgentTaskRun 原本存在 chrome.storage.local(键前缀 agent_task_run:), 存在几个问题:配额压力、listRuns 全表扫描、缓存常驻内存。 改为 OPFS 后: - 存储路径:agents/task_runs/{taskId}.json(内含 run[] 降序) - listRuns O(全表) → O(单文件 parse) - 环形缓冲:每任务保留最近 100 条(append 时自动裁剪) - Agent 体系存储后端统一到 OPFS 改动: - agent_task.ts: AgentTaskRunRepo extends Repo → extends OPFSRepo - task_scheduler.ts: updateRun 签名加 taskId 参数 - 测试:添加 OPFS mock + 2 个新用例(环形缓冲 / 更新不存在 id) Agent 功能尚未发布,无需考虑旧数据迁移。 Typecheck 0 错误,1736 测试全绿(+2 新用例) * 🧹 修复 Agent 重构遗留的 lint 问题 - 移除未使用的导入(AgentTask、sendMessage、ToolSource、部分 test helpers) - html_extractor.ts: inline import() 类型改为顶部 import type - prettier 格式化修复(with_timeout.ts、web_search.ts 等多处) * 🐛 修复 Provider 注册被 tree-shake 导致 e2e 失败 package.json 限制 sideEffects 到 CSS 文件,生产构建时 rspack 会 tree-shake 纯副作用导入。原本 providers/index.ts 用 \`import "./openai"\` 的副作用导入触发 provider 注册,在生产 bundle 中被剥离,导致运行时报 "Unsupported LLM provider: openai"。 修复:把内置 provider 注册移到 registry.ts 模块内,与 providerRegistry 单例同模块。消费者 import providerRegistry 时 必然会触发 registry.ts 的顶层语句,注册就一定发生。 验证: - dist bundle 现在包含 d7.register({name:"openai",...}) 和 d7.register({name:"anthropic",...}) - 6 个 agent e2e 测试全部恢复通过 - 1736/1736 单元测试通过 * ♻️ AgentChatRepo 改为模块单例消除 setter fan-out AgentChatRepo 是 OPFS 的无状态包装,原本通过 AgentService._repo 配合 setter 同步给 4 个子服务 (CompactService/LLMClient/ ToolLoopOrchestrator/ChatService),新增子服务容易遗漏同步。 改为模块级单例 agentChatRepo,子服务直接 import 使用,测试用 vi.hoisted + vi.mock 替换整个模块,彻底消除 setter 同步仪式。 * ♻️ Agent 架构改进:SessionToolRegistry、事件扁平化、子代理持久化 - SessionToolRegistry 组合模式:per-session 工具注册隔离,消除并发会话工具覆盖 - excludeTools 后端强校验 + 未知 sub-agent type 抛错 - ChatStreamEvent 分类重组:LLMStreamEvent → ForwardableEvent 层次化, 消除递归 sub_agent_event 改为 subAgent? 扁平标识 - Sub-agent context OPFS 持久化,resume 时内存优先、OPFS 回退 - 清理 AgentService 测试兼容性代理 getter/setter(~45 行) - 移除 ChatService 未使用的 compactService 字段 - 提取 OPFS mock 到共享 test-helpers * 🧹 修复 prettier 格式问题 * 🔒 子代理隔离、安全加固与健壮性改进 - 子代理使用独立 childRegistry 替代共享父 sessionRegistry,避免工具泄漏 - system prompt 增加防注入/防数据外泄/数据指令分离安全规则 - get_tab_content 增加 URL 策略校验(assertDomUrlAllowed) - researcher 子代理移除 execute_script 工具 - Anthropic provider 对畸形 toolCall arguments 降级为空对象 - 子代理超时后追加提示信息,区分用户取消与超时 - tool loop 移除 withRetry 包装,重试逻辑下沉到 llm_client - 修正 tool call budget 描述为 rounds 而非单次调用 * ✨ navigate_tab 工具、researcher 页面只读能力、精简 task 工具 - 新增 navigate_tab 工具:导航已有标签页到新 URL,支持等待加载完成 - researcher 子代理增加页面只读能力(get_tab_content/open_tab/list_tabs/close_tab/navigate_tab),但不允许 DOM 交互 - 移除 get_task 和 delete_task 工具,list_tasks 改为返回完整任务信息 - 页面交互指南仅在同时拥有 get_tab_content 和 execute_script 时显示 * 🧹 修复 tab_tools lint 问题(prefer-const、no-unsafe-function-type) * ♻️ 合并 navigate_tab 到 open_tab:tab_id 可选,有则导航已有标签页 * ✨ Skill 系统:version 字段、URL 分发机制、更新检查 - Skill 支持 version 字段(SkillSummary/SkillMetadata/SkillRecord) - 新增 URL 安装流程:以 SKILL.cat.md 为入口,按相对路径 fetch scripts/references - 新增更新检查机制:checkForUpdates/updateSkill,基于 semver 比较 - SKILL.cat.md 优先于 SKILL.md(parseSkillZip 兼容) - DNR 拦截 .cat.md URL,安装页自动识别并走 URL 安装流程 - SkillInstallView 展示 version 和 installUrl - AgentSkills 页面支持 URL 安装输入和批量检查更新 - 8 个语言文件新增 i18n keys - 新增单元测试覆盖 version/URL 安装/更新检查 * ♻️ Agent 代码质量重构:SOLID 原则改进、DRY 消除、类型安全增强 - 提取 TokenUsage 公共类型,消除 6 处内联重复 - AgentTask 改为判别联合类型(InternalAgentTask | EventAgentTask) - zhipu 注册为正式 Provider,消除 llm_client 硬编码分支 - handleConversationChat 拆分为 5 个职责单一的子方法 - Provider 层提取 content_utils.ts 公共逻辑(SSE 读取、内容块转换、附件 ID 生成) - agentChatRepo 统一构造函数注入(LLMClient/CompactService/ToolLoop/ChatService) - skill_service 提取 fetchSkillResources 共享方法 - web_search 搜索方法模板抽取(fetchAndExtract) - 统一工具参数校验辅助函数(param_utils.ts) - 提取 UA/超时常量,tab_tools 复用 stripHtmlTags - 删除目录重组遗留的旧文件 - 修复 e2e agent-fixtures 缺失 expect 导出 * 🧹 简化 Agent e2e 测试、修复 fixtures 导出 - 简化 agent-chat.spec.ts:移除不可靠的 LLM mock 测试,保留基础 UI 验证 - 修复 agent-fixtures.ts 缺失的 expect 导出 - 提交 e2e/utils.ts 的 Agent 辅助函数(openAgentChatPage 等) - 删除 debug-provider 调试文件 * 🧹 修复 lint 问题:prettier 格式化、移除未使用变量 * 🔧 优化 Git Hooks:pre-commit 跑 lint-fix,pre-push 跑测试 - pre-commit:每次提交前运行 typecheck + eslint --fix - pre-push:仅推送到 main/release 时运行测试 - 修正原 pre-commit 实际为 pre-push 逻辑的问题 - 移除 lint-staged(直接全量 lint-fix) * 🐛 修复 Copilot Review 发现的问题 - SSE 解析器空行时无条件重置 currentEvent,防止残留污染 - OPFS 写入 Uint8Array 时精确截取字节段,修复切片视图 bug - execute_script 超时消息改为动态生成,匹配实际超时时间 - cleanupIfDone 回调中重新检查会话状态,防止误删已复用会话 - 模型能力检测函数移至 core/model_capabilities.ts,消除 core→UI 反向依赖 - web_search max_results 改用 optionalNumber 防止 NaN - task_tools subject/description 使用类型安全的参数校验 * ✨ 子代理 tab_id 上下文传递:避免重复打开页面 agent 工具新增 tab_id 可选参数,父代理可将已打开的标签页传递给子代理, 子代理会直接使用该标签页而非重复打开新页面。 * 🔧 pre-commit 改用 lint 替代 lint-fix * 🔧 将测试检查从 pre-push 移至 pre-commit pre-commit:lint + main/release/* 分支跑 test:ci 删除 pre-push hook --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: cyfung1031 <44498510+cyfung1031@users.noreply.github.com>
1 parent 15c62d0 commit 94b533d

217 files changed

Lines changed: 44687 additions & 1444 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ on:
55
branches:
66
- main
77
- release/*
8-
- feature/*
98
- dev
109
paths-ignore:
1110
- ".github/**"

.github/workflows/test.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ on:
55
branches:
66
- main
77
- release/*
8-
- feature/*
98
- dev
109
- develop/*
1110
pull_request:

.husky/pre-commit

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,20 @@
11
#!/bin/sh
22

3-
# Skip checks: SKIP_PRE_PUSH=1 git push or git push --no-verify
4-
if [ "$SKIP_PRE_PUSH" = "1" ]; then
5-
echo "SKIP_PRE_PUSH=1, skipping pre-push checks"
3+
# Skip: SKIP_PRE_COMMIT=1 git commit 或 git commit --no-verify
4+
if [ "$SKIP_PRE_COMMIT" = "1" ]; then
5+
echo "SKIP_PRE_COMMIT=1, skipping pre-commit checks"
66
exit 0
77
fi
88

9-
# Only run checks when pushing to main or release/* branches
10-
remote="$1"
11-
need_check=0
12-
13-
while read local_ref local_sha remote_ref remote_sha; do
14-
branch=$(echo "$remote_ref" | sed 's|refs/heads/||')
15-
16-
if [ "$branch" = "main" ] || echo "$branch" | grep -q "^release/"; then
17-
need_check=1
18-
echo "🔍 Detected push target: $branch"
19-
fi
20-
done
21-
22-
if [ "$need_check" = "0" ]; then
23-
exit 0
24-
fi
25-
26-
echo ""
27-
echo "▶ Running lint..."
289
pnpm run lint || exit 1
2910

30-
echo ""
31-
echo "▶ Running tests..."
32-
pnpm run test:ci || exit 1
33-
34-
echo ""
35-
echo "✅ All checks passed! (build and e2e tests will run in CI)"
11+
# 在 main 或 release/* 分支上提交时额外跑测试
12+
branch=$(git rev-parse --abbrev-ref HEAD)
13+
if [ "$branch" = "main" ] || echo "$branch" | grep -q "^release/"; then
14+
echo ""
15+
echo "🔍 Detected branch: $branch"
16+
echo "▶ Running tests..."
17+
pnpm run test:ci || exit 1
18+
echo ""
19+
echo "✅ All checks passed!"
20+
fi

e2e/agent-chat.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { test, expect } from "./agent-fixtures";
2+
import { openAgentChatPage } from "./utils";
3+
4+
test.describe("Agent Chat", () => {
5+
test("should show new chat button and model selector", async ({ context, extensionId }) => {
6+
const page = await openAgentChatPage(context, extensionId);
7+
await page.waitForTimeout(2000);
8+
9+
// 新建会话按钮应可见
10+
const newChatBtn = page.locator("button", { hasText: /new|/i }).first();
11+
await expect(newChatBtn).toBeVisible({ timeout: 10000 });
12+
13+
// 模型选择器应可见且包含预配置的 Mock LLM 模型
14+
const modelSelect = page.locator(".arco-select");
15+
await expect(modelSelect.first()).toBeVisible({ timeout: 5000 });
16+
17+
await page.close();
18+
});
19+
});

e2e/agent-conversation.spec.ts

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
import { expect } from "@playwright/test";
2+
import { test, makeTextSSE, makeToolCallSSE } from "./agent-fixtures";
3+
import { runInlineTestScript } from "./utils";
4+
5+
const TARGET_URL = "https://content-security-policy.com/";
6+
7+
test.describe("Agent Conversation API", () => {
8+
test.setTimeout(300_000);
9+
10+
test("basic chat — send message and receive text reply", async ({ context, extensionId, mockLLMResponse }) => {
11+
mockLLMResponse(() => makeTextSSE("1+1等于2。"));
12+
13+
const code = `// ==UserScript==
14+
// @name Agent Basic Chat Test
15+
// @namespace https://e2e.test
16+
// @version 1.0.0
17+
// @description Test basic CAT.agent.conversation.chat()
18+
// @author E2E
19+
// @match ${TARGET_URL}*
20+
// @grant CAT.agent.conversation
21+
// ==/UserScript==
22+
23+
(async () => {
24+
let passed = 0;
25+
let failed = 0;
26+
function assert(name, condition) {
27+
if (condition) { passed++; console.log("PASS: " + name); }
28+
else { failed++; console.log("FAIL: " + name); }
29+
}
30+
31+
try {
32+
const conv = await CAT.agent.conversation.create({
33+
system: "你是一个助手。",
34+
});
35+
assert("conversation created", !!conv && !!conv.id);
36+
37+
const reply = await conv.chat("1+1等于几?");
38+
assert("reply has content", !!reply.content);
39+
assert("reply content correct", reply.content.includes("2"));
40+
assert("reply has usage", !!reply.usage);
41+
} catch (e) {
42+
failed++;
43+
console.log("ERROR: " + e.message);
44+
}
45+
46+
console.log("通过: " + passed + ", 失败: " + failed);
47+
})();
48+
`;
49+
50+
const { passed, failed, logs } = await runInlineTestScript(context, extensionId, code, TARGET_URL, 60_000);
51+
52+
console.log(`[agent-basic-chat] passed=${passed}, failed=${failed}`);
53+
if (failed !== 0) console.log("[agent-basic-chat] logs:", logs.join("\n"));
54+
expect(failed, "Some basic chat tests failed").toBe(0);
55+
expect(passed, "No test results found").toBeGreaterThan(0);
56+
});
57+
58+
test("tool calling — script-defined tools are invoked", async ({ context, extensionId, mockLLMResponse }) => {
59+
let callCount = 0;
60+
mockLLMResponse(() => {
61+
callCount++;
62+
// First call: LLM decides to call the tool
63+
if (callCount === 1) {
64+
return makeToolCallSSE([
65+
{
66+
id: "call_1",
67+
name: "get_weather",
68+
arguments: JSON.stringify({ city: "北京" }),
69+
},
70+
]);
71+
}
72+
// Second call: after tool result, LLM gives final answer
73+
return makeTextSSE("北京今天22度,多云。");
74+
});
75+
76+
const code = `// ==UserScript==
77+
// @name Agent Tool Calling Test
78+
// @namespace https://e2e.test
79+
// @version 1.0.0
80+
// @description Test tool calling via CAT.agent.conversation
81+
// @author E2E
82+
// @match ${TARGET_URL}*
83+
// @grant CAT.agent.conversation
84+
// ==/UserScript==
85+
86+
(async () => {
87+
let passed = 0;
88+
let failed = 0;
89+
let toolCalled = false;
90+
let toolArgs = null;
91+
92+
function assert(name, condition) {
93+
if (condition) { passed++; console.log("PASS: " + name); }
94+
else { failed++; console.log("FAIL: " + name); }
95+
}
96+
97+
try {
98+
const conv = await CAT.agent.conversation.create({
99+
system: "你是天气助手。",
100+
tools: [
101+
{
102+
name: "get_weather",
103+
description: "获取天气",
104+
parameters: {
105+
type: "object",
106+
properties: {
107+
city: { type: "string", description: "城市" },
108+
},
109+
required: ["city"],
110+
},
111+
handler: async (args) => {
112+
toolCalled = true;
113+
toolArgs = args;
114+
return { city: args.city, temperature: 22, condition: "多云" };
115+
},
116+
},
117+
],
118+
});
119+
120+
const reply = await conv.chat("北京天气怎么样?");
121+
assert("tool was called", toolCalled);
122+
assert("tool received city arg", toolArgs && toolArgs.city === "北京");
123+
assert("final reply has content", !!reply.content);
124+
assert("final reply mentions weather", reply.content.includes("22") || reply.content.includes("多云"));
125+
} catch (e) {
126+
failed++;
127+
console.log("ERROR: " + e.message);
128+
}
129+
130+
console.log("通过: " + passed + ", 失败: " + failed);
131+
})();
132+
`;
133+
134+
const { passed, failed, logs } = await runInlineTestScript(context, extensionId, code, TARGET_URL, 60_000);
135+
136+
console.log(`[agent-tool-calling] passed=${passed}, failed=${failed}`);
137+
if (failed !== 0) console.log("[agent-tool-calling] logs:", logs.join("\n"));
138+
expect(failed, "Some tool calling tests failed").toBe(0);
139+
expect(passed, "No test results found").toBeGreaterThan(0);
140+
});
141+
142+
test("multi-turn conversation — context is preserved", async ({ context, extensionId, mockLLMResponse }) => {
143+
let requestCount = 0;
144+
let lastMessages: any[] = [];
145+
146+
mockLLMResponse(({ messages }) => {
147+
requestCount++;
148+
lastMessages = messages;
149+
if (requestCount === 1) {
150+
return makeTextSSE("斐波那契数列是一个数列,每个数是前两个数之和。");
151+
}
152+
return makeTextSSE("前5个数是:1, 1, 2, 3, 5。");
153+
});
154+
155+
const code = `// ==UserScript==
156+
// @name Agent Multi-turn Test
157+
// @namespace https://e2e.test
158+
// @version 1.0.0
159+
// @description Test multi-turn conversation context preservation
160+
// @author E2E
161+
// @match ${TARGET_URL}*
162+
// @grant CAT.agent.conversation
163+
// ==/UserScript==
164+
165+
(async () => {
166+
let passed = 0;
167+
let failed = 0;
168+
function assert(name, condition) {
169+
if (condition) { passed++; console.log("PASS: " + name); }
170+
else { failed++; console.log("FAIL: " + name); }
171+
}
172+
173+
try {
174+
const conv = await CAT.agent.conversation.create({
175+
system: "你是数学老师。",
176+
});
177+
178+
const reply1 = await conv.chat("什么是斐波那契数列?");
179+
assert("first reply has content", !!reply1.content);
180+
181+
const reply2 = await conv.chat("前5个数是什么?");
182+
assert("second reply has content", !!reply2.content);
183+
assert("second reply has fibonacci numbers", reply2.content.includes("1") && reply2.content.includes("5"));
184+
} catch (e) {
185+
failed++;
186+
console.log("ERROR: " + e.message);
187+
}
188+
189+
console.log("通过: " + passed + ", 失败: " + failed);
190+
})();
191+
`;
192+
193+
const { passed, failed, logs } = await runInlineTestScript(context, extensionId, code, TARGET_URL, 60_000);
194+
195+
console.log(`[agent-multi-turn] passed=${passed}, failed=${failed}`);
196+
if (failed !== 0) console.log("[agent-multi-turn] logs:", logs.join("\n"));
197+
expect(failed, "Some multi-turn tests failed").toBe(0);
198+
expect(passed, "No test results found").toBeGreaterThan(0);
199+
// Verify the mock received multiple requests (context was sent)
200+
expect(requestCount, "Should have made 2 LLM requests").toBe(2);
201+
// The second request should contain history messages
202+
expect(lastMessages.length, "Second request should include conversation history").toBeGreaterThan(2);
203+
});
204+
});

0 commit comments

Comments
 (0)