Summary
Pinned tabs and session bucket markers do not sync across machines, but the active/current session does. Ideally this should be the other way around: pins and bucket markers should be shared across devices, while the active session should be per-device.
Why it currently behaves this way
Active session → server-side (shared). Whether a session is current is computed from the server's single session.sessionId:
// server.ts
isCurrent: cwd === piCwd && info.id === session.sessionId,
All clients talk to the same server process, so they all see the same active session (and get WebSocket updates). Single source of truth on the backend.
Pinned tabs + markers → browser localStorage (per-device). These live only in localStorage (src/app/types.ts):
const pinnedSessionsKey = "pi-web-pinned-sessions";
const sessionMarkersKey = "pi-web-session-markers";
localStorage is scoped per browser/device and never sent to the server, so each machine keeps its own independent pins/markers.
| State |
Stored where |
Synced? |
| Active session |
Server (session.sessionId) |
✅ shared |
| Pinned tabs |
Browser localStorage |
❌ per-device |
| Bucket markers |
Browser localStorage |
❌ per-device |
Proposed work
1. Sync pins + markers to the server (easy, self-contained)
- Add endpoints (e.g.
GET/POST /api/ui-state) backed by a small server-side JSON store.
- Replace the
localStorage calls in src/app/types.ts (persistPinnedSessions, persistSessionMarkers, and readers) with fetches to those endpoints.
- Optionally broadcast changes over the existing WebSocket for live updates (otherwise syncs on refresh).
2. Make active session per-device (hard, core refactor)
The server is built around a single global session object — streaming, conversation tree, stats, WebSocket events, and sessions/open all flow through it. Supporting different active sessions per client would require managing multiple live sessions keyed per WebSocket connection and routing every event/stream to the right client. Structural change to server.ts.
Recommendation
Ship part 1 first (high value, low risk). Treat part 2 as a separate, larger effort and only pursue it if running two machines side-by-side against the same server simultaneously is a real need.
Summary
Pinned tabs and session bucket markers do not sync across machines, but the active/current session does. Ideally this should be the other way around: pins and bucket markers should be shared across devices, while the active session should be per-device.
Why it currently behaves this way
Active session → server-side (shared). Whether a session is current is computed from the server's single
session.sessionId:All clients talk to the same server process, so they all see the same active session (and get WebSocket updates). Single source of truth on the backend.
Pinned tabs + markers → browser localStorage (per-device). These live only in
localStorage(src/app/types.ts):localStorageis scoped per browser/device and never sent to the server, so each machine keeps its own independent pins/markers.session.sessionId)localStoragelocalStorageProposed work
1. Sync pins + markers to the server (easy, self-contained)
GET/POST /api/ui-state) backed by a small server-side JSON store.localStoragecalls insrc/app/types.ts(persistPinnedSessions,persistSessionMarkers, and readers) with fetches to those endpoints.2. Make active session per-device (hard, core refactor)
The server is built around a single global
sessionobject — streaming, conversation tree, stats, WebSocket events, andsessions/openall flow through it. Supporting different active sessions per client would require managing multiple live sessions keyed per WebSocket connection and routing every event/stream to the right client. Structural change toserver.ts.Recommendation
Ship part 1 first (high value, low risk). Treat part 2 as a separate, larger effort and only pursue it if running two machines side-by-side against the same server simultaneously is a real need.