# State Stores in OpenMAIC's lib/store: How Classroom and Agent Data Are Managed

> Discover how OpenMAIC's lib/store manages classroom and agent data using distinct Zustand stores. Learn about useStageStore for classroom lifecycle and useSettingsStore for agent configuration.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: internals
- Published: 2026-09-02

---

**The OpenMAIC codebase uses two distinct Zustand stores located in `lib/store` to manage classroom and agent data: `useStageStore` in [`stage.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/stage.ts) handles classroom lifecycle and content, while `useSettingsStore` in [`settings.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/settings.ts) manages global agent configuration including agent selection, mode, and voice overrides.**

This guide examines the architecture behind OpenMAIC's state management, focusing specifically on how classroom and agent data flow through the `lib/store` directory. Understanding these store boundaries is essential for extending the platform or debugging synchronization issues between classroom sessions and agent preferences.

---

## Overview of the State Store Architecture

OpenMAIC implements a **clean separation of concerns** across two primary stores. Each store owns a distinct domain and exposes typed actions for predictable state mutations.

| Store | Domain | Key File | Primary Responsibility |
|-------|--------|----------|------------------------|
| `useStageStore` | Classroom (stage) data | [`lib/store/stage.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/stage.ts) | Classroom document, scenes, sessions, persistence |
| `useSettingsStore` | Agent configuration | [`lib/store/settings.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/settings.ts) | Agent selection, mode, voice overrides |

This design prevents circular dependencies and ensures that agent preferences persist across classroom sessions while stage-specific data remains scoped to individual classrooms.

---

## useStageStore: Managing Classroom (Stage) Data

