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

The OpenMAIC codebase uses two distinct Zustand stores located in lib/store to manage classroom and agent data: useStageStore in stage.ts handles classroom lifecycle and content, while useSettingsStore in 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 Classroom document, scenes, sessions, persistence
useSettingsStore Agent configuration 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) 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

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) serves dual purposes: it contains global application settings and owns the agent configuration domain. This store persists user preferences across sessions and classrooms.

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

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.

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) 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) 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 manages classroom lifecycle, scenes, chat sessions, and runtime UI state — it represents a loaded classroom or "stage"

  • useSettingsStore in 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →