Skip to content

feat(agent): GUI 工具目标发现、稳定引用与输入补齐(P1+P2) - #1335

Open
sheepbox8646 wants to merge 1 commit into
feat/computer-browser-p0from
feat/computer-browser-p1-p2
Open

sheepbox8646 wants to merge 1 commit into
feat/computer-browser-p0from
feat/computer-browser-p1-p2

Conversation

@sheepbox8646

Copy link
Copy Markdown
Member

Author

  • Human
  • Agent

Type

  • bug
  • feat
  • test

Summary

Stacked PR,基于 #1328(P0)。实施 docs/computer-browser-capability-completion-plan.mdP1(目标发现和稳定引用)与 P2(补齐输入和元素操作);P3–P5 将在后续 PR 中继续。合并顺序:#1328 → 本 PR。

问题

  • 浏览器操作只能作用于「当前页」,切换靠索引;多标签页与并行任务容易操作错误目标(C12)。
  • 没有应用/浏览器发现接口,ref 只是重扫后的序号,页面变化后同一序号会落到别的元素上。
  • 缺少粘贴格式、精确选文、直接设值和指定辅助动作,多行富文本、局部编辑和原生控件操作不完整(C13)。

改动

  • computer_context 新工具internal/agent/tool/computer_context.go
    • get_state:应用、浏览器、标签页与本会话选择,分域报错,不把一域失败伪装成空列表。
    • list_apps / get_appa11y-cli apps 枚举无障碍总线上的应用,pid 由总线守护进程解析,app_id = app:<pid>;名称歧义返回候选 id;launch=true 按 desktop entry / PATH / 绝对路径解析为独立 argv 启动(不拼 shell),并由审批策略按 exec 治理(approval.OperationForCall);启动后未注册到总线的进程(如 xterm)返回 accessible: false 的结构化结果。
    • list_browsers / get_browser:按 --remote-debugging-port 发现 Chromium 实例,browser_id = chrome-<port>,只有默认工作区浏览器按需启动;URL 只用于匹配现有标签页,多个匹配返回候选而不猜测。
    • documentation:返回生成 schema 所用的动作契约(同一份规范)、身份/坐标/时序说明与各后端能力(含哪些动作明确不支持)。
  • 目标身份与会话缺省gui_session.gogui_browsers.go):所有浏览器调用接受 browser_id / tab_id(兼容 tab_index,二者互斥),所有桌面调用接受 app_id;缺省来自 per-(bot, session) 内存状态,不同会话互不覆盖;动作前冻结目标并在结果里返回 browser_id / tab_id / tab_sourcebrowser_id 必须是发现返回的 id,URL、命令名在校验层即被拒绝。
  • 稳定引用:浏览器快照把元素列表钉在页面上并返回 snapshot_id,ref 只在同一快照、同一标签页、元素仍在文档中时解析;导航/刷新/历史使其失效,不重新编号。桌面快照的 snapshot_id 写入 helper 引用索引(协议版本升到 3),动作携带 --snapshot,跨快照 ref 被拒绝;Go 侧还要求本会话最近快照覆盖所选应用。
  • 输入补齐
    • Browser:set_value(原生 setter + input/change;select 校验选项)、paste(真实 paste 事件携带 text/plain 与 text/html,未被消费时 contenteditable 走 insertHTML、字段走 insertText)、select_textprefix+text+suffix 唯一匹配,重复/缺失分别报错,支持选中与光标前后)、secondary_action(明确返回不支持)、scroll 支持 pageskeydown/keyup 在会话内跟踪按住的键并作用于后续 pressfill 改为选中全部后经输入管线替换并回读校验;focus 返回实际焦点。
    • Computer:set_value(EditableText 或 Value 接口;按钮等控件明确不支持)、select_text(AT-SPI Text 选区/光标)、secondary_action(只执行快照 actions= 列出的动作,未列出返回可用列表)、paste(xclip 剪贴板:保存 → 写入 → 聚焦 → Ctrl+V → 恢复,粘贴期间被改动则保留,按 bot 串行;工作区镜像新增 xclip)。快照行新增 actions=app_pid
    • 中断清理:浏览器 press 失败时释放本次按下的修饰键,mouseDrag 中途失败也释放按钮;桌面 drag 失败补发释放,mouse_move/pointer 保持的按钮在后续失败时释放。
  • 前端computer_context 加入 GUI 工具集与工具调用标签(三语言),新动作补图标与 i18n。
  • 文档docs/agent-runtime.md 补充目标身份、引用绑定与新输入动作说明;计划文档勾选 P1/P2 并附实施记录。

Related Issues

依赖 #1328

Validation

自动化

  • cargo fmt --checkcargo clippy --workspace --locked -D warningscargo test --workspace --locked:45 项通过。
  • go test ./...:全量通过(internal/agent/tool 新增契约、目标解析、会话隔离、快照绑定等测试;internal/agent/decision/approval 新增 launch 按 exec 治理的测试)。
  • golangci-lint run ./...:无问题。
  • pnpm exec eslint 变更的 TS 文件:无问题;vitest 工具注册相关用例通过(panel-preview.test.tssession-info-ring.test.ts 两个失败在未改动的 main 上同样失败,与本 PR 无关)。

真实运行环境

  • feat(agent): 补齐 Browser/Computer Use 动作契约并对齐 a11y 协议(P0) #1328 相同的独立开发环境 memoh-cua(Server http://localhost:19880,Web http://localhost:19882),工作区镜像用 scripts/prepare-dev-workspace-image.sh 从本分支重建(含协议 3 的 a11y-clixclip),server 重启重新导入,Bot 工作区重建后 a11y_available: true
  • 模型说明:与 feat(agent): 补齐 Browser/Computer Use 动作契约并对齐 a11y 协议(P0) #1328 相同,本机没有真实模型密钥,验证使用脚本化的 OpenAI 兼容模型服务按对话状态返回下一步工具调用;驱动的是真实的 Memoh Web 聊天 → Agent 运行时 → 工具执行 → 工作区浏览器/桌面链路,工具调用、结果和截图均为真实执行产物。这不构成 Human QA。
  • 通过 Web 聊天页发送四条消息(每条一个新会话),各触发一个场景:
    1. 目标发现exec 启动两台 xfce4-terminal --disable-server 与第二个 Chromium(端口 9223)→ list_appsget_app "xfce4-terminal" 返回「2 running applications match … app:17257, app:17258」→ get_app app:17257 选中并返回应用级快照(45 个 ref)→ get_app "xfce4-appfinder" launch=true 通过工具启动并选中(130 个 ref,launched.source: desktop)→ list_browsers 列出 chrome-9222(default,7 个标签页)与 chrome-9223 → 两次 tab_new 返回稳定 tab_idget_browser chrome-9223 后无参数 get_title 返回「second-browser」(tab_source: active)→ get_browser url=…tab-two… 因此前运行留下两个同 URL 标签页而返回 2 个候选、不猜测 → get_stateget_app "mousepad" launch=true 返回「no installed application…」→ get_app "xterm" launch=true 返回 accessible: false(进程在运行但无 AT-SPI 树)→ documentation browser_id=chrome-9223
    2. 稳定引用:在标签页 A 打开表单并 snapshot(snapshot_id b1a0b86e94c8-1)→ tab_new 使会话默认切到另一标签页 → 带 tab_idfill 仍写入标签页 A(tab_source: explicitevaluate 回读一致)→ 不带 tab_id 的 fill ref=e1 因会话标签页从未观察而被拒绝 → navigate 标签页 A 后旧 ref 被拒绝(「no element refs exist on this page (it was navigated…)」)→ 重新 snapshot 得到新 snapshot_id 后,携带旧 snapshot_id 的 ref 被拒绝(「belongs to snapshot … but this page currently holds …」)→ 桌面连续两次 snapshot 后携带上一次 snapshot_idclick 被 helper 拒绝(「observe again before acting」)。
    3. 浏览器输入set_value「设置的值 🚀」→ select_text "的值"keyboard_type "新" 得「设置新 🚀」→ select_text "hello" 在无匹配与 2 次匹配时分别报错 → prefix:"world " + cursor_after 后输入「!」得「hello world hello!」→ paste format=html 被页面 paste 处理器消费(via: paste_event)→ paste format=textinsert_textsecondary_action 明确不支持 → scroll pages=1 于 120px 容器返回 scroll_before 0 → scroll_after 120keydown Shiftpress a 页面记录 shift=truekeyup 后为 false(页面事件日志见截图)。
    4. 桌面输入exec 预置剪贴板「user clipboard」并启动应用查找器 → get_app 选中 → set_value「set-value-text」(via a11y)→ select_text "value" 返回 4–9 → type 于光标处插入 → fill "" 清空 → paste「粘贴 pasted 🚀」后 clipboard: restored,随后 exec xclip -o 读回「user clipboard」→ 截图显示输入框内容为「粘贴 pasted 🚀」→ secondary_action "no-such-action" 返回「available: Activate」→ 对按钮 set_value 返回「exposes neither EditableText nor Value」。
  • 实施中发现并修复:会话脚本里 pkill -f <字面量> 会匹配到执行它的 shell 自身(命令行含同一字面量)而使整条命令以 143 退出,这是验证脚本的问题,不是工具缺陷,已改为变量拼接。
  • 未做:P3(真实 AX 树、增量快照、截图媒体注入)、P4(CDP 会话与 target 分离、交付/接手标记)、P5(端到端回归与文档发布)不在本 PR;uploadpdf 未在真实 UI 中重新触发。