The `useStageStore` defined in [[`lib/store/stage.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/stage.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/stage.ts) is the core state container for classroom operations. A "stage" in OpenMAIC terminology represents a complete classroom environment with its associated metadata, scenes, and runtime state.

### What the Stage Store Holds

- The current **stage document** (classroom identity and metadata)
- **Scene data** and the active scene reference
- **Chat sessions** and generation status
- **Viewer-access flags** for presentation mode
- **UI state** including playback/edit mode and toolbar visibility
- **Agent roster** synchronized from settings (via `generatedAgentConfigs`)

### Key Actions in useStageStore

| Action | Purpose |
|--------|---------|
| `setStage()` | Replace the entire stage object |
| `setStageAgents()` | Inject agent configurations from settings |
| `clearStoreForDeletedStage()` | Reset state when a classroom is removed |
| `loadFromStorage()` | Hydrate store from persistent storage |
| Scene mutations | Add, remove, reorder scenes |

### Accessing Classroom Data

```tsx
import { useStageStore } from '@/lib/store';

// Subscribe to current stage (classroom) object
const currentStage = useStageStore((s) => s.stage);

// Subscribe to specific properties for granular re-renders
const stageTitle = useStageStore((s) => s.stage?.title);
const isGenerating = useStageStore((s) => s.isGenerating);

// Direct state mutation via action
useStageStore.getState().setStage({
  ...currentStage!,
  title: 'Advanced Machine Learning',
});

```

The store uses Zustand's selector pattern to enable efficient subscriptions. Calls to `loadFromStorage(stageId)` automatically populate the store and synchronize the agent roster through `setStageAgents`.

---

## useSettingsStore: Managing Agent Configuration

The `useSettingsStore` in [[`lib/store/settings.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/settings.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/settings.ts) serves dual purposes: it contains global application settings and owns the **agent configuration domain**. This store persists user preferences across sessions and classrooms.

### Agent-Related State in useSettingsStore

| State Key | Type | Description |
|-----------|------|-------------|
| `selectedAgentIds` | `string[]` | IDs of agents currently selected for use |
| `agentMode` | `'preset' \| 'custom'` | Whether using preset agents or custom configuration |
| `agentVoiceOverrides` | `Record<string, VoiceConfig>` | Per-agent voice customizations |
| `agentSelectionIsUserSet` | `boolean` | Flag indicating manual vs. default selection |

### Key Actions for Agent Management

| Action | Signature | Purpose |
|--------|-----------|---------|
| `setSelectedAgentIds()` | `(ids: string[]) => void` | Update the active agent roster |
| `setAgentMode()` | `(mode: 'preset' \| 'custom') => void` | Switch between preset and custom agent modes |
| `setAgentVoiceOverride()` | `(agentId: string, config: VoiceConfig) => void` | Configure voice for a specific agent |

### Working with Agent Configuration

```tsx
import { useSettingsStore } from '@/lib/store';

// Read current agent selection
const selectedAgents = useSettingsStore((s) => s.selectedAgentIds);
const agentMode = useSettingsStore((s) => s.agentMode);

// Update agent selection (e.g., from AgentBar UI)
useSettingsStore.getState().setSelectedAgentIds(['agent-42', 'agent-99']);

// Switch to preset mode
useSettingsStore.getState().setAgentMode('preset');

// Override voice configuration for a specific agent
useSettingsStore.getState().setAgentVoiceOverride('agent-42', {
  voiceId: 'en_us_female',
  speed: 1.0,
});

```

The `agentSelectionIsUserSet` flag enables the UI to distinguish between explicit user choices and system defaults, supporting intelligent reset behavior.

---

## Store Interactions: Loading a Classroom with Agents

When a classroom initializes, the two stores coordinate to ensure consistent state. The `useStageStore` pulls agent configuration from `useSettingsStore` during the loading sequence.

```tsx
import { useStageStore } from '@/lib/store/stage';
import { useSettingsStore } from '@/lib/store/settings';

// Load classroom — this triggers internal synchronization
await useStageStore.getState().loadFromStorage(stageId);

// The stage now contains agent configs derived from settings
const agentsInClassroom = useStageStore
  .getState()
  .stage?.generatedAgentConfigs ?? [];

// Settings remain the source of truth for user preferences
const userPreferredAgents = useSettingsStore((s) => s.selectedAgentIds);

```

This pattern ensures that:
- **Classroom-specific data** (scenes, content) lives in `useStageStore`
- **Cross-cutting concerns** (agent preferences) live in `useSettingsStore`
- **Synchronization** happens at well-defined lifecycle boundaries

---

## Supporting Files in lib/store

Beyond the two primary stores, the `lib/store` directory contains additional utilities:

| File | Role |
|------|------|
| [[`lib/store/index.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/index.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/index.ts) | Barrel export for all stores (`useCanvasStore`, `useStageStore`, `useSnapshotStore`, `useKeyboardStore`, `useSettingsStore`) |
| [[`lib/store/course-refs.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/course-refs.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/course-refs.ts) | Utilities for course reference handling, indirectly supporting classroom data relationships |

All stores are implemented with **Zustand**, a lightweight state management library that provides TypeScript-native type inference and minimal boilerplate compared to alternatives like Redux.

---

## Summary

- **`useStageStore` in [`lib/store/stage.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/stage.ts)** manages classroom lifecycle, scenes, chat sessions, and runtime UI state — it represents a loaded classroom or "stage"

- **`useSettingsStore` in [`lib/store/settings.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/settings.ts)** holds global configuration including agent selection (`selectedAgentIds`), agent mode, and voice overrides

- **Store coordination** occurs through `setStageAgents` and `loadFromStorage`, ensuring agent preferences propagate into active classrooms

- **Clean architectural boundaries** prevent state leakage: stage data is ephemeral per-classroom, while settings persist across sessions

- All stores expose **typed actions** for predictable mutations and use Zustand's selector pattern for performance

---

## Frequently Asked Questions

### How do I access the current classroom data in a React component?

Import `useStageStore` from `@/lib/store` and use a selector: `const stage = useStageStore((s) => s.stage)`. This subscribes only to stage changes, preventing unnecessary re-renders. For mutations, access `useStageStore.getState().setStage()` or other action methods.

### What's the difference between `selectedAgentIds` and `generatedAgentConfigs`?

`selectedAgentIds` in `useSettingsStore` stores the user's agent preferences globally. `generatedAgentConfigs` on the stage object contains the fully-resolved agent configurations active in a specific classroom. The stage store populates the latter from the former via `setStageAgents`.

### Can I modify agent voice settings per classroom?

Voice overrides are stored globally in `useSettingsStore` via `agentVoiceOverrides`. However, the stage store's `generatedAgentConfigs` captures these at classroom load time. To achieve per-classroom voices, you would need to extend `useStageStore` with classroom-scoped voice state.

### Where does persistence happen for classroom data?

The `useStageStore` implements persistence through `loadFromStorage` and implicit save operations. The [`lib/store/stage.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/store/stage.ts) file contains the persistence pipeline that serializes stage state, though the exact storage backend (likely IndexedDB or localStorage) would be visible in the full implementation of that module.