perf(sessions): archive a worktree's sessions through one server batch

Removing a worktree archived its sessions one SDK call at a time and
then re-rendered the whole sidebar once per streamed session.updated
echo. On a worktree with 121 sessions that meant 14.8s of main-thread
work, 121 requests, and 328 localStorage writes.

- Add POST /api/openchamber/sessions/archive: validates a batch (max
  500 ids, per-request archivedAt), archives sequentially, and reports
  partial failures instead of dropping the batch. VS Code serves no
  such route and answers 501; the shared UI then falls back to the
  per-session path.
- Plan batches from the sessions this client actually holds, live
  directory stores first, so worktree-only sessions still batch.
- Claim (id, archivedAt) pairs before the request and consume the
  matching session.updated echoes, so the server's own confirmations
  no longer fan out into 121 store publications. Runtime-scoped, TTL
  30s, released on response or fallback; non-matching updates pass.
- Make the managed-chats persistence a real trailing debounce instead
  of a 50ms throttle, so a burst of publications coalesces into one
  localStorage write.

Benchmark (121 sessions, production build, real Chrome): 14785ms ->
~1030ms, long tasks 100 -> 1, global store publications 236 -> 1,
persistence writes 328 -> 3.
This commit is contained in:
Iuliia Ivashko
2026-09-04 16:50:03 +03:00
parent 0d8709a72e
commit 0ef189f3c8
11 changed files with 769 additions and 15 deletions
+8
View File
@@ -384,6 +384,14 @@ const handleLocalApiRequest = async (input: RequestInfo | URL, url: URL, init: R
return unsupportedWebRouteResponse('Remote tunnel settings');
}
// Archiving a batch of sessions server-side needs an OpenChamber server
// process; the extension host has none. Answering explicitly keeps the
// shared UI on its per-session archive path instead of leaving the request
// to the generic proxy.
if (normalizedPathname === '/api/openchamber/sessions/archive') {
return unsupportedWebRouteResponse('Server-side session archiving');
}
if (/^\/api\/projects\/[^/]+\/scheduled-tasks(?:\/[^/]+)?$/.test(normalizedPathname)) {
return unsupportedWebRouteResponse('Scheduled tasks');
}