lazyBoy/docs/hermes-agent-cua-review.md

27 KiB
Raw Blame History

hermes-agent 對照harness 與 Cua 合作的順暢度2026-09-08更新2026-09-09

研究對象:NousResearch/hermes-agentcommit abd83ab560327c58f17f8e2ecd9be0307d5c9cf912,269 檔)。本輪擷取 tools/computer_use/ 全 14 檔與官網 computer-use 使用者手冊;下文 hermes 路徑皆為該 repo 相對路徑LazyBoy 路徑附 file:line

要回答的問題不是「要不要換一套 Cua 用法」,而是:hermes-agent 在同樣的 cua-driver 限制下做了哪些選擇,是我們的 harness 可以拿來讓模型用得更順的?

結論

可以改善,而且不需要改動 Cua 串接層。 hermes-agent 的 computer_use 同樣落到 cua-driver差別是它走 MCP over stdio我們走 per-call CLI兩邊面對的是同一個 Linux AT-SPI/X11 桌面、同一批 driver 工具、同一種「動作可能送達但無法證明生效」的不確定性。差距幾乎全在 harness 這一層

  1. hermes 把 Cua 回給我們的語義結果effect / escalation / verified誠實地換算成一個 verdict 交給模型,並在 prompt 裡明令「已確認生效不得重打輸入」。我們讀了這些欄位,只用來決定要不要自己重試一次,模型本身看不到
  2. 我們用 byte-exact 的 frame_id 判斷「畫面沒變」真實桌面上時鐘與游標一直動等於這個判斷永遠不成立——重複點同一個按鈕的教練機制因此形同虛設。repo 裡已有感知級簽名,但只用在教學影格。
  3. 我們的 observation 文字同時列出元素清單與完整 elements JSON兩份內容、皆無上限dense 桌面一輪就能燒掉數千 token。hermes 對元素數量與標題長度都有預算,多出來的 spill 到檔、讓模型自行 read_file

其餘是次要項串行觀察呼叫、非視覺模型、driver 版本策略、safety 硬擋)。以下逐項附證據。

比較基準

LazyBoy hermes-agent
呼叫方式 每次動作 spawn 一個 cua-driver call CLI 行程(crates/control/src/cua/client.rs:273 常駐 MCP session over stdioCLI 僅當 fallbacktools/computer_use/cua_backend_session.py
驅動版本 硬 pin 0.23.x,不符即 unhealthycrates/control/src/cua/mod.rs:39 不 pin改成 runtime contract gateversion floor + 必要 argv 集合)+ 逐工具 capabilitycua_backend_driver.py:154
工具數 21 個獨立 toolcrates/api/src/tools.rs:64 單一 tool + action 判別參數,省每輪 schema tokentools/computer_use/schema.py
結果語義 語義結果換算成 ActionVerdict,並寫進 tool result 第一行(crates/control/src/cua/client.rs:442 verified/effect/escalation/degraded 換算成 verdict.decisiontool.py:409
失敗後重放 工具層 150s 與底層 120s 都 fail closed不重放、訊息講明效果不明crates/api/src/runs.rs:1288crates/control/src/cua/client.rs:199),並把 session 標 suspect、下次呼叫前重建crates/control/src/cua/client.rs:247 傳輸/逾時一律 fail closed、絕不重放 mutation,並將 session 標記 suspect、下次呼叫前重建cua_backend_session.py:71, :181, :495

逐項對照

每一節講的都是診斷當下的狀態;已落地的章節在標題標明,位置與對應測試統一列在〈已交付〉。

1. 逐動作 verdict 沒有回到模型(最高價值,已實作)

hermes 的做法(tool.py:409 _classify_action_resulttool.py:429 _action_payloadcua_backend_parse.py:_action_result_from):把 driver 的 verifiedeffect ∈ {confirmed, unverifiable, suspected_noop}escalation.recommendedpathdegradedcode 原帶回傳,再換算成一個模型看得懂的決定:

  • confirmed{"decision": "done"}
  • unverifiable{"decision": "verify_fresh_state"},附「先重擷再決定,不可以只因為有 escalation 建議就重打輸入
  • suspected_noop / 有 code{"decision": "escalate"} + 建議模式
  • 傳輸成功但無語義證據 → 同樣是 verify_fresh_state(原文註解:transport success without semantic proof is not proof of effect

LazyBoy 現況:recommended_delivery()/escalation/recommendedcrates/control/src/cua/client.rs:430)後自己換 delivery_mode 重試一次(crates/control/src/cua/client.rs:226effect 只在 refused|error 時當失敗(crates/control/src/cua/client.rs:484)。往上一層就斷了:ActionResult { clipboard_text, completed, observation } 原本沒有逐動作訊號欄位模型只會看到「completed N computer action(s)」加下一張截圖。(已修:ActionResult.verdictcrates/control/src/sandbox.rs:190,見〈已交付 A〉。

後果:一個 unverifiable 的輸入Linux AT-SPI 很常見)在我們這裡跟 confirmed 完全一樣。模型無法判斷該重看還是重打,只能從下一張截圖猜——這就是重複點擊的起點。

2. 「畫面沒變」是 sha256 byte-exact最高價值已修正

frame_id = sha256(PNG)crates/control/src/observe.rs:16),比較用 frames_matchcrates/control/src/observe.rs:133。桌面只要有時鐘、游標閃爍、notification、動畫sha256 就不同 → unchanged 永遠是 false → miss_streak 永不累積(crates/api/src/tools.rs:1059)。兩個教練因此失靈:

  • note_click_resultcrates/api/src/tools.rs:1055)的「別再重複同一個點擊」建議
  • should_block_stale_clickcrates/control/src/actions.rs:139的硬性擋掉streak ≥ 2 且同一目標)

失靈後模型會一直重複同一個無效點擊,直到 LoopGuard 的輪次政策(crates/harness/src/policy.rs)把整個 run 停下——花掉整輪預算才換來一句本來可以在第一輪就講的話。

repo 裡已經有對的工具:frame_signature / signatures_similarcrates/control/src/observe.rs:143, :15632×18 灰階、變動 < 2% 視為相似),但目前只用在教學關鍵影格(crates/api/src/skills.rs:858)。

3. Observation payload 沒有預算,而且重複兩份(已修正重複與上限)

observation_textcrates/api/src/tools.rs:471)先印 format_ui_elementscrates/control/src/observe.rs:69[id] title 串接、無上限),接著又 dump 整個 elements JSONselectorrole、幾何)。上層 merge_ui_elementscrates/control/src/a11y.rs:6也不設上限。Windows 標題允許 256 字(crates/control/src/cua/mod.rs:542Chromium 的 a11y 樹還會把整段訊息內文當成 label。只有 checkpoint 寫入時才會被 shrink_checkpoint 截到 48KBcrates/api/src/runs.rs:1933)——那是存檔保護,不是送給模型的預算。

hermes 的預算(tool.py:441_DEFAULT_MAX_ELEMENTS = 100_MAX_ELEMENT_LABEL_CHARS = 120、summary 只列 40 行,被截到的完整樹 spill 到檔並告知模型可用 read_file/search_files 取回原因是「Discord/Slack via UIA 把整個訊息內文當 label沒上限會爆掉 tool-result 預算並外洩聊天文字」。它甚至刻意讓 multimodal 回應不附 elements array(已有截圖時兩份是浪費,tool.py:549 _capture_response)。

4. 未知結果沒有徹底 fail closed已實作

hermes 把「效果不明確」當成獨立類別(cua_backend_session.py:71 _UNKNOWN_OUTCOME_MESSAGES):傳輸失敗或 MCP 逾時都回 isError + next_step: "fresh_state",訊息裡明講「動作可能已經生效,所以 Hermes 沒有重放它」。_TRANSPORT_REPLAY_SAFE_TOOLS:181)是一組唯讀工具白名單,只有它們允許在傳輸錯誤後重試(:495, :504mutation 一律不重放。

LazyBoy工具層 150 秒逾時的措辭已經對了(crates/api/src/runs.rs:1288Its effects are unknown … never repeat a step that already worked)。但底層 CLI 的 120 秒逾時只拋 ControlError::Timeoutcrates/control/src/cua/client.rs:381),訊息是 computer action timed out——沒有任何指示,模型的自然反應就是重打一次。同一個不確定性有兩種處理標準。(已修:crates/control/src/controller.rs:59Timeout 訊息現在明講效果不明、先觀察、不得重放成功的步驟。)

