From 49e7c390f3474367983b6350e8ab43f74f3caf8f Mon Sep 17 00:00:00 2001 From: Federico Jaramillo Martinez Date: Sun, 26 Jul 2026 16:19:05 +0200 Subject: [PATCH] feat(sessions): tell the user what a session start is waiting on Creating or opening a session could stall for reasons the daemon knew about and never shared. The browser invented the whole message it showed while waiting -- "Creating session: Waiting for the backend session to be ready" -- which says that we are waiting but never what for. A shared ModelRuntime read during startup can be handed a network refresh that is already in flight, and extensions may do their own network I/O while loading, so the wait is real and previously unattributable. The pre-session gap turned out to be a missing shared key rather than a missing channel: publishActivity needs the PiAgentSession being built, but the session id and cwd are both known before the first await. So create() now publishes a new global session.startup event carrying an ordinary SessionActivity, routed by cwd -- the one identity a browser row waiting for a session id can match, since the client-invented pending id is unknown to the daemon and the daemon's id is unknown to the browser. Two phases are reported, each published before the await it describes so the label changes during the wait rather than after it: "Starting the Pi session" and "Loading session extensions". Both are facts, because the service awaits exactly one call for each. A concurrent background catalog refresh is appended as a note ("provider model lists are refreshing"), never as the cause: the refresher can prove a refresh is running but not that this startup joined it. ModelCatalogRefresher gains only a read-only isRefreshInFlight() getter; cadence, timeout, and coalescing are untouched. Reporting is event-only and synchronous. It writes no activities entry, no workspace activity, and no unread state, so a failed creation leaves nothing stranded, no await is added, and creation ordering and semantics are unchanged. The window-ending idle report is skipped when a real activity was published during startup, so an extension error survives. The browser applies startup progress only when it can prove the target: one non-discarded pending start in that cwd on the selected machine, or a session whose id it already knows. A foreign workspace, another machine, or two concurrent starts in one workspace keep today's generic wording rather than showing one row the phase of another. An idle report restores that generic wording, including the queued-messages variant. docs/config.md said nothing a request triggers waits on a catalog fetch. That is not strictly true for a refresh already in flight, so both it and the generated docs/config.html now state the exception and say PI WEB reports it while it happens. --- .../session-start-progress-visibility.md | 5 + docs/config.html | 13 +- docs/config.md | 4 +- src/client/src/api/parsers.test.ts | 32 ++- src/client/src/api/parsers.ts | 35 ++- .../sessionController.startupProgress.test.ts | 163 ++++++++++++++ .../src/controllers/sessionController.ts | 33 ++- src/client/src/sessionSocket.test.ts | 13 ++ src/client/src/sessionSocket.ts | 3 +- src/server/sessiond.ts | 3 + .../sessions/modelCatalogRefresher.test.ts | 29 +++ src/server/sessions/modelCatalogRefresher.ts | 9 + .../piSessionService.startupProgress.test.ts | 208 ++++++++++++++++++ src/server/sessions/piSessionService.ts | 113 ++++++++++ src/shared/apiTypes.ts | 23 +- 15 files changed, 677 insertions(+), 9 deletions(-) create mode 100644 .changeset/session-start-progress-visibility.md create mode 100644 src/client/src/controllers/sessionController.startupProgress.test.ts create mode 100644 src/server/sessions/piSessionService.startupProgress.test.ts diff --git a/.changeset/session-start-progress-visibility.md b/.changeset/session-start-progress-visibility.md new file mode 100644 index 0000000..1f4afcc --- /dev/null +++ b/.changeset/session-start-progress-visibility.md @@ -0,0 +1,5 @@ +--- +"@jmfederico/pi-web": patch +--- + +Say what a slow session start is waiting on. While a session is being created or opened, the activity line now names the current startup step — starting the Pi session, or loading session extensions — and adds a note when provider model lists happen to be refreshing at the same time. When nothing can be attributed, the previous generic wording is kept rather than guessing a cause. diff --git a/docs/config.html b/docs/config.html index de15458..cb53e8a 100644 --- a/docs/config.html +++ b/docs/config.html @@ -699,9 +699,16 @@

Background model catalog refresh

PI WEB shares one model runtime across all sessions, and provider model catalogs are refreshed over the - network only on the session daemon's own background schedule. Nothing a browser or API request triggers - waits on a provider catalog fetch, so a slow or unreachable provider cannot stall opening the model - selector, starting a session, or the auth dialogs. + network only on the session daemon's own background schedule. Requests never start a catalog fetch of + their own, so a slow or unreachable provider cannot stall opening the model selector, starting a session, + or the auth dialogs on its own account. +

+

+ A refresh that is already in flight can still briefly delay starting or opening a session, + because the shared runtime is read while that refresh is running. PI WEB says so while you wait: the + session's activity line names the startup step it is on and adds + provider model lists are refreshing when a background refresh is running at the same time. + That note reports what is happening concurrently, not a proven cause.

The session daemon runs the refresh: