AgentOS 桌面操作手冊(任何 agent 通用)
本手冊自包含:只要你的框架能掛 stdio MCP server(goose / Claude Code / Z Code / Cursor 等), 按 §1 掛載後即可獲得下面全部能力。所有條目均真機實測(Fedora + niri Wayland, 2026-10 端到端證據見倉庫 docs/capabilities.md)。
0. 心智模型:四層能力,按優先級降級
- L0 系統層——窗口/工作區/啟動/檔案/通知/剪貼板(niri/desktop/system/files 擴展):最穩,優先用;
- L1 無障礙樹——GUI 應用內按名稱點擊/讀寫文字(a11y 擴展,AT-SPI2):無需焦點、不打擾用戶, GTK3/GTK4/Qt 實測全通(Qt 的全局開關與 Chromium 的 toolkit-accessibility 已是系統默認, 2026-10-18 起);Chromium 樹讀/文字輸入可達(頁面點擊走 L2);Java 未裝(路徑見 capabilities.md 附錄 B);純 X11 應用(xterm 類)無樹,只能 L3;
- L2 瀏覽器 CDP——網頁一切操作(browser 擴展,agent 專屬無頭 Chromium);
- L3 視覺/輸入兜底——grim 截圖(可靠)、鍵盤注入(
scripts/kb-inject.sh:wtype 用戶級 為主力,文字/組合鍵可靠;控制鍵(Enter/Tab)回落 ydotool 需 sudo)、指針注入(不可靠: 加速度下漂移,只作最後手段)。
遇到「打不開/打不好」的應用:先想是不是 L1 前置條件沒滿足(a11y 開關),再用 L3 截圖看真實狀態, 不要盲目重試。
1. 掛載(一次性)
goose(~/.config/goose/config.yaml,1.54 實測格式;name: 字段必填,缺了整條擴展被靜默丟棄):
extensions:
niri:
type: stdio
name: niri
enabled: true
cmd: /path/to/AgentOS/scripts/env/with-niri-env.sh
args: []
env_keys: []
envs: {}
timeout: 300 # 單位秒
# desktop/system/files/session/a11y/browser/cred 同構,
# cmd 分別用 with-desktop-env.sh / with-system-env.sh / ... (scripts/env/ 下八個啟動器)
其他框架(Claude Code / Z Code 等 mcpServers JSON 風格):
{
"mcpServers": {
"niri": { "command": "/path/to/AgentOS/scripts/env/with-niri-env.sh" },
"desktop": { "command": "/path/to/AgentOS/scripts/env/with-desktop-env.sh" },
"system": { "command": "/path/to/AgentOS/scripts/env/with-system-env.sh" },
"files": { "command": "/path/to/AgentOS/scripts/env/with-files-env.sh" },
"session": { "command": "/path/to/AgentOS/scripts/env/with-session-env.sh" },
"a11y": { "command": "/path/to/AgentOS/scripts/env/with-atspi-env.sh" },
"browser": { "command": "/path/to/AgentOS/scripts/env/with-browser-env.sh" },
"cred": { "command": "/path/to/AgentOS/scripts/env/with-cred-env.sh" }
}
}
要點:
- 用啟動器腳本,不要直接指 node dist/index.js——啟動器負責發現 niri IPC socket
(路徑含 compositor PID,每次會話不同,如
/run/user/1000/niri.wayland-1.2762.sock)和補齊 Wayland/dbus 環境(XDG_RUNTIME_DIR/WAYLAND_DISPLAY/DBUS_SESSION_BUS_ADDRESS), 從 SSH/服務環境拉起時工具才可用; - 手測啟動器:
timeout 2 scripts/env/with-niri-env.sh </dev/null,exit 0 即正常; - 前置:Node ≥ 20、倉庫已
npm install && npm run build、運行在 niri 圖形會話所屬用戶下。
2. 工具速查(按擴展分組,工具名 = <擴展>__<工具>)
niri —— 窗口/工作區/啟動(L0)
| 工具 | 用途 | 關鍵參數 |
|---|---|---|
list_windows | 列所有窗口(id/app_id/標題/工作區/焦點) | — |
list_workspaces | 列工作區(idx 序號 與 id 是兩個東西,move 用序號或名稱) | — |
list_outputs | 列顯示器 | — |
focus_window | 切前台聚焦(會切用戶視角,慎用) | id |
focus_workspace | 切工作區(會切用戶視角) | reference: "3" 或名稱 |
move_window_to_workspace | 移窗口(不搶焦點,整理用) | id?(缺省=聚焦窗)、reference |
close_window | 關窗口 | id? |
open_app | 在用戶桌面啟動程式 | command: 陣列,如 ["ghostty"] |
a11y —— GUI 應用內操作(L1,AT-SPI2)
| 工具 | 用途 | 關鍵參數 |
|---|---|---|
a11y_list_apps | 列無障礙總線上註冊的應用 | — |
a11y_dump_tree | 倒某應用的無障礙樹(找按鈕名/結構) | app |
a11y_click | 按 accessible 名稱點擊(無需焦點不打擾用戶) | app、buttonName |
a11y_read_texts | 讀應用全部非空文字(顯示屏/結果/標籤) | app |
a11y_set_text | 按節點名寫文字框 | app、match(名稱子串,缺省 * 第一個)、text |
實測紀律:按名稱尋址,不要按樹索引(樹結構跨 AT 連接會重建);GTK4 的樹要等 AT 客戶端 註冊後才完整;同名多實例時橋解析到先註冊者——先讀文字對狀態判別實例,必要時關掉多餘實例。 Chromium 例外注意:樹懶構建(橋自動觸發,首次 dump 多等 1-2 秒屬正常);寫文字框用 "grabFocus + kb-inject type"組合(Chromium 的 entry 不吃 setTextContents);頁面按鈕點擊 不生效(doAction 派發但 onclick 不跑),頁面操作一律走 browser 擴展。
browser —— 網頁操作(L2,agent 專屬無頭 Chromium)
| 工具 | 用途 | 關鍵參數 |
|---|---|---|
browser_open | 導航(首次調用自動拉起瀏覽器) | url |
browser_eval | 頁面內執行 JS,填表/點擊/讀內容全靠它 | JS 代碼 |
browser_screenshot | 截圖(瀏覽器無頭,視覺全靠它) | — |
填表標準動作:el.value = "..." 後必須 el.dispatchEvent(new Event("input", {bubbles: true})),
否則現代框架收不到輸入。遇驗證碼:停下告知用戶,絕不自動繞過。
files —— 檔案沙箱(L0,根 = ~/Agent/,路徑都寫相對)
| 工具 | 用途 | 關鍵參數 |
|---|---|---|
read_file / write_file | 讀寫文字(自動建父目錄) | path、content |
list_dir / create_dir | 列目錄/建目錄 | path |
move / delete | 移動 / 刪(進 .trash 可恢復,別用 shell rm) | source/dest、path |
find_by_name | 按名找檔案 | pattern |
download | http(s) 下載進沙箱(≤50MB / 60s,別用 shell curl) | url、path |
desktop —— 呈現/通知/剪貼板/確認(L0)
| 工具 | 用途 | 關鍵參數 |
|---|---|---|
open | 打開產物:URL 或檔案;office 文檔必須帶 app: "libreoffice"(白名單,系統 MIME 關聯不可靠) | target、app? |
notify | 桌面通知(三檔見 §3) | summary(必填)、body、urgency、action? |
confirm | 向用戶請示/要決策(T1 四橫幅:允許/會話允許/拒絕/永不,30s 無應答=拒絕) | summary(不放敏感內容)、tier?(t1/t2,影響文案) |
clipboard_read / clipboard_write | 剪貼板讀寫 | — |
session —— 水族館(agent 專屬嵌套 niri 會話,GUI 活都在這幹)
| 工具 | 用途 |
|---|---|
agent_session_start / agent_session_stop / agent_session_status | 嵌套會話生命週期(桌面上出現一個可圍觀的窗口) |
agent_open_app | 在水族館裡啟動應用(如 gnome-calculator、chromium) |
agent_list_windows | 列水族館內窗口 |
agent_screenshot | 定向截水族館內部(grim) |
system —— 系統查詢(L0 唯讀全放行)
system_status / list_services / service_status / journal / network_info /
list_packages / package_info / package_install(T2 強確認:彈 polkit 密碼框,需人在;
無頭跑必 300s 逾時拒絕——別在無人值守時調)。
cred —— 憑證(agent 拿引用不拿明文)
cred_list(可用憑證及其引用/env 變數名)· cred_autofill(通道 A:頁面自動填充,明文即棄)·
cred_exec(通道 B:白名單代執行+輸出打碼)· cred_inject(通道 C:短 TTL 環境變數注入,
到點整組收割)。任何需要密碼/token 的操作只走 cred,禁止向用戶索要明文。
3. 行為約定(實測有效的紀律)
- 產物統一落
~/Agent/,按downloads/ inbox/ reports/ notes/ canvas/ screenshots/分類; 落位即自動進 artifacts 索引(http://127.0.0.1:7788/artifacts),用戶問「我上次的報告在哪」 → 打開該頁或報相對路徑; - 呈現優先級:儀表板/報告/互動內容 → 寫自包含單檔案 HTML 到
canvas/<名>/index.html再open打開http://127.0.0.1:7788/<名>/;office 文檔 →open帶app: "libreoffice"; 網頁 → browser 擴展; - 通知三檔:完成 →
notifyurgency=low + action({kind:"open_path", path:"reports/x.fodt"}或{kind:"open_url", url:...},點通知直達產物);失敗/需盡快知道 → urgency=critical + 一句話結論 + 原因建議;要用戶決策 → 不用 notify,走confirm(notify 沒有選擇語意)。 非同步結果一律通知,不要默默完成; - 確認與分寸:不要動用戶正在用的窗口,除非明確要求(先
list_windows認準目標); 策略拒絕(isError 且提示「策略拒絕」)時如實告知,不要繞路;GUI 操作在水族館裡做, 不搶用戶焦點; - 憑證只走 cred-mcp 引用(§2 cred 表後那句紀律)。
4. 環境坑速查(全部踩過)
| 坑 | 現象 | 處置 |
|---|---|---|
| niri IPC socket 路徑每次會話變 | niri msg 連不上 | 用啟動器(自動發現 NIRI_SOCKET),別硬編碼 |
| Chromium 鑰匙圈鎖死網路服務 | 瀏覽器拉起即卡 | 啟動帶 --password-store=basic(browser 擴展已內建;自己起 chromium 記得帶) |
| soffice 單實例鎖 | 已開 LibreOffice 時再 open 同類文檔可能無窗/卡 | 先確認 soffice 行程狀態;文檔打開用 app:"libreoffice" 走啟動器語意;上次異常退出還會彈 "Document Recovery" 窗口(2026-10-18 實測),列表確認窗口時別把它當成目標文檔窗 |
| LibreOffice MIME 關聯不可靠 | Fedora 把 fodt 識別成 text/xml 用編輯器打開 | open 必須帶 app:"libreoffice" |
| Qt 應用不出現在 a11y 總線 | a11y_list_apps 看不到 | 系統已全局開(/etc/environment 的 QT_LINUX_ACCESSIBILITY_ALWAYS_ON=1,重啟持久);仍看不到=應用啟動早於環境落位或非 Qt 官方綁定,重啟應用 |
| Chromium/Electron a11y 樹為空 | dump 不出內容 | ① 啟動帶 --force-renderer-accessibility(browser 擴展已默認帶);② toolkit-accessibility 必須在應用啟動前為 true(已全局開;後改無效,須重啟應用);③ 樹懶構建,dump 一次讓橋觸發(見 §2 a11y 紀律) |
| 鍵盤注入選型 | 需要打字/快捷鍵 | scripts/kb-inject.sh:文字與組合鍵用 wtype(用戶級);Enter/Tab 等控制鍵 wtype 對 GTK 不生效,走 ydotool(sudo);中文可能丟首字元,輸完從 a11y 樹讀回核對 |
| 水族館初始有 niri 熱鍵浮層 | 截圖被"Important Hotkeys"遮擋 | 注入 Esc 關閉;agent 沙箱裡連不上 ydotoold socket(root 600),借 niri__open_app 在真實桌面環境執行注入 |
| ydotool 指針漂移 | 絕對定位失準(libinput 加速度 ≈0.5) | 指針不可依賴;鍵盤注入可靠;socket 路徑注意 /tmp/.ydotool_socket vs $XDG_RUNTIME_DIR 的版本差異 |
| GTK4 樹初始不完整 | dump 只有十幾個節點 | 先 a11y_list_apps/dump 一次讓 AT 客戶端註冊,再 dump 才完整(16→105 節點實測) |
| 無頭跑 T1/T2 確認 | 30s/300s 無人應答自動拒絕 | 預期行為:寫操作需要用戶在場,或測試時經 org.agentos.Confirm1.Respond 程式設計應答 |
| 裝包被 packagekitd 搶鎖 | dnf 偶發失敗 | 裝前 systemctl stop packagekit(T2 helper 已處理) |
5. 升級路徑(當目標應用打不開時按序嘗試)
L0(系統層,永遠先試)→ L1(a11y:GTK3/4/Qt 已默認可達;Chromium 檢查 flag 與
toolkit-accessibility 順序;第三方 Electron 按 capabilities.md 附錄 B 加 flag)→
L2(網頁類必走)→ L3 兜底(grim 看 + kb-inject 鍵盤;指針不可靠)。
仍打不開的類型:純 X11 應用(xterm 類,無樹,L3 是終點)、Java(需 JAB,路徑見
capabilities.md 附錄 B)、合成器自身 UI 只能輸入注入——詳表見倉庫 docs/capabilities.md。