# OrchestratorState Annotation in OpenMAIC: The Complete LangGraph State Schema Guide

> Explore the OrchestratorState annotation in OpenMAIC. Understand how LangGraph's Annotation.Root creates a type-safe state schema for multi-agent orchestration, separating inputs from mutable node updates.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: api-reference
- Published: 2026-09-05

---

**The `OrchestratorState` in OpenMAIC is defined using LangGraph's `Annotation.Root` in [`lib/orchestration/director-graph.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-graph.ts), serving as the centralized type-safe state container that separates immutable input fields from mutable fields updated by graph nodes during multi-agent orchestration.**

OpenMAIC implements its multi-agent conversation flow through a **LangGraph** orchestration graph. At the heart of this system lies the `OrchestratorState` annotation—a carefully structured schema that declares every piece of data flowing through the director-graph. This article examines the complete state annotation, its field categories, reducer patterns, and practical usage based on the THU-MAIC/OpenMAIC source code.

## What Is the OrchestratorState Annotation?

The `OrchestratorState` is a **LangGraph `Annotation.Root`** object that functions as the shared state contract for all nodes in the orchestration graph. It distinguishes between two fundamental categories of fields:

- **Input fields** — set once at graph entry and remain immutable
- **Mutable fields** — updated by nodes during execution, some using reducers for array accumulation

This design pattern ensures type safety across the entire multi-agent pipeline while enabling predictable state mutations.

## Complete OrchestratorState Declaration