Screenshots / Recordings

截图托管在分支 computer-p0/assetsshots/p1p2/)。

截图 说明
discovery 目标发现场景的聊天记录:同名应用歧义返回候选 id、按 app_id 选中、通过工具启动应用等;右侧实时桌面可见两台终端、第二个 Chromium 与 xterm
refs 稳定引用场景:显式 tab_id 不受会话切页影响,导航后与跨快照的 ref 被拒绝
browser inputs chat 浏览器输入场景的 21 步工具调用记录
browser inputs page 工具保存的页面截图:输入框最终值、contenteditable 里被处理器插入的富文本、滚动后的容器,以及页面事件日志(input 事件、paste handled html、scrollTop=120、keydown shift=true/false)
desktop inputs chat 桌面输入场景的聊天记录(set_value / select_text / paste 的 a11y 结果与 clipboard: restored)
desktop inputs appfinder 工具保存的桌面截图:应用查找器输入框内容为通过剪贴板粘贴的「粘贴 pasted 🚀」

Human QA

  • Human QA passed

🤖 Generated with Claude Code

按 docs/computer-browser-capability-completion-plan.md 的 P1、P2 阶段实施,
基于 P0(#1328):

- 新增 computer_context:get_state / list_apps / get_app / list_browsers /
  get_browser / documentation。应用经 a11y-cli apps 发现(app:<pid>),
  浏览器按 --remote-debugging-port 发现(chrome-<port>);名称歧义返回候选,
  URL 只匹配现有标签页;launch=true 以独立 argv 启动并按 exec 审批治理。
- 所有浏览器调用接受 browser_id/tab_id(兼容 tab_index),桌面调用接受
  app_id;缺省按 (bot, session) 保存,动作前冻结目标并回传 tab_source。
- ref 绑定 snapshot_id:浏览器把元素列表钉在页面上,导航/刷新使其失效;
  桌面快照 id 写入 helper 引用索引(协议版本 3),跨快照 ref 被拒绝。
- 新增 set_value / paste / select_text / secondary_action,Browser 的
  pages 滚动与 keydown/keyup 按键保持,Computer 的 xclip 剪贴板粘贴与恢复;
  失败路径释放按住的按钮与修饰键;工作区镜像新增 xclip。
- 前端注册 computer_context 与新动作的图标和三语言标签;文档更新。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@sheepbox8646
sheepbox8646 requested review from a team as code owners September 19, 2026 07:00
@github-actions github-actions Bot added change:server Changes backend code, configuration, or API contracts change:web Changes web frontend or shared frontend packages feat Adds or improves functionality size:XL PR size uses the larger of added or deleted lines, excluding generated files labels Sep 19, 2026
@sheepbox8646
sheepbox8646 added this pull request to stack #1343 September 20, 2026 11:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:server Changes backend code, configuration, or API contracts change:web Changes web frontend or shared frontend packages feat Adds or improves functionality size:XL PR size uses the larger of added or deleted lines, excluding generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant