Skip to main content

AgentController

beta

The AgentController feature is in beta stage and subject to breaking changes in minor versions until it graduates from its beta status.

The AgentController class is a shared host for one or more 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.

Usage example
Direct link to Usage example

The following example initializes a controller and creates a session. It subscribes to session events before sending a message.

src/mastra/agent-controller.ts
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
Direct link to 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.
AgentControllerMode

id:

string
Unique mode identifier.

name?:

string
Display name.

defaultModelId?:

string
Model selected when a session enters this mode without a stored selection.

description?:

string
Text shown in mode selectors.

instructions?:

string
Instructions layered above the backing agent instructions for this mode.

transitionsTo?:

string
Mode entered after an approved submit_plan suspension.

availableTools?:

string[]
Allowlist of exposed tool names. An empty array hides every tool in this mode.

metadata?:

Record<string, unknown>
Pass-through mode metadata. metadata.default: true marks the default mode.

tools?:

ToolsInput
Mode tools. Mutually exclusive with additionalTools.

additionalTools?:

ToolsInput
Tools added to the backing agent tools. Mutually exclusive with tools.

agent?:

Agent
Deprecated mode-specific agent. Use the top-level agent parameter.

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<TState, any>
Schema used to validate session.state updates.

initialState?:

Partial<TState>
Initial state merged with schema defaults for each new session.

memory?:

DynamicArgument<MastraMemory>
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<ToolsInput | undefined>
Tools shared by controller runs and available to configured subagents.

workspace?:

DynamicArgument<Workspace | undefined>
Static workspace or per-session workspace factory. A session must resolve a valid workspace.

browser?:

DynamicArgument<MastraBrowser | undefined>
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.
AgentControllerSubagent

id:

string
Unique subagent type identifier.

name:

string
Display name.

description:

string
Description used by the generated tool.

instructions:

DynamicArgument<AgentInstructions>
Subagent instructions.

tools?:

ToolsInput
Tools owned by the subagent.

allowedControllerTools?:

string[]
Controller tool IDs added to the subagent tools.

allowedWorkspaceTools?:

string[]
Workspace tool names visible to the subagent.

defaultModelId?:

string
Default subagent model.

maxSteps?:

number
Maximum execution steps.

stopWhen?:

LoopOptions["stopWhen"]
Loop stop condition.

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<void>; release: (threadId: string) => void | Promise<void> }
Lock implementation used to coordinate thread ownership.

observability?:

ObservabilityEntrypoint
Observability configuration for a standalone controller Mastra instance.

Properties
Direct link to Properties

id:

string
The controller identifier passed to the constructor.

Methods
Direct link to Methods

Sessions
Direct link to Sessions

createSession(options)
Direct link to createsessionoptions

Get or create the live session registered for the (resourceId, scope) pair. Call init() before this method.

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<string, string>
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<Session<TState>>

getSessionByResource(resourceId, scope?)
Direct link to getsessionbyresourceresourceid-scope

Return the live session registered for a resource and optional scope.

const session = await controller.getSessionByResource('project-42', 'editor-window-1')

Returns: Promise<Session<TState> | undefined>

setResourceId(session, { resourceId })
Direct link to setresourceidsession--resourceid-

Move a live session to another resource and clear its active thread binding.

await controller.setResourceId(session, { resourceId: 'project-43' })

getKnownResourceIds(session)
Direct link to getknownresourceidssession

List resource identifiers present in stored threads.

const resourceIds = await controller.getKnownResourceIds(session)

Returns: Promise<string[]>

Lifecycle
Direct link to Lifecycle

init()
Direct link to init

Initialize shared storage, workspace services, and configured interval handlers. Repeated calls reuse the same initialization promise.

await controller.init()

destroy()
Direct link to destroy

Stop controller-owned interval handlers. This doesn't destroy Sessions created by the controller.

await controller.destroy()

Modes and agents
Direct link to Modes and agents

listModes()
Direct link to listmodes

Return the configured mode definitions.

const modes = controller.listModes()

Returns: AgentControllerMode[]

getCurrentAgent(session)
Direct link to getcurrentagentsession

Return the backing agent for the session's active mode.

const agent = controller.getCurrentAgent(session)

Returns: Agent

Workspace and browser
Direct link to Workspace and browser

hasWorkspace()
Direct link to hasworkspace

Report whether the controller has a static, dynamic, or object-based workspace configuration.

if (controller.hasWorkspace()) {
console.log('Workspace configured')
}

Returns: boolean

isWorkspaceReady()
Direct link to isworkspaceready

Report whether the controller-level workspace is ready.

const ready = controller.isWorkspaceReady()

Returns: boolean

getWorkspace()
Direct link to getworkspace

Return a static controller workspace. Dynamic workspace factories return undefined until resolved.

const workspace = controller.getWorkspace()

Returns: Workspace | undefined

resolveWorkspace({ session, requestContext? })
Direct link to resolveworkspace-session-requestcontext-

Resolve a dynamic workspace for a session and cache the result on the controller.

const workspace = await controller.resolveWorkspace({ session, requestContext })

Returns: Promise<Workspace | undefined>

setBrowser(browser)
Direct link to setbrowserbrowser

Replace the controller browser and propagate it to the backing agents.

controller.setBrowser(browser)

Mastra and channels
Direct link to Mastra and channels

getMastra()
Direct link to getmastra

Return the parent Mastra instance or the internal instance created by init().

const mastra = controller.getMastra()

Returns: Mastra | undefined

getChannels()
Direct link to getchannels

Return the configured chat channel integration.

const channels = controller.getChannels()

Returns: AgentControllerChannels | null

Models
Direct link to Models

getCurrentModelAuthStatus(session)
Direct link to getcurrentmodelauthstatussession

Return authentication status for the session's selected model.

const status = await controller.getCurrentModelAuthStatus(session)

Returns: Promise<ModelAuthStatus>

listAvailableModels()
Direct link to listavailablemodels

List models from the configured and built-in gateways. Results are cached briefly and sorted with usage data when modelUseCountProvider is configured.

const models = await controller.listAvailableModels()

Returns: Promise<AvailableModel[]>

invalidateAvailableModelsCache()
Direct link to invalidateavailablemodelscache

Clear the available-model cache.

controller.invalidateAvailableModelsCache()

Observational memory and permissions
Direct link to Observational memory and permissions

loadOMProgress(session)
Direct link to loadomprogresssession

Load stored observational memory progress for the active thread and emit an om_status event.

await controller.loadOMProgress(session)

getObservationalMemoryRecord(session)
Direct link to getobservationalmemoryrecordsession

Return the observational memory record for the active thread.

const record = await controller.getObservationalMemoryRecord(session)

Returns: Promise<ObservationalMemoryRecord | null>

getToolCategory({ toolName })
Direct link to gettoolcategory-toolname-

Resolve the permission category for a tool.

const category = controller.getToolCategory({ toolName: 'execute_command' })

Returns: ToolCategory | null

Intervals
Direct link to Intervals

registerInterval(handler)
Direct link to registerintervalhandler

Start or replace a periodic handler.

controller.registerInterval({
id: 'refresh',
intervalMs: 60_000,
handler: async () => refreshData(),
})

removeInterval({ id })
Direct link to removeinterval-id-

Stop one interval and run its optional shutdown callback.

await controller.removeInterval({ id: 'refresh' })

stopIntervals()
Direct link to stopintervals

Stop all intervals and run their optional shutdown callbacks.

await controller.stopIntervals()