Free protocol behaviour
These are derived from the projection. There is no code to write for any of them — which is exactly why they are easy to forget.
What tools/list contains
Not every open window — only the operable ones. The list is the product of two filters:
- Which windows are operable — modals block the windows beneath them (below).
- Which of that window's actions are available — an action hidden with
set_available(false)contributes nothing.
So a tool appears if and only if its window is operable and the action itself is available. Everything else is absent, and any change to either recomputes the list and notifies clients.
Operability and modal chains
Opening a modal changes which windows an agent may operate. Purview recomputes operableWindowIds and removes the blocked windows' tools from tools/list: the clearest possible signal that a window is unavailable.
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| Opened with | Effect on the operable set |
|---|---|
app.open(..) | A normal top-level window |
win.open_modal(..) | Its owner becomes non-operable while the modal is open |
app.open_app_modal(..) | Only the modal's own tree stays operable |
It is a hand-off, not an addition
Opening a modal does not add its tools to the parent's. The parent's tools disappear and the modal's appear in their place. An agent that was mid-task on the parent will find those tools simply gone.
Three consequences worth internalising:
Only the tail of a chain is operable. With B → D1 → D2, both B and D1 are blocked — D1 is itself a modal, but it is also D2's owner. Only D2 contributes tools.
Unrelated windows are untouched. A modal blocks its own owner, not the application. If A opens a modal while C sits beside it, C stays operable and keeps its tools. The one exception is an app-level modal, which collapses everything outside its own tree.
Nothing is frozen server-side. Operability governs what is offered; it is not a lock. A stale call to a now-blocked tool is still validated at runtime and rejected with blocked_by_modal — see Errors.
Design rule
A modal must carry the tools needed to dismiss it. If the parent's tools vanish and the modal offers none, the agent is stuck with no way forward — it cannot even close the dialog.
Optimistic concurrency
Every window carries a version. A tool call may include expectedVersion; if it no longer matches, Purview rejects the call with stale_state (and the current version) before your handler runs. Two agents cannot silently clobber each other, and you write nothing to get it.
And the rest
openedWindowIds. Windows opened during a handler are tracked and reported in that call's result, so the agent immediately learns their ids.
Notifications. Version bumps trigger resource-updated notifications, coalesced, and broadcast to every session.
Redaction. Fields marked sensitive never leave in app://windows.