Agent Zero AI framework

Defer Office desktop startup

Make the Office canvas mount passive so Xpra starts only when the Desktop surface is opened or an official Office document is created/opened. Track Desktop host visibility to unload hidden frames, stop monitors, dedupe viewport resize work, and set Xpra offscreen mode according to HTTPS support. Add a near-future note for the tunnel memory footprint. Show Office desktop startup progress Display a loading message while the Agent Zero Desktop environment is starting or restarting, so the right-canvas Desktop button gives immediate feedback before Xpra finishes waking up.

Alessandro committed May 3, 2026 at 02:57 UTC 2d389af7279ae0b51c878eac6869899e7ded1bce
8 files changed +162 -48
docs/agents/office-tunnel-memory-note.md new
+20
@@ -0,0 +1,20 @@
1 +# Office Tunnel Memory Footprint Note
2 +
3 +During the ARM Desktop audit on 2026-05-03, `run_tunnel.py` was the clearest near-future memory optimization candidate. The process held roughly 573 MiB PSS while the main UI process held roughly 706 MiB PSS, even though the tunnel should mostly be a lightweight network edge.
4 +
5 +## Observed Shape
6 +
7 +- `run_tunnel.py` imports enough of the framework stack to pull in heavy provider and API dependencies.
8 +- The tunnel stays resident for the life of the container, so every eagerly imported module becomes steady-state memory.
9 +- The Desktop/Xpra service itself was not the largest outlier; the always-on tunnel process was.
10 +
11 +## Future Work
12 +
13 +- Split tunnel startup into a small import surface that only loads routing, auth, and socket plumbing required for health and proxy operation.
14 +- Lazy-import provider/framework modules only when a tunnel request truly needs them.
15 +- Review any `ApiHandler` or helper imports used by the tunnel path and replace broad framework imports with narrower functions.
16 +- Measure with `smem -P run_tunnel.py`, `/proc/<pid>/smaps_rollup`, and before/after cold-start RSS/PSS on ARM64.
17 +
18 +## Success Signal
19 +
20 +The tunnel process should remain useful as an always-on edge while dropping its idle PSS substantially below the main UI process. A good first target is under 250 MiB PSS on ARM64 without changing tunnel behavior.
helpers/virtual_desktop.py
+1 -1
@@ -131,7 +131,7 @@ def session_url(token: str, *, title: str = "Desktop") -> str:
131 "printing": "true",
132 "file_transfer": "true",
133 "sound": "false",
134 - "offscreen": "false",
134 + "offscreen": "true",
135 "floating_menu": "false",
136 "xpramenu": "false",
137 },
plugins/_office/extensions/python/startup_migration/_20_office_routes.py
+1 -4
@@ -3,7 +3,7 @@ from __future__ import annotations
3 from helpers.extension import Extension
4 from helpers.print_style import PrintStyle
5 from plugins._office import hooks
6 -from plugins._office.helpers import libreoffice_desktop, libreoffice_desktop_routes
6 +from plugins._office.helpers import libreoffice_desktop_routes
7
8
9 class OfficeStartupCleanup(Extension):
@@ -14,6 +14,3 @@ class OfficeStartupCleanup(Extension):
14 PrintStyle.warning("Office runtime preparation reported errors:", result["errors"])
15 elif result.get("installed") or result.get("removed"):
16 PrintStyle.info("Office runtime prepared:", result)
17 - desktop = libreoffice_desktop.get_manager().ensure_system_desktop()
18 - if not desktop.get("available"):
19 - PrintStyle.warning("Office desktop startup was deferred:", desktop.get("error") or desktop)
plugins/_office/extensions/webui/right-canvas-panels/office-panel.html
+1
@@ -2,6 +2,7 @@
2 class="right-canvas-surface-panel office-canvas-surface"
3 data-surface-id="office"
4 x-show="$store.rightCanvas && $store.rightCanvas.isOpen && $store.rightCanvas.activeSurfaceId === 'office'"
5 + x-effect="$store.office?.setDesktopHostVisible?.($store.rightCanvas && $store.rightCanvas.isOpen && $store.rightCanvas.activeSurfaceId === 'office')"
6 style="display: none;"
7 >
8 <x-component path="/plugins/_office/webui/office-panel.html"></x-component>
plugins/_office/helpers/libreoffice_desktop.py
+5 -3
@@ -271,21 +271,23 @@ class LibreOfficeDesktopManager:
271 session = self.get(session_id)
272 if not session:
273 return {"ok": False, "error": "LibreOffice desktop session not found."}
274 + is_system_desktop = session.session_id == SYSTEM_SESSION_ID and session.extension == "desktop"
275 result = virtual_desktop.resize_display(
276 display=session.display,
277 width=width,
278 height=height,
279 max_width=MAX_SCREEN_WIDTH,
280 max_height=MAX_SCREEN_HEIGHT,
280 - window_class="libreoffice",
281 - keys=("Escape",),
281 + window_class="" if is_system_desktop else "libreoffice",
282 + keys=() if is_system_desktop else ("Escape",),
283 xauthority=self._xauthority(session),
284 home=str(session.profile_dir),
285 )
286 if result.get("ok"):
287 session.width = int(result["width"])
288 session.height = int(result["height"])
288 - self._dismiss_blocking_dialogs(session)
289 + if not is_system_desktop:
290 + self._dismiss_blocking_dialogs(session)
291 return result
292
293 def proxy_for_token(self, token: str) -> tuple[str, int] | None:
plugins/_office/webui/office-store.js
+117 -27
@@ -12,6 +12,7 @@ const SAVE_MESSAGE_MS = 1800;
12 const INPUT_PUSH_DELAY_MS = 650;
13 const DESKTOP_HEARTBEAT_MS = 3500;
14 const DESKTOP_RESIZE_DELAY_MS = 80;
15 +const DESKTOP_START_MESSAGE = "Starting Agent Zero Desktop environment";
16 const XPRA_DESKTOP_PRIME_INTERVAL_MS = 220;
17 const XPRA_DESKTOP_PRIME_ATTEMPTS = 120;
18 const SYSTEM_DESKTOP_FILE_ID = "system-desktop";
@@ -219,8 +220,11 @@ const model = {
220 _desktopResizeTarget: null,
221 _desktopResizeTimer: null,
222 _desktopResizeKey: "",
223 + _desktopResizePendingKey: "",
224 _desktopResizeSuspended: false,
225 _desktopResizePending: false,
226 + _desktopViewportSyncTimers: [],
227 + _desktopHostVisible: false,
228 _desktopPrimeTimer: null,
229 _desktopPrimeAttempts: 0,
230 _desktopKeyboardActive: false,
@@ -237,10 +241,12 @@ const model = {
241 async onMount(element = null, options = {}) {
242 if (element) this._root = element;
243 this._mode = options?.mode === "modal" ? "modal" : "canvas";
240 - if (this._mode === "modal") this.setupFloatingModal(element);
241 - await this.refresh();
242 - await this.ensureDesktopSession({ select: !this.session });
243 - this.ensureActiveTab();
244 + if (this._mode === "modal") {
245 + this._desktopHostVisible = true;
246 + this.setupFloatingModal(element);
247 + await this.onOpen({ source: "modal" });
248 + return;
249 + }
250 this.queueRender();
251 },
252
@@ -259,7 +265,9 @@ const model = {
265 },
266
267 beforeHostHidden(options = {}) {
268 + this._desktopHostVisible = false;
269 this.flushInput();
270 + this.clearDesktopViewportSyncTimers();
271 this.unloadDesktopFrames();
272 },
273
@@ -267,6 +275,7 @@ const model = {
275 this.flushInput();
276 this.stopDesktopMonitor();
277 this.stopDesktopResizeObserver();
278 + this.clearDesktopViewportSyncTimers();
279 this.stopXpraDesktopPrime();
280 this.stopDesktopKeyboardBridge();
281 this.stopDesktopClipboardBridge();
@@ -292,10 +301,23 @@ const model = {
301 this.updateDesktopMonitor();
302 return existing;
303 }
295 - if (this._desktopStarting) return await this._desktopStarting;
304 + const showProgress = options.progress !== false;
305 + const progressMessage = String(options.message || DESKTOP_START_MESSAGE);
306 + if (this._desktopStarting) {
307 + if (showProgress) {
308 + this.loading = true;
309 + this.message = progressMessage;
310 + }
311 + return await this._desktopStarting;
312 + }
313
314 this._desktopStarting = (async () => {
315 try {
316 + if (showProgress) {
317 + this.loading = true;
318 + this.message = progressMessage;
319 + this.error = "";
320 + }
321 const response = await callOffice("desktop");
322 if (response?.ok === false) throw new Error(response.error || "Desktop session could not be opened.");
323 const session = normalizeSession(response);
@@ -327,6 +349,10 @@ const model = {
349 this.error = error instanceof Error ? error.message : String(error);
350 return null;
351 } finally {
352 + if (showProgress) {
353 + this.loading = false;
354 + if (this.message === progressMessage) this.message = "";
355 + }
356 this._desktopStarting = null;
357 }
358 })();
@@ -493,7 +519,7 @@ const model = {
519 this.ensureActiveTab();
520 }
521 this.updateDesktopMonitor();
496 - await this.ensureDesktopSession({ select: !this.session });
522 + this.ensureActiveTab();
523 await this.refresh();
524 },
525
@@ -836,7 +862,36 @@ const model = {
862 },
863
864 officialOfficeUrl(tab = this.session) {
839 - return tab?.desktop?.url || "";
865 + const url = tab?.desktop?.url || "";
866 + if (!url) return "";
867 + try {
868 + const parsed = new URL(url, window.location.href);
869 + const secureContext = globalThis.isSecureContext === true;
870 + parsed.searchParams.set("offscreen", secureContext ? "true" : "false");
871 + parsed.searchParams.set("clipboard_poll", secureContext ? "true" : "false");
872 + if (parsed.origin === window.location.origin) return `${parsed.pathname}${parsed.search}${parsed.hash}`;
873 + return parsed.href;
874 + } catch {
875 + return url;
876 + }
877 + },
878 +
879 + isDesktopHostVisible() {
880 + if (this._mode === "modal") return true;
881 + const canvas = globalThis.Alpine?.store?.("rightCanvas") || rightCanvasStore;
882 + return Boolean(canvas?.isOpen && canvas.activeSurfaceId === "office");
883 + },
884 +
885 + setDesktopHostVisible(visible) {
886 + const next = Boolean(visible);
887 + if (!next && this._mode === "modal") return;
888 + if (this._desktopHostVisible === next) return;
889 + this._desktopHostVisible = next;
890 + if (next) {
891 + this.afterDesktopHostShown({ source: "canvas-visibility" });
892 + } else {
893 + this.beforeHostHidden({ reason: "hidden" });
894 + }
895 },
896
897 desktopFrames() {
@@ -879,6 +934,7 @@ const model = {
934 },
935
936 restoreDesktopFrames() {
937 + if (!this.isDesktopHostVisible()) return;
938 const url = this.officialOfficeUrl();
939 if (!url) return;
940 for (const frame of this.desktopFrames()) {
@@ -892,22 +948,21 @@ const model = {
948
949 afterDesktopHostShown() {
950 if (!this.hasOfficialOffice()) return;
951 + this._desktopHostVisible = true;
952 this._desktopResizeKey = "";
953 + this._desktopResizePendingKey = "";
954 this._desktopResizeSuspended = false;
955 this._desktopResizePending = false;
956 this.restoreDesktopFrames();
957 this.requestDesktopViewportSync({ force: true, frame: this.desktopFrame() });
900 - for (const delay of [720, 1280]) {
901 - globalThis.setTimeout(() => {
902 - this.requestDesktopViewportSync({ force: true, frame: this.desktopFrame() });
903 - }, delay);
904 - }
958 },
959
960 beforeDesktopHostHandoff() {
961 this.stopDesktopResizeObserver();
962 + this.clearDesktopViewportSyncTimers();
963 this.stopXpraDesktopPrime();
964 this._desktopResizeKey = "";
965 + this._desktopResizePendingKey = "";
966 this._desktopResizeSuspended = true;
967 this._desktopResizePending = true;
968 },
@@ -920,6 +975,7 @@ const model = {
975
976 onDesktopFrameLoaded(event = null) {
977 if (event?.target?.getAttribute?.("src") === "about:blank") return;
978 + if (!this.isDesktopHostVisible()) return;
979 this.error = "";
980 this.queueDesktopFrameFocus(event?.target || null);
981 this.requestDesktopViewportSync({ force: true, frame: event?.target || null });
@@ -955,7 +1011,7 @@ const model = {
1011 },
1012
1013 updateDesktopMonitor() {
958 - if (!this.hasOfficialOffice()) {
1014 + if (!this.hasOfficialOffice() || !this.isDesktopHostVisible()) {
1015 this.stopDesktopMonitor();
1016 this.stopDesktopResizeObserver();
1017 this._desktopKeyboardActive = false;
@@ -975,7 +1031,7 @@ const model = {
1031 },
1032
1033 startDesktopResizeObserver() {
978 - if (!this.hasOfficialOffice()) {
1034 + if (!this.hasOfficialOffice() || !this.isDesktopHostVisible()) {
1035 this.stopDesktopResizeObserver();
1036 return;
1037 }
@@ -1017,6 +1073,7 @@ const model = {
1073 this._desktopResizeCleanup = null;
1074 this._desktopResizeTarget = null;
1075 this._desktopResizeKey = "";
1076 + this._desktopResizePendingKey = "";
1077 this._desktopResizeSuspended = false;
1078 this._desktopResizePending = false;
1079 },
@@ -1027,6 +1084,7 @@ const model = {
1084 globalThis.clearTimeout(this._desktopResizeTimer);
1085 this._desktopResizeTimer = null;
1086 }
1087 + this._desktopResizePendingKey = "";
1088 },
1089
1090 resumeDesktopResize() {
@@ -1046,7 +1104,15 @@ const model = {
1104 );
1105 },
1106
1107 + clearDesktopViewportSyncTimers() {
1108 + for (const timer of this._desktopViewportSyncTimers.splice(0)) {
1109 + globalThis.clearTimeout(timer);
1110 + }
1111 + },
1112 +
1113 requestDesktopViewportSync(options = {}) {
1114 + if (!this.hasOfficialOffice() || !this.isDesktopHostVisible()) return;
1115 + if (options.force) this.clearDesktopViewportSyncTimers();
1116 const run = (force = false) => {
1117 this.syncDesktopViewport({ ...options, force });
1118 };
@@ -1055,13 +1121,16 @@ const model = {
1121 } else {
1122 globalThis.setTimeout(() => run(Boolean(options.force)), 0);
1123 }
1058 - for (const delay of [140, 420]) {
1059 - globalThis.setTimeout(() => run(false), delay);
1060 - }
1124 + if (options.followup === false) return;
1125 + const timer = globalThis.setTimeout(() => {
1126 + this._desktopViewportSyncTimers = this._desktopViewportSyncTimers.filter((item) => item !== timer);
1127 + run(false);
1128 + }, options.force ? 260 : 180);
1129 + this._desktopViewportSyncTimers.push(timer);
1130 },
1131
1132 syncDesktopViewport(options = {}) {
1064 - if (!this.hasOfficialOffice()) return false;
1133 + if (!this.hasOfficialOffice() || !this.isDesktopHostVisible()) return false;
1134 const frame = this.desktopFrame(options.frame || null);
1135 if (!frame) return false;
1136 this.startDesktopResizeObserver();
@@ -1554,7 +1623,7 @@ const model = {
1623 },
1624
1625 queueDesktopResize(options = {}) {
1557 - if (!this.hasOfficialOffice()) return;
1626 + if (!this.hasOfficialOffice() || !this.isDesktopHostVisible()) return;
1627 const token = this.session?.desktop?.token || "";
1628 const frame = this.desktopFrame(options.frame || null);
1629 const target = frame?.parentElement || frame;
@@ -1565,18 +1634,33 @@ const model = {
1634 const width = Math.round(rect.width);
1635 const height = Math.round(rect.height);
1636 if (width < 320 || height < 220) return;
1568 - this.applyXpraDesktopFrameMode(frame, { requestServerResize: false, requestRefresh: false });
1637 + const key = `${token}:${width}x${height}`;
1638 + const refreshFrameOnly = () => {
1639 + this.applyXpraDesktopFrameMode(frame, { requestServerResize: false, requestRefresh: false });
1640 + };
1641 + if (!serverResize) {
1642 + refreshFrameOnly();
1643 + return;
1644 + }
1645 + if (key === this._desktopResizeKey || key === this._desktopResizePendingKey) {
1646 + refreshFrameOnly();
1647 + return;
1648 + }
1649 + refreshFrameOnly();
1650 if (!force && this.shouldDeferDesktopResize()) {
1651 this._desktopResizePending = true;
1652 return;
1653 }
1573 - const key = `${token}:${width}x${height}`;
1574 - if (!serverResize) return;
1575 - if (!force && key === this._desktopResizeKey) return;
1654 if (this._desktopResizeTimer) globalThis.clearTimeout(this._desktopResizeTimer);
1655 + this._desktopResizePendingKey = key;
1656 this._desktopResizeTimer = globalThis.setTimeout(async () => {
1657 this._desktopResizeTimer = null;
1658 + if (!this.hasOfficialOffice() || !this.isDesktopHostVisible()) {
1659 + if (this._desktopResizePendingKey === key) this._desktopResizePendingKey = "";
1660 + return;
1661 + }
1662 if (!force && this.shouldDeferDesktopResize()) {
1663 + if (this._desktopResizePendingKey === key) this._desktopResizePendingKey = "";
1664 this._desktopResizePending = true;
1665 return;
1666 }
@@ -1603,6 +1687,8 @@ const model = {
1687 }
1688 } catch (error) {
1689 console.warn("Desktop resize skipped", error);
1690 + } finally {
1691 + if (this._desktopResizePendingKey === key) this._desktopResizePendingKey = "";
1692 }
1693 }, DESKTOP_RESIZE_DELAY_MS);
1694 },
@@ -1711,7 +1797,7 @@ const model = {
1797
1798 startDesktopMonitor() {
1799 this.stopDesktopMonitor();
1714 - if (!this.hasOfficialOffice()) return;
1800 + if (!this.hasOfficialOffice() || !this.isDesktopHostVisible()) return;
1801 const tabId = this.session?.tab_id || "";
1802 const sessionId = this.session?.desktop_session_id || this.session?.session_id || "";
1803 if (!tabId || !sessionId) return;
@@ -1720,7 +1806,7 @@ const model = {
1806 this._desktopHeartbeatMisses = 0;
1807
1808 const tick = async () => {
1723 - if (!this.session || this.session.tab_id !== tabId || !this.hasOfficialOffice()) return;
1809 + if (!this.session || this.session.tab_id !== tabId || !this.hasOfficialOffice() || !this.isDesktopHostVisible()) return;
1810 try {
1811 const response = await callOffice("desktop_sync", {
1812 desktop_session_id: sessionId,
@@ -1773,8 +1859,12 @@ const model = {
1859 this.stopDesktopMonitor();
1860 this.stopDesktopResizeObserver();
1861 this.stopXpraDesktopPrime();
1776 - this.setMessage("Desktop is restarting");
1777 - await this.ensureDesktopSession({ force: true, select: this.activeTabId === tabId || Boolean(hiddenDesktopDocument) });
1862 + this.message = "Desktop is restarting";
1863 + await this.ensureDesktopSession({
1864 + force: true,
1865 + select: this.activeTabId === tabId || Boolean(hiddenDesktopDocument),
1866 + message: "Desktop is restarting",
1867 + });
1868 target._desktopClosed = false;
1869 await this.refresh();
1870 },
tests/test_office_canvas_setup.py
+14 -1
@@ -13,6 +13,9 @@ def test_document_canvas_uses_markdown_editor_and_official_libreoffice_desktop_f
13 store = (PROJECT_ROOT / "plugins" / "_office" / "webui" / "office-store.js").read_text(
14 encoding="utf-8",
15 )
16 + canvas_panel = (
17 + PROJECT_ROOT / "plugins" / "_office" / "extensions" / "webui" / "right-canvas-panels" / "office-panel.html"
18 + ).read_text(encoding="utf-8")
19
20 assert "office-source-editor" in panel
21 assert "data-office-source" in panel
@@ -47,10 +50,18 @@ def test_document_canvas_uses_markdown_editor_and_official_libreoffice_desktop_f
50 assert "zoom: 1" not in store
51 assert 'callOffice("desktop")' in store
52 assert "ensureDesktopSession" in store
53 + assert 'await this.onOpen({ source: "modal" });' in store
54 + assert "setDesktopHostVisible" in store
55 + assert "isDesktopHostVisible" in store
56 + assert "clearDesktopViewportSyncTimers" in store
57 + assert "setDesktopHostVisible" in canvas_panel
58 + assert "Starting Agent Zero Desktop environment" in store
59 assert "handleOfficialOfficeClosed" in store
60 assert "ResizeObserver" in store
61 assert "_desktopResizeSuspended" in store
62 assert "_desktopResizePending" in store
63 + assert "_desktopResizePendingKey" in store
64 + assert "_desktopViewportSyncTimers" in store
65 assert "shouldDeferDesktopResize" in store
66 assert "right-canvas-resize-start" in store
67 assert "right-canvas-resize-end" in store
@@ -91,6 +102,8 @@ def test_document_canvas_uses_markdown_editor_and_official_libreoffice_desktop_f
102 assert "_desktopHeartbeatTimer" in store
103 assert "office-modal-focus-button" in store
104 assert "officialOfficeUrl" in store
105 + assert 'parsed.searchParams.set("offscreen", secureContext ? "true" : "false")' in store
106 + assert 'parsed.searchParams.set("clipboard_poll", secureContext ? "true" : "false")' in store
107 assert "hasOfficialOffice" in store
108 assert "isOfficeSocketData" in store
109 assert "office_command" not in store
@@ -233,7 +246,7 @@ def test_official_libreoffice_desktop_route_and_packages_are_declared():
246 assert '"quality": "85"' in primitive
247 assert '"speed": "80"' in primitive
248 assert '"printing": "true"' in primitive
236 - assert "offscreen" in primitive
249 + assert '"offscreen": "true"' in primitive
250 assert "xpra" in desktop
251 assert "xpra-html5" in desktop
252 assert "Xvfb" in desktop
tests/test_office_document_store.py
+3 -12
@@ -684,7 +684,7 @@ def test_cleanup_hook_removes_stale_runtime_state_idempotently(tmp_path, monkeyp
684 assert marker.exists()
685
686
687 -def test_office_startup_bootstraps_persistent_desktop_runtime(monkeypatch):
687 +def test_office_startup_defers_persistent_desktop_runtime(monkeypatch):
688 calls = []
689 routes_module = types.ModuleType("plugins._office.helpers.libreoffice_desktop_routes")
690 routes_module.install_route_hooks = lambda: calls.append("routes")
@@ -697,25 +697,16 @@ def test_office_startup_bootstraps_persistent_desktop_runtime(monkeypatch):
697
698 from plugins._office.extensions.python.startup_migration import _20_office_routes as office_startup
699
700 - class Manager:
701 - def ensure_system_desktop(self):
702 - calls.append("desktop")
703 - return {"available": True, "session_id": "agent-zero-desktop"}
704 -
700 monkeypatch.setattr(
701 office_startup.hooks,
702 "cleanup_stale_runtime_state",
703 lambda: {"ok": True, "errors": [], "installed": [], "removed": []},
704 )
710 - monkeypatch.setattr(
711 - office_startup.libreoffice_desktop,
712 - "get_manager",
713 - lambda: Manager(),
714 - )
705
706 office_startup.OfficeStartupCleanup(agent=None).execute()
707
718 - assert calls == ["routes", "desktop"]
708 + assert calls == ["routes"]
709 + assert not hasattr(office_startup, "libreoffice_desktop")
710
711
712 def test_cleanup_hook_reruns_when_stale_packages_exist_after_old_marker(tmp_path, monkeypatch):