Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.ixo.world/llms.txt

Use this file to discover all available pages before exploring further.

Overview

MainAgentGraphState is a LangGraph annotation-based state. Every node in the graph reads it and may return a partial update; the runtime merges updates via per-field reducers and checkpoints the result to per-user SQLite (backed by Matrix).
import { Annotation, MessagesAnnotation } from '@langchain/langgraph';

export const MainAgentGraphState = Annotation.Root({
  messages: MessagesAnnotation.spec.messages,
  config: Annotation<{ wsId?: string; did: string }>({ /* ... */ }),
  client: Annotation<'portal' | 'matrix' | 'slack'>({ /* ... */ }),
  editorRoomId: Annotation<string | undefined>({ /* ... */ }),
  spaceId: Annotation<string | undefined>({ /* ... */ }),
  currentEntityDid: Annotation<string | undefined>({ /* ... */ }),
  browserTools: Annotation<BrowserToolCallDto[] | undefined>({ /* ... */ }),
  agActions: Annotation<AgActionDto[] | undefined>({ /* ... */ }),
  userContext: Annotation<UserContextData>({ /* ... */ }),
  mcpUcanContext: Annotation<{ invocations: Record<string, string> } | undefined>({ /* ... */ }),
  userPreferences: Annotation<UserPreferences | undefined>({ /* ... */ }),
  loadedPlugins: Annotation<string[]>({
    reducer: (current, update) =>
      Array.from(new Set([...(current ?? []), ...(update ?? [])])),
    default: () => [],
  }),
});
Plugins read state via rtCtx.history.state (typed as ReadonlyState) or via specific helpers like rtCtx.history.messages / rtCtx.history.userContext.

Fields

  • Type: LangChain BaseMessage[]
  • Reducer: MessagesAnnotation default (append, dedupe by id).
  • Owner: runtime + agent loop.
The thread’s full message history. Plugins read it via rtCtx.history.messages (readonly) or rtCtx.history.recent(n).
  • Type: { wsId?: string; did: string }
  • Owner: runtime.
Per-request runtime config — primarily the user’s DID and the WebSocket connection ID.
  • Type: 'portal' | 'matrix' | 'slack'
  • Owner: runtime.
Which client surface this turn arrived through. Same value plugins read via rtCtx.session.client.
  • Type: string | undefined
  • Owner: Editor plugin.
The active BlockNote room for editor sub-agent work.
  • Type: string | undefined
  • Owner: runtime / plugins.
The active workspace/space ID, when relevant.
  • Type: string | undefined
  • Owner: Domain Indexer plugin.
The IXO entity DID currently in focus, when set by a domain lookup.
  • Type: BrowserToolCallDto[] | undefined
  • Owner: Portal client.
Browser tools declared by the Portal frontend for this turn. The Portal plugin’s sub-agent only builds when this array is non-empty.
  • Type: AgActionDto[] | undefined
  • Owner: Portal client (AG-UI).
AG-UI actions declared by the frontend. The AG-UI plugin’s sub-agent only builds when this is non-empty.
  • Type: UserContextData (Record<string, unknown>)
  • Owner: Memory plugin (writes via enrichment middleware).
Memory-enriched user profile. Other plugins read via rtCtx.shared.userProfile (registered by Memory’s getSharedState).
  • Type: { invocations: Record<string, string> } | undefined
  • Owner: runtime / UCAN service.
Pre-minted MCP UCAN invocations indexed by service name.
  • Type: UserPreferences | undefined
  • Owner: user-preferences plugin.
Behavioural preferences (tone, format, length) injected into the prompt.
  • Type: string[]
  • Reducer: union via Set (deduplicating).
  • Default: [].
  • Owner: runtime — written by the load_capability meta-tool.
The names of on-demand plugins the agent has loaded for this thread. Monotonically growing across turns. Cleared on new thread.This is the single new field the plugin runtime added to the state — every other field above pre-dates the plugin rewrite.

ReadonlyState

Plugins access the state via rtCtx.history.state, which is typed as ReadonlyState:
export interface ReadonlyState {
  readonly messages: readonly BaseMessage[];
  readonly userContext?: UserContextData;
  readonly loadedPlugins?: ReadonlySet<string>;
  readonly [key: string]: unknown;
}
The full annotation state is open-ended ([key: string]: unknown), so plugins can read field names they know exist but the type doesn’t enforce it. For fully-typed reads, declare the field on SharedAccessors via shared state, or check existence at runtime.

Reducers

Each field has a reducer that merges partial updates from agent nodes:
  • messages — append + dedupe by ID (LangGraph default for the messages channel).
  • loadedPlugins — union via Set; never removes.
  • Most other fields use last-write-wins or “merge if present” semantics; consult the source for the exact reducer when authoring middleware that mutate state.
Plugin middleware that need to update state should return a partial state object from the hook. The runtime applies it through the reducer pipeline.

Checkpointing

State is checkpointed per thread to per-user SQLite via UserMatrixSqliteSyncService + SqliteSaver. The DB lives under SQLITE_DATABASE_PATH, synced to Matrix in the background. The runtime exposes hooks.checkpointerForUser(userDid) so hosts can override the default per-user checkpointer with an alternate implementation — pass via createOracleApp({ hooks }).