5. 逾時後不重建 driver session已實作

hermes 一次 timeout 就把 session 標成 suspect下次 computer-use 呼叫前先重建。LazyBoy 只在 driver 回報 session_ended 時 revive重發 start_sessioncrates/control/src/cua/client.rs:151-163)。逾時/半死 socket 不會進那個分支,於是同一個可疑 session 會繼續被用下去,後續每個動作的結果都不可信。(已修:逾時即標記 suspect下次 mutation 前重建 sessioncrates/control/src/cua/client.rs:247。)

6. 一次觀察要 spawn 四次 CLI已併行

observe_display 原本串行做:get_desktop_state(寫檔讀 PNGscreen_size(必要時)→ cursorwindowsnative::observe,每次都是一個行程 + socket 往返,全部加在模型等待時間裡。(已修:crates/control/src/cua/mod.rs:487cursor 與 windows 用 tokio::join! 併行(crates/control/src/cua/mod.rs:512。cursor 不省——scroll_point 要靠它瞄準,而且併行之後已不額外花時間。)

hermes 另外示範了「用本機命令取代一次 driver 呼叫」:_select_capture_targetcua_backend_capture.py:58)用 xprop _NET_ACTIVE_WINDOW2 秒 timeout判作用中視窗只在 X11 z_index 同值時才採用,避免多一次 driver 往返。

7. 空結果會跨 transport 重取(已區分「失敗」與「空」)

hermes _fetch_or_refetchcua_backend_capture.py:150MCP 成功但內容為空時,改用 CLI 再取一次——因為「成功但空」在 Linux 上常常是 transport 問題,不是桌面真的沒東西。我們原本也沒有防禦:windows(...).unwrap_or_default() 把失敗直接變成「沒有視窗」,模型看到的是空桌面。(已修:失敗與「真的沒有視窗」分開,觀察會標 partial見〈已交付 E〉跨 transport 重取本身不做,理由見〈不做〉。)

8. 非視覺模型完全不能用電腦(已給 ax-only 觀察)

hermes 支援 capture(mode='ax')(不附截圖、只有元素樹)與 vision_routing.py:不確定主模型能否吃圖時 fail closed 到 aux vision 先把截圖轉文字。LazyBoy 的 vision_guardcrates/api/src/tools.rs:455)原本對非視覺模型直接回「請改選視覺模型」,連純元素樹的觀察都一起擋掉。已修ax-only 降級路徑已做,非視覺模型保留元素樹觀察、擋掉像素工作,見〈已交付 H〉aux vision 需要第二模型的呼叫管線,延後。)

9. 驅動相容性:硬 pin vs contract gate已加 capability probe

hermes 明確拒絕 version pincua_backend_driver.py:19 註解上游安裝器一律抓最新版pin 只是幻象改檢查「version floor + 必要 argv 集合 + 逐工具 capability tokens」舊 driver 不支援 foreground 就明確 refused 而不是降級假成功。LazyBoy 只認 0.23.xcrates/control/src/cua/mod.rs:39,且有測試驗證 0.24.0 視為不符,crates/control/src/cua/mod.rs:951)。