The full annotation is implemented in [[`lib/orchestration/director-graph.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-graph.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-graph.ts):

```typescript
const OrchestratorState = Annotation.Root({
  // Input (set once at graph entry)
  messages: Annotation<StatelessChatRequest['messages']>,
  storeState: Annotation<StatelessChatRequest['storeState']>,
  availableAgentIds: Annotation<string[]>,
  languageModel: Annotation<LanguageModel>,
  thinkingConfig: Annotation<ThinkingConfig | null>,
  discussionContext: Annotation<{ topic: string; prompt?: string } | null>,
  triggerAgentId: Annotation<string | null>,
  userProfile: Annotation<{ nickname?: string; bio?: string } | null>,
  agentConfigOverrides: Annotation<Record<string, AgentConfig>>,

  // Mutable (updated by nodes)
  currentAgentId: Annotation<string | null>,
  turnCount: Annotation<number>,
  agentResponses: Annotation<AgentTurnSummary[]>({
    reducer: (prev, update) => [...prev, ...update],
    default: () => [],
  }),
  whiteboardLedger: Annotation<WhiteboardActionRecord[]>({
    reducer: (prev, update) => [...prev, ...update],
    default: () => [],
  }),
  shouldEnd: Annotation<boolean>,
  totalActions: Annotation<number>,
});
type OrchestratorStateType = typeof OrchestratorState.State;

```

The `OrchestratorStateType` export provides the TypeScript type used throughout the codebase for function signatures and node implementations.

## Input Fields: Graph Entry Parameters

These **nine fields** are populated once when the orchestration graph initializes and remain read-only during execution:

| Field | Type | Purpose |
|-------|------|---------|
| `messages` | `StatelessChatRequest['messages']` | Incoming chat message history |
| `storeState` | `StatelessChatRequest['storeState']` | Persistent conversation state |
| `availableAgentIds` | `string[]` | IDs of agents eligible to participate |
| `languageModel` | `LanguageModel` | LLM instance for director decision-making |
| `thinkingConfig` | `ThinkingConfig \| null` | Optional reasoning configuration |
| `discussionContext` | `{ topic: string; prompt?: string } \| null` | Topic and prompt context |
| `triggerAgentId` | `string \| null` | Optional first agent to activate |
| `userProfile` | `{ nickname?: string; bio?: string } \| null` | User identity information |
| `agentConfigOverrides` | `Record<string, AgentConfig>` | Runtime agent configuration modifications |

These fields originate from the `StatelessChatRequest` type defined in [[`lib/types/chat.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/types/chat.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/types/chat.ts).

## Mutable Fields: Node-Updatable State

These **six fields** can be modified by graph nodes during execution:

| Field | Type | Update Pattern |
|-------|------|--------------|
| `currentAgentId` | `string \| null` | Direct assignment (which agent speaks now) |
| `turnCount` | `number` | Incremented by nodes tracking conversation progress |
| `agentResponses` | `AgentTurnSummary[]` | **Reducer appends** — new entries merged with existing array |
| `whiteboardLedger` | `WhiteboardActionRecord[]` | **Reducer appends** — records accumulated across turns |
| `shouldEnd` | `boolean` | Flag set to terminate conversation |
| `totalActions` | `number` | Counter incremented per action |

The **reducer pattern** on `agentResponses` and `whiteboardLedger` is critical—these annotations specify:

```typescript
reducer: (prev, update) => [...prev, ...update],
default: () => [],

```

This ensures that when a node returns partial state with new array items, they merge rather than replace existing entries.

## Practical Usage Examples

### Accessing State in a Node

Nodes receive the full `OrchestratorStateType` and can read any field:

```typescript
import type { OrchestratorStateType } from '@/lib/orchestration/director-graph';

function exampleNode(state: OrchestratorStateType) {
  const current = state.currentAgentId; // e.g., 'agent-1' or null
  const messages = state.messages;      // readonly chat history
  // …director logic using availableAgentIds, languageModel, etc.
}

```

### Updating Scalar Fields

Return a partial state object with updated values:

```typescript
async function incrementTurn(
  state: OrchestratorStateType,
): Promise<Partial<OrchestratorStateType>> {
  return { turnCount: (state.turnCount ?? 0) + 1 };
}

```

### Appending to Reducer-Backed Arrays

For fields with reducers, return only the new items—the reducer handles merging:

```typescript
function addAgentResponse(
  state: OrchestratorStateType,
  response: AgentTurnSummary,
): Partial<OrchestratorStateType> {
  return { agentResponses: [response] }; // reducer automatically appends
}

```

The same pattern applies to `whiteboardLedger` for recording collaborative actions.

## Key Source Files

Understanding the `OrchestratorState` annotation requires familiarity with these OpenMAIC files:

| File | Relevance to State Annotation |
|------|-------------------------------|
| [[`lib/orchestration/director-graph.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-graph.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-graph.ts) | Contains the `OrchestratorState` annotation and complete graph definition |
| [[`lib/orchestration/registry/types.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/registry/types.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/registry/types.ts) | Defines `AgentConfig` used in `agentConfigOverrides` |
| [[`lib/orchestration/registry/store.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/registry/store.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/registry/store.ts) | Agent registry that consumes `availableAgentIds` |
| [[`lib/orchestration/prompt-builder.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/prompt-builder.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/prompt-builder.ts) | Constructs prompts from `OrchestratorState` fields |
| [[`lib/types/chat.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/types/chat.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/types/chat.ts) | Source of `StatelessChatRequest` types referenced in input fields |

## Summary

- The `OrchestratorState` annotation in OpenMAIC is a **LangGraph `Annotation.Root`** declared in [`lib/orchestration/director-graph.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-graph.ts)
- It separates **immutable input fields** (9 total) from **mutable node-updatable fields** (6 total)
- **Reducer functions** on `agentResponses` and `whiteboardLedger` enable safe array accumulation without manual merging
- The `OrchestratorStateType` derived type enforces type safety across all graph nodes
- This state schema enables the director node to coordinate multi-agent conversations while maintaining predictable, traceable state mutations

## Frequently Asked Questions

### What is Annotation.Root in LangGraph?

**`Annotation.Root`** is a LangGraph utility that declares the complete state schema for a graph. In OpenMAIC, it wraps individual `Annotation<>` field declarations to create the `OrchestratorState` object, which serves as the single source of truth for type definitions and runtime state shape.

### Why do some OrchestratorState fields use reducers?

Reducer functions specify **how to merge updates** when nodes return partial state. OpenMAIC uses reducers on `agentResponses` and `whiteboardLedger` so that nodes can return new array items without overwriting existing entries—the reducer automatically spreads previous and updated values together.

### How do I extend OrchestratorState with custom fields?

Add new fields to the `Annotation.Root` declaration in [`lib/orchestration/director-graph.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-graph.ts). For arrays requiring accumulation, include a reducer function; for scalar values, a simple `Annotation<Type>` suffices. Rebuild the project to regenerate `OrchestratorStateType` across dependent files.

### What triggers the initial population of input fields?

Input fields are populated from the `StatelessChatRequest` received at graph entry. The orchestration initialization code maps request properties—including `messages`, `storeState`, `availableAgentIds`, and optional configuration—to their corresponding `OrchestratorState` fields before the first node executes.