> Discover all available pages from the documentation index: https://mastra.ai/llms.txt # AgentController > **Beta:** Breaking changes may occur without a major version bump until the API is stable. The `AgentController` class is a shared host for one or more [`Session`](https://mastra.ai/reference/agent-controller/session) instances. Initialize the controller, create a session, then use `session.*` APIs for conversation state and run control. For a guided introduction, see the [AgentController overview](https://mastra.ai/docs/harness/agent-controller). ## Usage example The following example initializes a controller and creates a session. It subscribes to session events before sending a message. ```typescript import { Agent } from '@mastra/core/agent' import { AgentController } from '@mastra/core/agent-controller' import { Workspace } from '@mastra/core/workspace' const agent = new Agent({ id: 'coding-agent', name: 'Coding agent', instructions: 'Help with software engineering tasks.', model: 'anthropic/claude-sonnet-4-6', }) const controller = new AgentController({ id: 'coding-controller', agent, workspace: new Workspace({ id: 'coding-workspace' }), modes: [{ id: 'build', name: 'Build', metadata: { default: true } }], }) await controller.init() const session = await controller.createSession({ resourceId: 'project-42' }) const unsubscribe = session.subscribe(event => { if (event.type === 'message_update') { console.log(event.message) } }) await session.sendMessage({ content: 'Review the project structure.' }) unsubscribe() ``` ## Constructor parameters **id** (`string`): Unique controller identifier. It is also the default session and resource identifier. **modes** (`AgentControllerMode[]`): Mode definitions available to every session. At least one mode is required. **modes.id** (`string`): Unique mode identifier. **modes.name** (`string`): Display name. **modes.defaultModelId** (`string`): Model selected when a session enters this mode without a stored selection. **modes.description** (`string`): Text shown in mode selectors. **modes.instructions** (`string`): Instructions layered above the backing agent instructions for this mode. **modes.transitionsTo** (`string`): Mode entered after an approved submit\_plan suspension. **modes.availableTools** (`string[]`): Allowlist of exposed tool names. An empty array hides every tool in this mode. **modes.metadata** (`Record`): Pass-through mode metadata. metadata.default: true marks the default mode. **modes.tools** (`ToolsInput`): Mode tools. Mutually exclusive with additionalTools. **modes.additionalTools** (`ToolsInput`): Tools added to the backing agent tools. Mutually exclusive with tools. **modes.agent** (`Agent`): Deprecated mode-specific agent. Use the top-level agent parameter. **modes.default** (`boolean`): Deprecated default marker. Use metadata.default or defaultModeId. **agent** (`Agent`): Shared backing agent used by the configured modes. **resourceId** (`string`): Default resource identifier for sessions and threads. Defaults to id. **storage** (`MastraCompositeStore`): Storage used for persistent threads, messages, settings, and resumable run data. **stateSchema** (`PublicSchema`): Schema used to validate session.state updates. **initialState** (`Partial`): Initial state merged with schema defaults for each new session. **memory** (`DynamicArgument`): Memory instance shared with backing agents that do not define their own memory. **defaultModeId** (`string`): Default mode identifier. It takes precedence over mode metadata. **instructions** (`string`): Controller instructions layered with the current mode instructions. **tools** (`DynamicArgument`): Tools shared by controller runs and available to configured subagents. **workspace** (`DynamicArgument`): Static workspace or per-session workspace factory. A session must resolve a valid workspace. **browser** (`DynamicArgument`): Static browser or per-session browser factory. **channels** (`AgentControllerChannelsConfig`): Chat channel configuration used to route channel threads into controller sessions. **intervalHandlers** (`IntervalHandler[]`): Periodic handlers started by init() and stopped by stopIntervals() or destroy(). **idGenerator** (`() => string`): Custom identifier generator for threads, messages, and signals. **modelUseCountProvider** (`ModelUseCountProvider`): Returns model usage counts used to sort available models. **modelUseCountTracker** (`ModelUseCountTracker`): Records a model selection after session.model.switch(). **subagents** (`AgentControllerSubagent[]`): Subagent types exposed through the built-in subagent tool. **subagents.id** (`string`): Unique subagent type identifier. **subagents.name** (`string`): Display name. **subagents.description** (`string`): Description used by the generated tool. **subagents.instructions** (`DynamicArgument`): Subagent instructions. **subagents.tools** (`ToolsInput`): Tools owned by the subagent. **subagents.allowedControllerTools** (`string[]`): Controller tool IDs added to the subagent tools. **subagents.allowedWorkspaceTools** (`string[]`): Workspace tool names visible to the subagent. **subagents.defaultModelId** (`string`): Default subagent model. **subagents.maxSteps** (`number`): Maximum execution steps. **subagents.stopWhen** (`LoopOptions["stopWhen"]`): Loop stop condition. **subagents.forked** (`boolean`): Whether the subagent inherits a cloned parent thread by default. **gateways** (`MastraModelGatewayInterface[]`): Custom model gateways merged with the built-in gateways. **omConfig** (`AgentControllerOMConfig`): Default observational memory models and thresholds. **disableBuiltinTools** (`BuiltinToolId[]`): Built-in controller tools to omit from runs. **toolCategoryResolver** (`(toolName: string) => ToolCategory | null`): Maps tool names to permission categories. **pubsub** (`PubSub`): PubSub implementation propagated to backing agents. **threadLock** (`{ acquire: (threadId: string) => void | Promise; release: (threadId: string) => void | Promise }`): Lock implementation used to coordinate thread ownership. **observability** (`ObservabilityEntrypoint`): Observability configuration for a standalone controller Mastra instance. ## Properties **id** (`string`): The controller identifier passed to the constructor. ## Methods ### Sessions #### `createSession(options)` Get or create the live session registered for the `(resourceId, scope)` pair. Call `init()` before this method. ```typescript const session = await controller.createSession({ resourceId: 'project-42', scope: 'editor-window-1', threadId: 'thread-7', }) ``` The same `resourceId` and `scope` return the same `Session` instance. A different scope creates an isolated session for the same resource. When `threadId` is supplied, the method switches a cached session to that thread or creates the thread when it doesn't exist. **resourceId** (`string`): Memory resource and live-session registry key. Defaults to the configured resourceId or controller id. **scope** (`string`): Optional registry namespace that allows multiple live sessions for one resource. **threadId** (`string`): Exact thread to bind. Missing threads are created with this identifier. **id** (`string`): Stable session identifier. Defaults to the controller id. **ownerId** (`string`): Stable session owner identifier. Defaults to id. **tags** (`Record`): Tags copied to threads created by the session. **workspace** (`Workspace`): Workspace override for this session. **browser** (`MastraBrowser`): Browser override for this session. **requestContext** (`RequestContext`): Context used to resolve dynamic workspace and browser factories. Returns: `Promise>` #### `getSessionByResource(resourceId, scope?)` Return the live session registered for a resource and optional scope. ```typescript const session = await controller.getSessionByResource('project-42', 'editor-window-1') ``` Returns: `Promise | undefined>` #### `onSessionDeleted(listener)` Subscribe to process-local notifications after `deleteSession()` tears down a live session. Listener errors are isolated and don't prevent other listeners from running. Call the returned function to unsubscribe. ```typescript const unsubscribe = controller.onSessionDeleted(session => { console.log(`Closed ${session.identity.getResourceId()}`) }) await controller.createSession({ resourceId: 'project-42' }) await controller.deleteSession({ resourceId: 'project-42' }) unsubscribe() ``` This listener is best-effort and runs only in the current process. It isn't a persisted deletion event. Returns: `() => void` #### `deleteSession({ resourceId, scope? })` Tear down the materialized session for a resource and optional scope. The method aborts active work, then removes thread subscriptions, the active thread lock, and the live session from the controller registry. It doesn't delete persisted threads or messages. ```typescript const deleted = await controller.deleteSession({ resourceId: 'project-42', scope: 'editor-window-1', }) ``` Returns: `Promise`. The result is `true` when a live session was removed and `false` when no matching live session exists or when another deletion is already in progress for the same resource and scope. #### `setResourceId(session, { resourceId })` Move a live session to another resource and clear its active thread binding. ```typescript await controller.setResourceId(session, { resourceId: 'project-43' }) ``` #### `getKnownResourceIds(session)` List resource identifiers present in stored threads. ```typescript const resourceIds = await controller.getKnownResourceIds(session) ``` Returns: `Promise` ### Lifecycle #### `init()` Initialize shared storage, workspace services, and configured interval handlers. Repeated calls reuse the same initialization promise. ```typescript await controller.init() ``` #### `destroy()` Stop controller-owned interval handlers. This doesn't destroy Sessions created by the controller. ```typescript await controller.destroy() ``` ### Modes and agents #### `listModes()` Return the configured mode definitions. ```typescript const modes = controller.listModes() ``` Returns: `AgentControllerMode[]` #### `getCurrentAgent(session)` Return the backing agent for the session's active mode. ```typescript const agent = controller.getCurrentAgent(session) ``` Returns: `Agent` ### Workspace and browser #### `hasWorkspace()` Report whether the controller has a static, dynamic, or object-based workspace configuration. ```typescript if (controller.hasWorkspace()) { console.log('Workspace configured') } ``` Returns: `boolean` #### `isWorkspaceReady()` Report whether the controller-level workspace is ready. ```typescript const ready = controller.isWorkspaceReady() ``` Returns: `boolean` #### `getWorkspace()` Return a static controller workspace. Dynamic workspace factories resolve per session and return `undefined` here, so use `resolveWorkspace` instead. ```typescript const workspace = controller.getWorkspace() ``` Returns: `Workspace | undefined` #### `resolveWorkspace({ session, requestContext? })` Return the workspace a session runs against. Sessions resolve theirs at creation, dynamic factories included, so this returns that instance and only falls back to the factory for a session created without one. ```typescript const workspace = await controller.resolveWorkspace({ session, requestContext }) ``` Returns: `Promise` #### `setBrowser(browser)` Replace the controller browser and propagate it to the backing agents. ```typescript controller.setBrowser(browser) ``` ### Mastra and channels #### `getMastra()` Return the parent Mastra instance or the internal instance created by `init()`. ```typescript const mastra = controller.getMastra() ``` Returns: `Mastra | undefined` #### `getChannels()` Return the configured chat channel integration. ```typescript const channels = controller.getChannels() ``` Returns: `AgentControllerChannels | null` ### Models #### `getCurrentModelAuthStatus(session)` Return authentication status for the session's selected model. ```typescript const status = await controller.getCurrentModelAuthStatus(session) ``` Returns: `Promise` #### `listAvailableModels()` List models from the configured and built-in gateways. Results are cached briefly and sorted with usage data when `modelUseCountProvider` is configured. ```typescript const models = await controller.listAvailableModels() ``` Returns: `Promise` #### `invalidateAvailableModelsCache()` Clear the available-model cache. ```typescript controller.invalidateAvailableModelsCache() ``` ### Observational memory and permissions #### `loadOMProgress(session)` Load stored observational memory progress for the active thread and emit an `om_status` event. ```typescript await controller.loadOMProgress(session) ``` #### `getObservationalMemoryRecord(session)` Return the observational memory record for the active thread. ```typescript const record = await controller.getObservationalMemoryRecord(session) ``` Returns: `Promise` #### `getToolCategory({ toolName })` Resolve the permission category for a tool. ```typescript const category = controller.getToolCategory({ toolName: 'execute_command' }) ``` Returns: `ToolCategory | null` ### Intervals #### `registerInterval(handler)` Start or replace a periodic handler. ```typescript controller.registerInterval({ id: 'refresh', intervalMs: 60_000, handler: async () => refreshData(), }) ``` #### `removeInterval({ id })` Stop one interval and run its optional shutdown callback. ```typescript await controller.removeInterval({ id: 'refresh' }) ``` #### `stopIntervals()` Stop all intervals and run their optional shutdown callbacks. ```typescript await controller.stopIntervals() ``` ## Related - [AgentController guide](https://mastra.ai/docs/harness/agent-controller) - [Session reference](https://mastra.ai/reference/agent-controller/session) - [Agents](https://mastra.ai/docs/agents/overview) - [Workspace](https://mastra.ai/docs/workspace/overview) - [Channels](https://mastra.ai/docs/capabilities/channels)