取捨要講清楚:在生產環境我們現在這樣比較好可重現、驗收過就是驗收過。hermes 的教訓是「pin 要配 capability probe」——當我們升到 0.24 時,不該只改常數,而該在 unhealthy 訊息裡點名缺哪個 argv否則現場只會看到「driver unhealthy」這種查不出原因的話。(已做:cua-driver manifest + list-tools 的 capability probe見〈已交付 G〉pin 本身保留。)

10. Safety 與 schema 細節(已實作硬擋與建議)

hermes 有幾件我們沒有的硬擋(tool.py:47:51:57:68

  • 危險 type 內容模式擋掉(curl … | bashsudo rm -rf、fork bomb
  • 危險按鍵組合硬擋,且 _canon_key_combo 會把 ctrl-alt-delete 這種底線寫法正規化後才比對
  • _input_target_mismatch:輸入落到別的視窗時不報 ok防「打錯視窗還說成功」
  • 未知 action 回「did you mean X?」,而不是丟一個 schema 錯誤讓模型亂猜
  • tool schema byte-frozen 以保 prompt cache 命中
  • capture_after=true:動作回應直接附新截圖,省一次 round-trip

LazyBoy 已有同級品的部分SOM 數字疊字 overlay_elementscrates/control/src/overlay.rs:47)、容許 #12/[7] 等寫法的 element_idcrates/control/src/actions.rs:49)、歷史裡只留最近一張截圖(crates/api/src/runs.rs:1905)、批次動作 MAX_COMPUTER_ACTIONS

11. LazyBoy 已經比較強的地方(不用移植)

輪次政策與停看聽(crates/harness/src/policy.rs、takeover/resume、checkpoint 續跑、記憶 recall 預算、steering、make cua-smoke 端到端驗收。hermes 沒有多使用者、容器隔離、真人接管這層——它的問題意識是單一使用者長期佔用一台機器。

改造清單

P0順暢度差異最直接改動侷限在 harness

項目 改動點 預期效益 風險
verdict 上層給模型(已做) ActionResultverdict/effect/escalationcrates/control/src/sandbox.rs:146)→ crates/sandbox/src/docker.rs 帶過 controld wire → observation_text 一行結論 模型知道「已送達未證明」該重看、不該重打;少一整個重複點擊循環 動到 controld wire format向後相容全部 #[serde(default)]);措辭要短,否則反而變長
感知級 unchanged 用於教練(已做) ToolCtx 加簽名欄位,pack_observationframe_signature;截圖是否附行的 byte-exact 邏輯不動 同一目標的重複點擊在累計兩次「無可見變化」後被擋掉;不再靠 LoopGuard 收場 簽名比對是啟發式;只影響教練建議、不影響送不送圖,風險可控

P1

  • observation payload 預算(已完成):元素逐行([id] role "標題" @ x,y+ 數量上限 + +N more not listed 提示,移除重複的 elements JSON(模型只需要 idelement_id);保留 frameId/尺寸/activeWindow 小 metadata。代價一旦截斷模型無法直接看到被省略的元素——先靠提示叫它捲動重看之後再考慮 hermes 式的「寫到檔 + read_file」。
  • 底層 timeout 改成 fail closed 措辭(已完成)ControlError::Timeout 的訊息加上「效果不明、可能已生效、先觀察再決定、不得重放」,並與 runs.rs:1288 的措辭統一。
  • 逾時後標記 suspect 並重建 session已完成:在 CuaClient 記一次逾時,下次 mutation 前先 start_session;失敗就明確報 driver unavailable。
  • 空結果不當成空桌面(已完成)list_windows 失敗要與「真的沒有視窗」區分(現在是 unwrap_or_default())。

P2

  • (已完成) observe_display 的 cursor 與 window 清單改成 tokio::join! 併行(crates/control/src/cua/mod.rs:512。cursor 不省scroll_point 靠游標位置瞄準,併行之後它已經不額外花時間。
  • ax-only 已完成aux vision 延後) 非視覺模型保留元素樹觀察,只有像素工作仍擋(〈已交付 H〉
  • (已完成) driver 不相容時點名缺少哪個 argv哪個工具而不是只說 unhealthy〈已交付 G〉
  • (已完成,capture_after 除外) 危險 type 內容與按鍵組合硬擋、未知 action 給建議(〈已交付 F〉capture_after 評估後不做(〈不做〉)。
  • (改做守門測試) 21 個 tool 的 schema 合併會弄壞 checkpoint 裡舊的 tool_calls,改以序列化穩定性測試保住快取鍵(〈已交付 I〉、〈不做〉

已交付

清單來自本節的診斷P0/P1/P2 全部落地或在〈不做〉裡記錄理由。每項給位置與測試名稱,測試都可在 repo 內直接搜尋。

A. verdict 回到模型P0

  • driver 回包的 effect / verified / escalation / degraded / code 換算成 ActionDecision{Done, VerifyFreshState, Escalate}crates/control/src/sandbox.rs:151)與 ActionVerdictcrates/control/src/sandbox.rs:171),掛在 ActionResult.verdictcrates/control/src/sandbox.rs:190)。
  • 換算規則在 crates/control/src/cua/client.rs:442 action_verdictconfirmed 壓過任何 escalation 建議、suspected_noop 或拒答 code → escalate、其餘證據 → verify_fresh_state完全沒有語義證據就回 Nonetransport 成功不等於生效)。測試:a_confirmed_effect_outranks_an_advisory_escalationunverifiable_effect_becomes_verify_fresh_statesuspected_noop_and_refusal_codes_escalatea_reply_without_semantic_evidence_claims_nothing
  • 批次動作取最嚴重的一筆(crates/control/src/cua/mod.rs:701 merge_verdictActionDecision 的列舉順序就是嚴重度),經 controld wirecrates/controld/src/main.rs:166)回到 crates/sandbox/src/docker.rs:263;兩側都是 Option + #[serde(default)]舊 controld 只是不带 verdict不會解析失敗
  • 模型看到的是 tool result 的第一行:crates/api/src/tools.rs:1077 verdict_notecrates/api/src/tools.rs:1096 with_verdict(前置,不會被截圖說明蓋掉)。已確認 → 「不得重複這個動作」;未證明 → 「先讀新截圖,絕不重打可能已生效的輸入」;無效果 → 「換做法,別重打同一輸入」。測試:crates/api/src/tools.rs:1983 verdict_tests
  • 「已確認生效」同時算進 miss streak 的判斷(crates/api/src/tools.rs:902),免得面板時鐘把一次成功的點擊當成「畫面沒變」。
  • 代價verdict 要 desktop image 裡的 controld 重建後才會出現;本輪沒有重啟 live 容器,所以線上暫時仍是「沒有 verdict」而非錯誤答案。

