| 3696 | } |
| 3697 | |
| 3698 | addWorkspace(metadata: FrontendWorkspaceMetadata): void { |
| 3699 | const workspaceId = metadata.id; |
| 3700 | |
| 3701 | // Skip if already registered |
| 3702 | if (this.workspaceMetadata.has(workspaceId)) { |
| 3703 | return; |
| 3704 | } |
| 3705 | |
| 3706 | // Store metadata for name lookup |
| 3707 | this.workspaceMetadata.set(workspaceId, metadata); |
| 3708 | this.derived.bump("workspaces"); |
| 3709 | |
| 3710 | // Backend guarantees createdAt via config.ts - this should never be undefined |
| 3711 | assert( |
| 3712 | metadata.createdAt, |
| 3713 | `Workspace ${workspaceId} missing createdAt - backend contract violated` |
| 3714 | ); |
| 3715 | |
| 3716 | const aggregator = this.getOrCreateAggregator( |
| 3717 | workspaceId, |
| 3718 | metadata.createdAt, |
| 3719 | metadata.unarchivedAt |
| 3720 | ); |
| 3721 | |
| 3722 | // Initialize recency cache and bump derived store immediately |
| 3723 | // This ensures UI sees correct workspace order before messages load |
| 3724 | const initialRecency = aggregator.getRecencyTimestamp(); |
| 3725 | if (initialRecency !== null) { |
| 3726 | this.recencyCache.set(workspaceId, initialRecency); |
| 3727 | this.derived.bump("recency"); |
| 3728 | } |
| 3729 | |
| 3730 | // Initialize transient chat state |
| 3731 | if (!this.chatTransientState.has(workspaceId)) { |
| 3732 | this.chatTransientState.set(workspaceId, createInitialChatTransientState()); |
| 3733 | } |
| 3734 | |
| 3735 | if (!this.historyPagination.has(workspaceId)) { |
| 3736 | this.historyPagination.set(workspaceId, createInitialHistoryPaginationState()); |
| 3737 | } |
| 3738 | |
| 3739 | // Clear stale streaming state |
| 3740 | aggregator.clearActiveStreams(); |
| 3741 | |
| 3742 | // Fetch persisted session usage (fire-and-forget) |
| 3743 | this.refreshSessionUsage(workspaceId); |
| 3744 | |
| 3745 | // Stats snapshots are subscribed lazily via subscribeStats(). |
| 3746 | this.subscribeToStats(workspaceId); |
| 3747 | |
| 3748 | this.ensureActiveOnChatSubscription(); |
| 3749 | |
| 3750 | if (!this.client) { |
| 3751 | console.warn(`[WorkspaceStore] No ORPC client available for workspace ${workspaceId}`); |
| 3752 | } |
| 3753 | } |
| 3754 | |
| 3755 | markPendingInitialSend(workspaceId: string, pendingStreamModel: string | null): void { |