Python: serving the SPA
How agency-django boots the Agency desktop SPA and pushes live catalog rows. Schema shape: UI Schema. Vue components: Frontend components (agency-ui).
Bootstrap shell
agency.views.ui renders templates/agency/ui/index.html with:
Vite manifest → hashed JS/CSS under
/static/agency/ui/#agency-bootstrapJSON:user,prefs(workspaceId+desktop),apiBase,websocket,ui(desktop document)
Product URL: /. Login: /login/ (mode: login bootstrap).
The shell is an installable PWA: /manifest.webmanifest + /sw.js
(scope /). /health/ returns boot_id / ui_build so the SPA can
reload after a backend restart. /offline.html is the cached wait screen.
Preferences API: GET/PATCH /api/me/prefs/. Window geometry and chrome
pose for the session are mirrored into prefs.desktop (see UI).
agency.ui.pose.parse_pose accepts numbers only.
Desktop schema
Default desktop layout: agency/data/default_desktop.json
(dock icons, window defs). Window ids use the address grammar
(agents, documents, …).
Host apps can extend via their own schema producers; live geometry lives in
the frontend windowsStore and is persisted via user prefs, not in the
schema document.
Live lists (observe)
Register a model once:
from agency.channel.observe import observe
observe(
Execution,
resource="execution",
card=execution_catalog_card,
fetch=lambda pk: Execution.objects.select_related("mission", "agent").get(pk=pk),
)
On save/delete → catalog bus envelope on /channel/ (group users) →
SPA catalog projector updates explorer lists without polling. Card shape =
list serializer fields.
Catalog hydrates (agent, mission, tool, model, provider) walk every DRF page
(fetchList). Default PAGE_SIZE is 10 — keeping only results drops
rows such as tools.tavily.internet_search. Timelines (executions, tasks) stay paged.
To add a resource:
Django:
observe(...)+ list APIagency-ui:
registerObserveBucketin the owning Pinia storeNo new resource-specific event — same catalog envelope (
<resource>.<id>.catalog.<action>)
Wire details: Connections.