B. 感知級 unchanged 只供教練判斷P0

  • ScreenChange{Identical, Similar, Changed}screen_change_betweencrates/control/src/observe.rs:174crates/control/src/observe.rs:184ToolCtx.previous_signature 每輪更新觀察文字分三种標記byte-exact (screen unchanged)、感知 (no visible change)、無標記)。
  • 附不附截圖仍然只看 byte-exact細微視覺變化不會被藏起來miss streak 與 should_block_stale_click 讀感知狀態,所以時鐘不再把 streak 歸零。測試:screen_change_calls_a_clock_tick_similar_not_identical

C. 觀察清單有預算P1

  • format_ui_element_linescrates/control/src/observe.rs:89)取代「清單 + 完整 elements JSON」兩份內容每個控制一行 [id] role "標題" @ x,y,標題 60 字、元素 120 個(crates/api/src/tools.rs:467 MAX_LISTED_ELEMENTS),超出以 +N more not listed 說明frameId尺寸activeWindow 小 metadata 保留。測試:lists_each_control_once_and_names_what_was_droppedelement_lines_cap_the_count_and_long_labels

D. 未知結果徹底 fail closedP1

  • ControlError::Timeout 的訊息(crates/control/src/controller.rs:59)改成「效果不明、可能已生效、先觀察再決定、不得重放已成功的步驟」,與 crates/api/src/runs.rs 工具層 150 秒的措辭一致;保留 timed out 子串,monitor.rsruns.rsmcp.rs 的失敗分類不受影響。
  • CuaClient 記 suspect 顯示(crates/control/src/cua/client.rs:23mutation 逾時即標記該 display 且永不重放;下一次 mutation 前先 repair_suspect_sessioncrates/control/src/cua/client.rs:247)重建命名 session成功的話補一次游標動態設定再逾時就保留標記繼續 fail closed。唯讀工具read_after_session_restart)不付這多出來的 round-trip。

