Skip to content

免費獲得的協定行為

以下這些全都是由投影推導出來的。它們沒有任何程式碼要你寫——而這正是它們容易被忘記的原因。

tools/list 裡究竟有什麼

不是所有已開啟的視窗——只有可操作的那些。這個清單是兩層篩選的乘積:

  1. 哪些視窗可操作 —— 模態會阻擋它底下的視窗(見下文)。
  2. 該視窗的哪些動作可用 —— 以 set_available(false) 隱藏的動作不會出現。

換句話說,一個工具會出現的條件是:它所屬的視窗可操作該動作本身可用。其餘一概不會出現;任何一層有變動都會重新計算清單並通知用戶端。

可操作性與模態鏈

開啟模態會改變 Agent 可以操作哪些視窗。Purview 會重新計算 operableWindowIds,並把被阻擋視窗的工具從 tools/list 中移除:這是表達「某個視窗目前不可用」最清楚的訊號。

rust
let confirm = win.open_modal("Confirm");   // window-modal: blocks its owner
let alert   = app.open_app_modal("Alert"); // app-modal: collapses to its own tree
confirm.close();                           // operability is restored
開啟方式對可操作集合的影響
app.open(..)一個普通的頂層視窗
win.open_modal(..)模態開啟期間,其擁有者變為不可操作
app.open_app_modal(..)只有模態自身的樹狀結構仍可操作

這是交接,不是疊加

開啟模態並不會把它的工具加到父視窗的工具之上。父視窗的工具會消失,由模態的工具取而代之。一個正在父視窗上執行任務的 Agent 會發現那些工具就這樣不見了。

有三點值得記住:

只有鏈尾可操作。 對於 B → D1 → D2B D1 都被阻擋——D1 本身是模態,但它同時也是 D2 的擁有者。只有 D2 會提供工具。

無關的視窗不受影響。 模態阻擋的是它自己的擁有者,而不是整個應用程式。如果 A 開啟了模態,而 C 就在一旁,那麼 C 依然可操作、工具照舊。唯一的例外是應用程式層級的模態,它會把自身樹狀結構以外的一切都收縮掉。

伺服器端並沒有「凍結」任何東西。 可操作性決定的是「提供什麼」,它不是一把鎖。對一個剛被阻擋的工具發出的過期呼叫,仍會在執行期被驗證並以 blocked_by_modal 拒絕——參見錯誤與驗證

設計準則

模態必須帶有能關閉自己所需的工具。如果父視窗的工具消失了,而模態又沒提供任何工具,Agent 就會徹底卡死——它連這個對話框都關不掉。

樂觀並行

每個視窗都帶有一個 version。工具呼叫可以附上 expectedVersion;一旦它已不再相符,Purview 就會在你的處理器執行之前stale_state(附帶當前版本)拒絕該次呼叫。兩個 Agent 不可能默默互相覆寫,而你什麼都不必寫就能得到這項保障。

其餘部分

openedWindowIds。 在處理器執行期間開啟的視窗會被追蹤,並在該次呼叫的結果中回報,讓 Agent 立刻得知它們的 id。

通知。 版本遞增會觸發資源更新通知,經合併後廣播給每一個 session。

遮蔽。 標記為敏感的欄位絕不會出現在 app://windows 中。

動態 tools/list。 只有當下可操作的視窗才會貢獻工具,並在每次變更時重新計算。