Files
kandev/apps/web/lib/state/store.ts
T
João Salavisa e9a7869463 refactor(web): drop dead taskPRs loading state and unused removeTaskPR (#791)
* refactor(web): drop dead taskPRs loading state and unused removeTaskPR

The taskPRs.loaded/loading fields were written but never read — useWorkspacePRs
uses its own fetchedRef for fetch-once gating instead. removeTaskPR was defined
but never called. Removing all three avoids a footgun in multi-repo tasks where
removeTaskPR would have wiped every repo's PR state at once.

* refactor(web): use plain deepMerge for taskPRs hydration

taskPRs no longer carries loading/loaded fields, so the mergeWithLoading helper has nothing to preserve. Switching to deepMerge makes the intent explicit.

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-05-03 12:00:22 +01:00

585 lines
24 KiB
TypeScript

import { createStore } from "zustand/vanilla";
import { immer } from "zustand/middleware/immer";
import { hydrateState, type HydrationOptions } from "./hydration/hydrator";
import type {
Repository,
Branch,
RepositoryScript,
Message,
Turn,
TaskSession,
} from "@/lib/types/http";
import type {
GitHubStatus,
TaskPR,
PRWatch,
ReviewWatch as GitHubReviewWatch,
IssueWatch as GitHubIssueWatch,
GitHubActionPresets,
} from "@/lib/types/github";
import type { SystemHealthResponse } from "@/lib/types/health";
import type { UISliceActions as UIA } from "./slices/ui/types";
import { mergeInitialState } from "./default-state";
import {
createKanbanSlice,
createWorkspaceSlice,
createSettingsSlice,
createSessionSlice,
createSessionRuntimeSlice,
createUISlice,
createGitHubSlice,
createJiraSlice,
defaultKanbanState,
defaultWorkspaceState,
defaultSettingsState,
defaultSessionState,
defaultSessionRuntimeState,
defaultUIState,
defaultGitHubState,
defaultJiraState,
type WorkspaceState,
type WorkflowsState,
type ExecutorsState,
type SettingsAgentsState,
type AgentDiscoveryState,
type AvailableAgentsState,
type AgentProfilesState,
type EditorsState,
type PromptsState,
type SecretsState,
type NotificationProvidersState,
type SettingsDataState,
type UserSettingsState,
type ProcessStatusEntry,
type Worktree,
type GitStatusEntry,
type SessionCommit,
type ContextWindowEntry,
type SessionAgentctlStatus,
type PreviewStage,
type PreviewViewMode,
type PreviewDevicePreset,
type ConnectionState,
} from "./slices";
import type {
AvailableCommand,
SessionModeEntry,
AgentCapabilitiesEntry,
SessionModelEntry,
ConfigOptionEntry,
PromptUsageEntry,
SessionPollMode,
TodoEntry,
UserShellInfo,
} from "./slices/session-runtime/types";
// Re-export all types from slices for backwards compatibility
export type {
KanbanState,
KanbanMultiState,
WorkflowSnapshotData,
WorkflowsState,
TaskState,
WorkspaceState,
RepositoriesState,
RepositoryBranchesState,
RepositoryScriptsState,
ExecutorsState,
SettingsAgentsState,
AgentDiscoveryState,
AvailableAgentsState,
AgentProfileOption,
AgentProfilesState,
EditorsState,
PromptsState,
SecretsState,
NotificationProvidersState,
SettingsDataState,
UserSettingsState,
MessagesState,
TurnsState,
TaskSessionsState,
TaskSessionsByTaskState,
SessionAgentctlStatus,
SessionAgentctlState,
Worktree,
WorktreesState,
SessionWorktreesState,
PendingModelState,
ActiveModelState,
TerminalState,
ShellState,
ProcessStatusEntry,
ProcessState,
FileInfo,
GitStatusEntry,
GitStatusState,
ContextWindowEntry,
ContextWindowState,
AgentState,
UserShellInfo,
UserShellsState,
PreviewStage,
PreviewViewMode,
PreviewDevicePreset,
PreviewPanelState,
RightPanelState,
DiffState,
ConnectionState,
MobileKanbanState,
GitHubSlice,
GitHubSliceState,
GitHubSliceActions,
GitHubStatusState,
TaskPRsState,
PRWatchesState,
ReviewWatchesState,
IssueWatchesState,
JiraSlice,
JiraSliceState,
JiraSliceActions,
JiraIssueWatchesState,
} from "./slices";
import type { JiraIssueWatch } from "@/lib/types/jira";
// Combined AppState type
export type AppState = {
// Kanban slice
kanban: (typeof defaultKanbanState)["kanban"];
kanbanMulti: (typeof defaultKanbanState)["kanbanMulti"];
workflows: (typeof defaultKanbanState)["workflows"];
tasks: (typeof defaultKanbanState)["tasks"];
// Workspace slice
workspaces: (typeof defaultWorkspaceState)["workspaces"];
repositories: (typeof defaultWorkspaceState)["repositories"];
repositoryBranches: (typeof defaultWorkspaceState)["repositoryBranches"];
repositoryScripts: (typeof defaultWorkspaceState)["repositoryScripts"];
// Settings slice
executors: (typeof defaultSettingsState)["executors"];
settingsAgents: (typeof defaultSettingsState)["settingsAgents"];
agentDiscovery: (typeof defaultSettingsState)["agentDiscovery"];
availableAgents: (typeof defaultSettingsState)["availableAgents"];
agentProfiles: (typeof defaultSettingsState)["agentProfiles"];
editors: (typeof defaultSettingsState)["editors"];
prompts: (typeof defaultSettingsState)["prompts"];
secrets: (typeof defaultSettingsState)["secrets"];
sprites: (typeof defaultSettingsState)["sprites"];
notificationProviders: (typeof defaultSettingsState)["notificationProviders"];
settingsData: (typeof defaultSettingsState)["settingsData"];
userSettings: (typeof defaultSettingsState)["userSettings"];
// Session slice
messages: (typeof defaultSessionState)["messages"];
turns: (typeof defaultSessionState)["turns"];
taskSessions: (typeof defaultSessionState)["taskSessions"];
taskSessionsByTask: (typeof defaultSessionState)["taskSessionsByTask"];
sessionAgentctl: (typeof defaultSessionState)["sessionAgentctl"];
worktrees: (typeof defaultSessionState)["worktrees"];
sessionWorktreesBySessionId: (typeof defaultSessionState)["sessionWorktreesBySessionId"];
pendingModel: (typeof defaultSessionState)["pendingModel"];
activeModel: (typeof defaultSessionState)["activeModel"];
taskPlans: (typeof defaultSessionState)["taskPlans"];
queue: (typeof defaultSessionState)["queue"];
// Session Runtime slice
terminal: (typeof defaultSessionRuntimeState)["terminal"];
shell: (typeof defaultSessionRuntimeState)["shell"];
processes: (typeof defaultSessionRuntimeState)["processes"];
gitStatus: (typeof defaultSessionRuntimeState)["gitStatus"];
environmentIdBySessionId: (typeof defaultSessionRuntimeState)["environmentIdBySessionId"];
sessionCommits: (typeof defaultSessionRuntimeState)["sessionCommits"];
contextWindow: (typeof defaultSessionRuntimeState)["contextWindow"];
agents: (typeof defaultSessionRuntimeState)["agents"];
availableCommands: (typeof defaultSessionRuntimeState)["availableCommands"];
sessionMode: (typeof defaultSessionRuntimeState)["sessionMode"];
userShells: (typeof defaultSessionRuntimeState)["userShells"];
prepareProgress: (typeof defaultSessionRuntimeState)["prepareProgress"];
sessionTodos: (typeof defaultSessionRuntimeState)["sessionTodos"];
agentCapabilities: (typeof defaultSessionRuntimeState)["agentCapabilities"];
sessionModels: (typeof defaultSessionRuntimeState)["sessionModels"];
promptUsage: (typeof defaultSessionRuntimeState)["promptUsage"];
sessionPollMode: (typeof defaultSessionRuntimeState)["sessionPollMode"];
// GitHub slice
githubStatus: (typeof defaultGitHubState)["githubStatus"];
taskPRs: (typeof defaultGitHubState)["taskPRs"];
prWatches: (typeof defaultGitHubState)["prWatches"];
reviewWatches: (typeof defaultGitHubState)["reviewWatches"];
issueWatches: (typeof defaultGitHubState)["issueWatches"];
actionPresets: (typeof defaultGitHubState)["actionPresets"];
// JIRA slice
jiraIssueWatches: (typeof defaultJiraState)["jiraIssueWatches"];
// UI slice
previewPanel: (typeof defaultUIState)["previewPanel"];
rightPanel: (typeof defaultUIState)["rightPanel"];
diffs: (typeof defaultUIState)["diffs"];
connection: (typeof defaultUIState)["connection"];
mobileKanban: (typeof defaultUIState)["mobileKanban"];
mobileSession: (typeof defaultUIState)["mobileSession"];
chatInput: (typeof defaultUIState)["chatInput"];
documentPanel: (typeof defaultUIState)["documentPanel"];
systemHealth: (typeof defaultUIState)["systemHealth"];
quickChat: (typeof defaultUIState)["quickChat"];
configChat: (typeof defaultUIState)["configChat"];
sessionFailureNotification: (typeof defaultUIState)["sessionFailureNotification"];
bottomTerminal: (typeof defaultUIState)["bottomTerminal"];
sidebarViews: (typeof defaultUIState)["sidebarViews"];
collapsedSubtaskParents: (typeof defaultUIState)["collapsedSubtaskParents"];
kanbanPreviewedTaskId: (typeof defaultUIState)["kanbanPreviewedTaskId"];
// GitHub actions
setGitHubStatus: (status: GitHubStatus | null) => void;
setGitHubStatusLoading: (loading: boolean) => void;
setTaskPRs: (prs: Record<string, TaskPR[]>) => void;
setTaskPR: (taskId: string, pr: TaskPR) => void;
setPRWatches: (watches: PRWatch[]) => void;
setPRWatchesLoading: (loading: boolean) => void;
removePRWatch: (id: string) => void;
setReviewWatches: (watches: GitHubReviewWatch[]) => void;
setReviewWatchesLoading: (loading: boolean) => void;
addReviewWatch: (watch: GitHubReviewWatch) => void;
updateReviewWatch: (watch: GitHubReviewWatch) => void;
removeReviewWatch: (id: string) => void;
setIssueWatches: (watches: GitHubIssueWatch[]) => void;
setIssueWatchesLoading: (loading: boolean) => void;
addIssueWatch: (watch: GitHubIssueWatch) => void;
updateIssueWatch: (watch: GitHubIssueWatch) => void;
removeIssueWatch: (id: string) => void;
setActionPresets: (workspaceId: string, presets: GitHubActionPresets) => void;
setActionPresetsLoading: (workspaceId: string, loading: boolean) => void;
// JIRA actions
setJiraIssueWatches: (watches: JiraIssueWatch[]) => void;
setJiraIssueWatchesLoading: (loading: boolean) => void;
addJiraIssueWatch: (watch: JiraIssueWatch) => void;
updateJiraIssueWatch: (watch: JiraIssueWatch) => void;
removeJiraIssueWatch: (id: string) => void;
resetJiraIssueWatches: () => void;
// Actions from all slices
hydrate: (state: Partial<AppState>, options?: HydrationOptions) => void;
setActiveWorkspace: (workspaceId: string | null) => void;
setWorkspaces: (workspaces: WorkspaceState["items"]) => void;
setActiveWorkflow: (workflowId: string | null) => void;
setWorkflows: (workflows: WorkflowsState["items"]) => void;
reorderWorkflowItems: (workflowIds: string[]) => void;
setWorkflowSnapshot: (
workflowId: string,
data: import("./slices/kanban/types").WorkflowSnapshotData,
) => void;
setKanbanMultiLoading: (loading: boolean) => void;
clearKanbanMulti: () => void;
updateMultiTask: (
workflowId: string,
task: import("./slices/kanban/types").KanbanState["tasks"][number],
) => void;
removeMultiTask: (workflowId: string, taskId: string) => void;
setExecutors: (executors: ExecutorsState["items"]) => void;
setSettingsAgents: (agents: SettingsAgentsState["items"]) => void;
setAgentDiscovery: (agents: AgentDiscoveryState["items"]) => void;
setAgentDiscoveryLoading: (loading: boolean) => void;
setAvailableAgents: (
agents: AvailableAgentsState["items"],
tools?: AvailableAgentsState["tools"],
) => void;
setAvailableAgentsLoading: (loading: boolean) => void;
setAgentProfiles: (profiles: AgentProfilesState["items"]) => void;
setRepositories: (workspaceId: string, repositories: Repository[]) => void;
setRepositoriesLoading: (workspaceId: string, loading: boolean) => void;
setRepositoryBranches: (
repositoryId: string,
branches: Branch[],
meta?: { fetchedAt?: string; fetchError?: string },
) => void;
setRepositoryBranchesLoading: (repositoryId: string, loading: boolean) => void;
setRepositoryBranchesFetchError: (repositoryId: string, error: string | undefined) => void;
setRepositoryScripts: (repositoryId: string, scripts: RepositoryScript[]) => void;
setRepositoryScriptsLoading: (repositoryId: string, loading: boolean) => void;
clearRepositoryScripts: (repositoryId: string) => void;
invalidateRepositories: (workspaceId: string) => void;
setSettingsData: (next: Partial<SettingsDataState>) => void;
setEditors: (editors: EditorsState["items"]) => void;
setEditorsLoading: (loading: boolean) => void;
setPrompts: (prompts: PromptsState["items"]) => void;
setPromptsLoading: (loading: boolean) => void;
setSecrets: (items: SecretsState["items"]) => void;
setSecretsLoading: (loading: boolean) => void;
addSecret: (item: import("@/lib/types/http-secrets").SecretListItem) => void;
updateSecret: (item: import("@/lib/types/http-secrets").SecretListItem) => void;
removeSecret: (id: string) => void;
setSpritesStatus: (status: import("@/lib/types/http-sprites").SpritesStatus) => void;
setSpritesInstances: (instances: import("@/lib/types/http-sprites").SpritesInstance[]) => void;
setSpritesLoading: (loading: boolean) => void;
removeSpritesInstance: (name: string) => void;
setNotificationProviders: (state: NotificationProvidersState) => void;
setNotificationProvidersLoading: (loading: boolean) => void;
setUserSettings: (settings: UserSettingsState) => void;
setTerminalOutput: (terminalId: string, data: string) => void;
appendShellOutput: (sessionId: string, data: string) => void;
setShellStatus: (
sessionId: string,
status: { available: boolean; running?: boolean; shell?: string; cwd?: string },
) => void;
clearShellOutput: (sessionId: string) => void;
appendProcessOutput: (processId: string, data: string) => void;
upsertProcessStatus: (status: ProcessStatusEntry) => void;
clearProcessOutput: (processId: string) => void;
setActiveProcess: (sessionId: string, processId: string) => void;
setPreviewOpen: (sessionId: string, open: boolean) => void;
togglePreviewOpen: (sessionId: string) => void;
setPreviewView: (sessionId: string, view: PreviewViewMode) => void;
setPreviewDevice: (sessionId: string, device: PreviewDevicePreset) => void;
setPreviewStage: (sessionId: string, stage: PreviewStage) => void;
setPreviewUrl: (sessionId: string, url: string) => void;
setPreviewUrlDraft: (sessionId: string, url: string) => void;
setRightPanelActiveTab: (sessionId: string, tab: string) => void;
setConnectionStatus: (status: ConnectionState["status"], error?: string | null) => void;
setMobileKanbanColumnIndex: (index: number) => void;
setMobileKanbanMenuOpen: (open: boolean) => void;
setMobileSessionPanel: (
sessionId: string,
panel: import("./slices/ui/types").MobileSessionPanel,
) => void;
setMobileSessionTaskSwitcherOpen: (open: boolean) => void;
setPlanMode: (sessionId: string, enabled: boolean) => void;
setActiveDocument: (
sessionId: string,
doc: import("./slices/ui/types").ActiveDocument | null,
) => void;
setSystemHealth: (response: SystemHealthResponse) => void;
setSystemHealthLoading: (loading: boolean) => void;
invalidateSystemHealth: () => void;
openQuickChat: (sessionId: string, workspaceId: string, agentProfileId?: string) => void;
closeQuickChat: () => void;
closeQuickChatSession: (sessionId: string) => void;
setActiveQuickChatSession: (sessionId: string) => void;
renameQuickChatSession: (sessionId: string, name: string) => void;
openConfigChat: (sessionId: string, workspaceId: string) => void;
startNewConfigChat: (workspaceId: string) => void;
closeConfigChat: () => void;
closeConfigChatSession: (sessionId: string) => void;
setActiveConfigChatSession: (sessionId: string) => void;
renameConfigChatSession: (sessionId: string, name: string) => void;
setSessionFailureNotification: (
n: import("./slices/ui/types").SessionFailureNotification | null,
) => void;
toggleBottomTerminal: () => void;
openBottomTerminalWithCommand: (command: string) => void;
clearBottomTerminalCommand: () => void;
setMessages: (
sessionId: string,
messages: Message[],
meta?: { hasMore?: boolean; oldestCursor?: string | null },
) => void;
addMessage: (message: Message) => void;
addTurn: (turn: Turn) => void;
completeTurn: (sessionId: string, turnId: string, completedAt: string) => void;
setActiveTurn: (sessionId: string, turnId: string | null) => void;
updateMessage: (message: Message) => void;
prependMessages: (
sessionId: string,
messages: Message[],
meta?: { hasMore?: boolean; oldestCursor?: string | null },
) => void;
setMessagesMetadata: (
sessionId: string,
meta: { hasMore?: boolean; isLoading?: boolean; oldestCursor?: string | null },
) => void;
setMessagesLoading: (sessionId: string, loading: boolean) => void;
setActiveSession: (taskId: string, sessionId: string) => void;
setActiveSessionAuto: (taskId: string, sessionId: string) => void;
setActiveTask: (taskId: string) => void;
clearActiveSession: () => void;
setTaskSession: (session: TaskSession) => void;
removeTaskSession: (taskId: string, sessionId: string) => void;
setTaskSessionsForTask: (taskId: string, sessions: TaskSession[]) => void;
setTaskSessionsLoading: (taskId: string, loading: boolean) => void;
setSessionAgentctlStatus: (sessionId: string, status: SessionAgentctlStatus) => void;
setWorktree: (worktree: Worktree) => void;
setSessionWorktrees: (sessionId: string, worktreeIds: string[]) => void;
setGitStatus: (sessionId: string, gitStatus: GitStatusEntry) => void;
clearGitStatus: (sessionId: string) => void;
registerSessionEnvironment: (sessionId: string, environmentId: string) => void;
setSessionCommits: (sessionId: string, commits: SessionCommit[]) => void;
setSessionCommitsLoading: (sessionId: string, loading: boolean) => void;
addSessionCommit: (sessionId: string, commit: SessionCommit) => void;
clearSessionCommits: (sessionId: string) => void;
setContextWindow: (sessionId: string, contextWindow: ContextWindowEntry) => void;
bumpAgentProfilesVersion: () => void;
setPendingModel: (sessionId: string, modelId: string) => void;
clearPendingModel: (sessionId: string) => void;
setActiveModel: (sessionId: string, modelId: string) => void;
// Task plan actions
setTaskPlan: (taskId: string, plan: import("@/lib/types/http").TaskPlan | null) => void;
setTaskPlanLoading: (taskId: string, loading: boolean) => void;
setTaskPlanSaving: (taskId: string, saving: boolean) => void;
clearTaskPlan: (taskId: string) => void;
markTaskPlanSeen: (taskId: string) => void;
// Plan revision actions
setPlanRevisions: (
taskId: string,
revisions: import("@/lib/types/http").TaskPlanRevision[],
) => void;
upsertPlanRevision: (
taskId: string,
revision: import("@/lib/types/http").TaskPlanRevision,
) => void;
setPlanRevisionsLoading: (taskId: string, loading: boolean) => void;
cachePlanRevisionContent: (revisionId: string, content: string) => void;
// Plan revision preview + compare actions
setPreviewRevision: (taskId: string, revisionId: string | null) => void;
toggleComparePair: (taskId: string, revisionId: string) => void;
clearComparePair: (taskId: string) => void;
// Queue actions
setQueueStatus: (sessionId: string, status: import("./slices/session/types").QueueStatus) => void;
setQueueLoading: (sessionId: string, loading: boolean) => void;
clearQueueStatus: (sessionId: string) => void;
// Available commands actions
setAvailableCommands: (sessionId: string, commands: AvailableCommand[]) => void;
clearAvailableCommands: (sessionId: string) => void;
// Session mode actions
setSessionMode: (sessionId: string, modeId: string, availableModes?: SessionModeEntry[]) => void;
clearSessionMode: (sessionId: string) => void;
// Agent capabilities actions
setAgentCapabilities: (sessionId: string, caps: AgentCapabilitiesEntry) => void;
// Session models actions
setSessionModels: (
sessionId: string,
data: {
currentModelId: string;
models: SessionModelEntry[];
configOptions: ConfigOptionEntry[];
},
) => void;
// Prompt usage actions
setPromptUsage: (sessionId: string, usage: PromptUsageEntry) => void;
// Session todos actions
setSessionTodos: (sessionId: string, entries: TodoEntry[]) => void;
// User shells actions
setUserShells: (sessionId: string, shells: UserShellInfo[]) => void;
setUserShellsLoading: (sessionId: string, loading: boolean) => void;
addUserShell: (sessionId: string, shell: UserShellInfo) => void;
removeUserShell: (sessionId: string, terminalId: string) => void;
setSessionPollMode: (sessionId: string, mode: SessionPollMode) => void;
/* prettier-ignore */ setSidebarActiveView: UIA["setSidebarActiveView"];
updateSidebarDraft: UIA["updateSidebarDraft"];
saveSidebarDraftAs: UIA["saveSidebarDraftAs"];
saveSidebarDraftOverwrite: UIA["saveSidebarDraftOverwrite"];
discardSidebarDraft: UIA["discardSidebarDraft"];
deleteSidebarView: UIA["deleteSidebarView"];
renameSidebarView: UIA["renameSidebarView"];
duplicateSidebarView: UIA["duplicateSidebarView"];
reorderSidebarViews: UIA["reorderSidebarViews"];
toggleSidebarGroupCollapsed: UIA["toggleSidebarGroupCollapsed"];
toggleSubtaskCollapsed: UIA["toggleSubtaskCollapsed"];
clearSidebarSyncError: UIA["clearSidebarSyncError"];
migrateLocalViewsToBackend: UIA["migrateLocalViewsToBackend"];
setKanbanPreviewedTaskId: UIA["setKanbanPreviewedTaskId"];
};
export type AppStore = ReturnType<typeof createAppStore>;
export function createAppStore(initialState?: Partial<AppState>) {
const merged = mergeInitialState(initialState);
return createStore<AppState>()(
immer((set, get, api) => ({
...merged,
// Compose all slices
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...createKanbanSlice(set as any, get as any, api as any),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...createWorkspaceSlice(set as any, get as any, api as any),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...createSettingsSlice(set as any, get as any, api as any),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...createSessionSlice(set as any, get as any, api as any),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...createSessionRuntimeSlice(set as any, get as any, api as any),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...createGitHubSlice(set as any, get as any, api as any),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...createJiraSlice(set as any, get as any, api as any),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...createUISlice(set as any, get as any, api as any),
// Override state with merged initial state
kanban: merged.kanban,
kanbanMulti: merged.kanbanMulti,
workflows: merged.workflows,
tasks: merged.tasks,
workspaces: merged.workspaces,
repositories: merged.repositories,
repositoryBranches: merged.repositoryBranches,
repositoryScripts: merged.repositoryScripts,
executors: merged.executors,
settingsAgents: merged.settingsAgents,
agentDiscovery: merged.agentDiscovery,
availableAgents: merged.availableAgents,
agentProfiles: merged.agentProfiles,
editors: merged.editors,
prompts: merged.prompts,
secrets: merged.secrets,
notificationProviders: merged.notificationProviders,
settingsData: merged.settingsData,
userSettings: merged.userSettings,
messages: merged.messages,
turns: merged.turns,
taskSessions: merged.taskSessions,
taskSessionsByTask: merged.taskSessionsByTask,
sessionAgentctl: merged.sessionAgentctl,
worktrees: merged.worktrees,
sessionWorktreesBySessionId: merged.sessionWorktreesBySessionId,
pendingModel: merged.pendingModel,
activeModel: merged.activeModel,
queue: merged.queue,
terminal: merged.terminal,
shell: merged.shell,
processes: merged.processes,
gitStatus: merged.gitStatus,
contextWindow: merged.contextWindow,
agents: merged.agents,
sessionMode: merged.sessionMode,
userShells: merged.userShells,
prepareProgress: merged.prepareProgress,
sessionTodos: merged.sessionTodos,
agentCapabilities: merged.agentCapabilities,
sessionModels: merged.sessionModels,
promptUsage: merged.promptUsage,
sessionPollMode: merged.sessionPollMode,
githubStatus: merged.githubStatus,
taskPRs: merged.taskPRs,
prWatches: merged.prWatches,
reviewWatches: merged.reviewWatches,
issueWatches: merged.issueWatches,
actionPresets: merged.actionPresets,
jiraIssueWatches: merged.jiraIssueWatches,
previewPanel: merged.previewPanel,
rightPanel: merged.rightPanel,
diffs: merged.diffs,
connection: merged.connection,
mobileKanban: merged.mobileKanban,
mobileSession: merged.mobileSession,
chatInput: merged.chatInput,
documentPanel: merged.documentPanel,
systemHealth: merged.systemHealth,
quickChat: merged.quickChat,
sessionFailureNotification: merged.sessionFailureNotification,
bottomTerminal: merged.bottomTerminal,
// Note: collapsedSubtaskParents is intentionally not overridden here —
// createUISlice hydrates it from sessionStorage and we want that to win.
// Add hydrate method
// eslint-disable-next-line @typescript-eslint/no-explicit-any
hydrate: (state, options) => set((draft) => hydrateState(draft as any, state, options)),
})),
);
}
export type StoreProviderProps = {
children: React.ReactNode;
initialState?: Partial<AppState>;
};