E. 空視窗清單不等於空桌面P1

  • list_windows 失敗不再 unwrap_or_default() 變成「沒視窗」:失敗時把 native_observation_complete 關掉(crates/control/src/cua/mod.rs:564),欄位語義寫進 crates/contracts/src/action.rs:145
  • 觀察文字在清單標題後加 coveragecrates/api/src/tools.rs:483(partial: some windows did not report controls, so a missing entry is not proof)——模型正是在這一行決定「按鈕不存在」。測試:an_incomplete_sweep_says_a_missing_control_is_not_proof

F. Safety 硬擋與「你是不是打錯」P2

  • 未知 action 名稱給出最近的正確名稱,或指向正確工具(crates/control/src/actions.rs:146 KIND_ALIASES:183 OTHER_TOOL_HINTS:190 nearest_action_kindscreenshotcaptureobservecomputer_observesnapshotbrowser。測試:an_unknown_action_name_points_at_the_right_thing
  • 按鍵組合先正規化再比對(:234 BLOCKED_KEY_COMBOS:242 canonical_keys:260 blocked_key_comboctrl+alt+deletectrl-alt-deletesuper+l 一律擋(session_killing_shortcuts_are_blocked_however_they_are_spelled)。
  • 危險 type 內容擋掉(:277 blocked_textcurl … | shrm -rf /、fork bomb、of=/dev/,比對前做空白折疊,所以多空格/換行繞不過(destructive_typed_text_is_blocked_and_the_work_is_not)。

G. driver 不相容時點名缺什麼P2

  • pin 保留(理由見〈不值得照搬〉),但失敗時不再只說 unhealthycua-driver manifestcrates/control/src/cua/client.rs:105)比對我們真的會 spawn 的 argvcrates/control/src/cua/mod.rs:52 REQUIRED_CLI_ARGScall --socketcall --screenshot-out-filestatus --socketcua-driver list-toolscrates/control/src/cua/client.rs:123)比對我們真的會 call 的 25 個工具(crates/control/src/cua/mod.rs:59 REQUIRED_TOOLS,與 0.23.2 逐一对過)。
  • 輸出是「driver CLI is missing: call --screenshot-out-file」或「driver tools are missing: X」全部都在時則是「…only the version series differs」crates/control/src/cua/mod.rs:136 capability_gap。兩個探測都唯讀、10 秒上限、只在 health 失敗時跑,老 driver 沒這兩個 verb 就靜默回到舊訊息。測試:an_older_driver_is_named_by_the_verb_flag_it_lacksthe_pinned_driver_surface_passes_the_cli_contracta_missing_tool_is_named_instead_of_a_bare_unhealthythe_tool_list_keeps_names_and_drops_banner_noise

H. 非視覺模型可以讀桌面P2

  • 拆成 gui_blockedcrates/api/src/tools.rs:440,真人接管時全擋)與 vision_guardcrates/api/src/tools.rs:455,只有像素工作需要視覺模型:computer_actbrowsershellopen_pathlaunch_appconnection_checkuse_saved_login)。
  • computer_observewait 改用 gui_blocked:非視覺模型照樣拿到元素樹文字,pack_observation 不附圖並加註 crates/api/src/tools.rs:1112「(elements only: this model cannot see the screen)」,順帶省掉 SOM overlay 與圖片 token。
  • 系統提示跟一句改寫(crates/api/src/runs.rs:50):點擊/輸入/瀏覽需要視覺模型,純文字模型仍可用 computer_observe 讀元素樹。

I. prompt cache 的守門測試P2

  • tool_definitionscrates/api/src/tools.rs:64)的位元組就是每輪請求的快取鍵。三條測試:the_tool_schema_is_byte_stable_across_callstool_names_are_unique_and_open_with_the_computer_pairmemory_tools_are_appended_so_the_desktop_schema_never_moves(記憶工具只能附加在尾端,桌面那半不能因為開關而位移)。位置:crates/api/src/tools.rs:2037 tool_schema_tests

不值得照搬

  • Python 重構、hermes 自己的 MCP session/embedded daemon 管理:我們的 per-call CLI 在容器模型下可重現性更高,換 transport 是另一個案子。
  • macOS/Windows 專屬路徑(windows_hide_flags、Quartz、UIA 特例)與 desktop app 整合LazyBoy 只做容器內 Linux 桌面。
  • 取消 driver pin與我們的驗收政策相反見第 9 節取捨)。
  • hermes 的 tool-result spill 到 host 檔:我們的沙箱檔案系統與 workspace 語義不同,先要用再設計。

不做(清單裡剩下的,連理由)

  • capture_afterActionRequest.observe(預設 true已經讓動作回應直接附上新觀察再疊一個 driver 層的 capture_after 是把同一件事做兩次,只會多出一個會不一致的開關。
  • 21 個 tool 合成單一 action 判別:省的是每輪幾 KB 的 schema token代價是 checkpoint 裡舊的 tool_calls 名稱失效——續跑的 run 會開始報 unknown tool。改用 〈已交付 I〉 的序列化穩定測試保住現有的快取鍵。
  • aux vision先用輔助視覺模型把截圖轉文字repo 內沒有第二模型的呼叫管線provider 選擇、額度、錯誤處理都要一條線),本輪先把「非視覺模型完全不能用」降級成「能讀不能動」,剩下的記在這裡。
  • 跨 transport 重取hermes _fetch_or_refetch:我們只有一條 per-call CLI transport沒有第二條可以換過去重取同樣的懷疑改用 〈已交付 E〉 的「不完整」標記表達。
  • 取消 driver pin:保留 PINNED_DRIVER = 0.23crates/control/src/cua/mod.rs:39,相容性測試在 mod.rsa_new_minor_series_is_not_compatible。hermes 的教訓不是「不該 pin」而是「pin 要配 capability probe」——現在升 0.24 時unhealthy 訊息會點名缺哪個 argv工具而不是只說不相容。

驗證

  • cargo test --workspace --locked --no-fail-fastcontracts 4、control 100、harness 36、sandbox 2、supervisor 2 全綠api 89 通過、2 失敗——memory::tests::database_enforces_agent_scope_and_queries_do_not_leakmonitor::tests::memory_usage_is_historical_scoped_and_respects_deletion 需要 make postgres 的測試資料庫(PoolTimedOut改動前即失敗,與本輪無關。
  • 新增測試control 端 verdict 4 條、safety 3 條、capability probe 4 條api 端 verdict_tests 3 條、觀察清單/coverage 3 條、tool_schema_tests 3 條(名單見〈已交付〉)。
  • cargo clippy --workspace --all-targets --locked -- -D warningscargo fmt --all --check:乾淨。
  • capability probe 的比對基準用 image/computer 那份 image 離線跑 cua-driver manifestlist-tools 取得0.23.2REQUIRED_TOOLS 25 項全部有廣告;探測本身不在 live 容器上驗證。
  • 端到端順暢度仍須 make cua-smokemake computer本輪未重啟、未重建 live 容器lazyboy-api-1lazyboy-supervisor-1lazyboy-postgres-1、paused 的 lb-team-local-space),所以 verdict 上層與 wire 變更要在下一次鏡像重建+驗收桌面重跑後,才會在線上看見;重複點擊的實際改善同樣要那一次才算數。