跳到主要内容
非官方简体中文翻译,内容版权归 Mastra 所有,遵循 Apache-2.0 许可。

Session

A Session is the isolated runtime for one resource and optional scope. It owns its event bus, thread binding, state, mode and model selections, run control, approvals, suspensions, follow-ups, and display state. The AgentController supplies shared agents, configuration, storage, workspaces, and services.

Create sessions through controller.createSession(). Direct construction and controller wiring methods aren't application APIs.

For a conceptual introduction, see the AgentController overview.

Usage example
Usage example的直接链接

The following example uses the supported controller-to-session flow.

await controller.init()

const session = await controller.createSession({ resourceId: 'project-42' })
const unsubscribe = session.subscribe(event => {
if (event.type === 'display_state_changed') {
render(event.displayState)
}
})

await session.sendMessage({ content: 'Review the current project.' })
unsubscribe()

Properties
Properties的直接链接

The session is organized into sub-objects, each owning one domain of per-conversation state.

identity:

SessionIdentity
Stable session, owner, and resource identity for the conversation. See identity methods below.

thread:

SessionThread
Active thread binding and thread/message reads. See thread methods below.

mode:

SessionMode
Active mode selection. See mode methods below.

model:

SessionModel
Active model selection, including per-mode persistence. See model methods below.

om:

SessionOM
Observer and reflector model settings for observational memory.

permissions:

SessionPermissions
Tool and category permission policies represented in session state.

subagents:

SessionSubagents
Global and per-agent-type subagent model selection.

run:

SessionRun
Run and trace identity plus abort state for the in-flight run. See run methods below.

stream:

SessionStream
The live subscription to the agent thread stream. See stream methods below.

suspensions:

SessionSuspensions
Parked interactive tool calls awaiting a resume. See suspensions methods below.

followUps:

SessionFollowUps
Queue of messages submitted while a run is in progress. See follow-up methods below.

approval:

SessionApproval
The pending tool-approval gate. See approval methods below.

displayState:

SessionDisplayState
The canonical AgentControllerDisplayState snapshot a UI renders from. See display-state methods below.

state:

AgentControllerRequestState<TState>
The schema-validated, session-owned AgentController state. See state methods below.

browser:

MastraBrowser | undefined
The browser automation instance for this session. Set at creation via createSession, or from the AgentController config default. Undefined when no browser is configured.

Methods
Methods的直接链接

Identity and events
Identity and events的直接链接

getTags()
gettags的直接链接

Return a copy of the tags supplied when the session was created.

const tags = session.getTags()

Returns: Record<string, string>

subscribe(listener)
subscribelistener的直接链接

Subscribe to this session's isolated event bus. The method returns an unsubscribe function.

const unsubscribe = session.subscribe(event => {
console.log(event.type)
})

unsubscribe()

Returns: () => void

Messages and run control
Messages and run control的直接链接

sendMessage({ content, files?, requestContext?, author? })
sendmessage-content-files-requestcontext-author-的直接链接

Send a user message. The session creates a thread first when no thread is active. Pass author when the host has authenticated the sender.

await session.sendMessage({
content: 'Summarize this file.',
files: [{ data: fileContents, mediaType: 'text/plain', filename: 'notes.txt' }],
author: { id: 'user-123' },
})

The author persists under content.metadata.signal.author. Read it with getMessageAuthor(message) from @mastra/core/agent. When requestContext carries MASTRA_MESSAGE_AUTHOR_KEY, the session uses that value if author is omitted. See Reserved keys.

queueMessage({ content, files?, requestContext?, author? })
queuemessage-content-files-requestcontext-author-的直接链接

Queue a message for the next run, or send it immediately while the session is idle. Author metadata persists with the queued message.

queueSignal(signal, { requestContext?, tracingContext?, tracingOptions? }?)
queuesignalsignal--requestcontext-tracingcontext-tracingoptions-的直接链接

Queue a typed signal for the next run, or send it immediately while the session is idle. The signal keeps its type, tagName, and attributes. Unlike queueMessage(), the session doesn't stamp an author from requestContext; set signal.author to record one.

await session.queueSignal({
type: 'reactive',
tagName: 'skill',
attributes: { name: 'triage' },
contents: 'Triage the new issue.',
})

steer({ content, files?, requestContext?, author? })
steer-content-files-requestcontext-author-的直接链接

Abort the active run, then send content as a new message. Pass files to attach files in the same shape as sendMessage(): { data, mediaType, filename? }, where data is raw text or a base64 data URL for text files, and base64 for binary files.

await session.steer({ content: 'Focus on the failing tests.' })

followUp({ content, files?, requestContext? })
followup-content-files-requestcontext-的直接链接

Queue a follow-up while a run is active, or send it immediately while idle. Pass files to attach files the same way as steer().

await session.followUp({
content: 'Then compare against this screenshot.',
files: [{ data: screenshotBase64, mediaType: 'image/png', filename: 'expected.png' }],
})

getCurrentRunId()
getcurrentrunid的直接链接

Return the active stream run identifier, the tracked run identifier, or null while idle.

const runId = session.getCurrentRunId()

Returns: string | null

abort()
abort的直接链接

Abort the active run and clear pending suspension display state.

session.abort()

Workspace
Workspace的直接链接

getWorkspace()
getworkspace的直接链接

Return the workspace resolved for this session. This preserves session-level overrides and workspaces selected from the session scope.

A workspace is optional. When neither the session nor the AgentController configures one, the session still runs without filesystem or sandbox access, and this returns undefined. Check the result before using it.

const workspace = session.getWorkspace()
const skill = await workspace?.skills?.get('code-review')

Returns: Workspace | undefined

Session grants
Session grants的直接链接

Session-scoped grants auto-approve tools without prompting. Grants are ephemeral: they reset when the session restarts and are never persisted.

grantCategory(category)
grantcategorycategory的直接链接

Grant a tool category for the current session. Tools in this category are auto-approved.

session.grantCategory('edit')

grantTool(toolName)
granttooltoolname的直接链接

Grant a specific tool for the current session.

session.grantTool('mastra_workspace_execute_command')

getGrants()
getgrants的直接链接

Return the currently granted categories and tools.

const grants = session.getGrants()
// { categories: string[], tools: string[] }

hasCategoryGrant(category)
hascategorygrantcategory的直接链接

Return whether a category has an in-memory session grant.

const allowed = session.hasCategoryGrant('edit')

Returns: boolean

hasToolGrant(toolName)
hastoolgranttoolname的直接链接

Return whether a tool has an in-memory session grant.

const allowed = session.hasToolGrant('write_file')

Returns: boolean

Tool approvals
Tool approvals的直接链接

resolveToolApproval(toolName)
resolvetoolapprovaltoolname的直接链接

Return the effective policy after applying explicit tool rules, session grants, and category rules.

const policy = session.resolveToolApproval('execute_command')

Returns: PermissionPolicy

respondToToolApproval({ decision, toolCallId, requestContext?, declineContext? })
respondtotoolapproval-decision-toolcallid-requestcontext-declinecontext-的直接链接

Respond to a pending tool approval request, raised by a tool_approval_required event. toolCallId is required and identifies the parked call, so pass the toolCallId from the event. Pass always_allow_category to also grant the tool's whole category for the gate's thread.

session.respondToToolApproval({ decision: 'approve', toolCallId: event.toolCallId })
session.respondToToolApproval({ decision: 'decline', toolCallId: event.toolCallId })
session.respondToToolApproval({ decision: 'always_allow_category', toolCallId: event.toolCallId })

respondToToolSuspension({ resumeData, address?, toolCallId?, requestContext? })
respondtotoolsuspension-resumedata-address-toolcallid-requestcontext-的直接链接

Resume a suspended tool with application-provided data. Supply toolCallId for a prompt on the active thread. To resume after switching threads, retain the full address returned by session.claimToolSuspension(toolCallId, runId) and pass it instead of toolCallId.

An addressed resume uses the source thread's mode, model, and thread-owned preferences, without selecting that thread or replacing the active thread's subscription. Tool state callbacks remain bound to the source even if you switch threads during execution. Host-owned custom state and permission rules remain shared.

After a process restart, a full address can recover a persisted suspension through the controller's configured agents. Recovery requires a uniquely identified originating agent and the existing resume authorization checks. A setup failure leaves the prompt retryable; it doesn't finish another thread's active run.

await session.respondToToolSuspension({
toolCallId: event.toolCallId,
resumeData: ['src'],
})

For submit_plan, pass { action: 'approved' } or { action: 'rejected', feedback }. Approval can switch to the mode configured by transitionsTo before the tool resumes.

Token usage
Token usage的直接链接

getTokenUsage()
gettokenusage的直接链接

Return a copy of the running token-usage tally for the active thread.

The executing run saves token usage to its own thread as each step finishes, including runs resumed while another thread is selected. If a storage operation fails, the execution owner retains the measured usage and retries it with the next step. Sessions that only subscribe to a thread display its saved total without counting replayed steps again or saving usage.

const usage = session.getTokenUsage()
// { promptTokens, completionTokens, totalTokens, ... }

Identity
Identity的直接链接

session.identity identifies the process-local session host: the resource ID and session id, plus an ownerId. The id and ownerId are stable for the life of the host and don't change when the resource ID is switched. AgentController doesn't persist the live session or mirror these values to a SessionRecord.

session.identity.getId()
sessionidentitygetid的直接链接

Return the stable session identifier.

const sessionId = session.identity.getId()

session.identity.getOwnerId()
sessionidentitygetownerid的直接链接

Return the stable owner identifier for the process-local session host. This value isn't a durable thread owner.

const ownerId = session.identity.getOwnerId()

session.identity.getResourceId()
sessionidentitygetresourceid的直接链接

Return the current resource ID.

const resourceId = session.identity.getResourceId()

session.identity.getDefaultResourceId()
sessionidentitygetdefaultresourceid的直接链接

Return the resource ID the session was created with.

const defaultResourceId = session.identity.getDefaultResourceId()

To change the resource ID, use controller.setResourceId(), which also clears the active thread. The session id and ownerId aren't affected by resource switches.

Thread
Thread的直接链接

session.thread owns the active thread binding and resource-scoped thread operations. Stored threads and messages can survive controller recreation when storage is configured. Conversation settings such as mode, model, thinking level, token usage, pending runs, and ownership are restored from the active thread when the session creates or switches threads. The live session and its event bus don't persist.

New threads store the Session host's ownerId in the reserved metadata.ownerId and metadata.createdBy fields. Clones use the cloning Session host for both fields. Existing threads without metadata.ownerId read as owned by the current Session host without writing a backfill.

session.thread.create({ title?, id?, requestContext? })
sessionthreadcreate-title-id-requestcontext-的直接链接

Create a thread, bind the session to it, and open its event stream. Pass requestContext when memory is a function that reads the signed-in user.

For a session created with createInitialThread: false, explicit mode, model, and thread-preference selections made before its first binding seed the first new thread. This also applies to ensureId() and the first message. Failed creation leaves those selections available for retry.

After the session has bound a conversation, creating another thread uses controller defaults, not the previous conversation's choices. Clearing or deleting the binding doesn't restore the first-thread behavior. Opening an existing thread restores that thread's settings instead of applying unbound startup selections.

const thread = await session.thread.create({
id: 'thread-7',
title: 'Investigate login failure',
})

Returns: Promise<AgentControllerThread>

session.thread.rename({ title })
sessionthreadrename-title-的直接链接

Rename the active stored thread.

await session.thread.rename({ title: 'Fix login failure' })

session.thread.clone({ sourceThreadId?, title?, resourceId?, requestContext? })
sessionthreadclone-sourcethreadid-title-resourceid-requestcontext-的直接链接

Clone an owned thread and its messages, then bind the session to the clone. Pass requestContext when memory is a function that reads the signed-in user.

const clone = await session.thread.clone({
sourceThreadId: 'thread-7',
title: 'Alternative approach',
})

Returns: Promise<AgentControllerThread>

session.thread.switch({ threadId, emitEvent?, requestContext? })
sessionthreadswitch-threadid-emitevent-requestcontext-的直接链接

Switch to an owned stored thread and hydrate its mode, model, and observational memory settings. Pass requestContext when memory is a function that reads the signed-in user.

Switching away cancels a running local execution only when this session initiated it. A session listening to another session's run detaches without cancelling that run. This follows the current action's initiator, not ownership of the thread. Parked tool prompts remain available for later resume.

await session.thread.switch({ threadId: 'thread-8' })

session.thread.delete({ threadId, requestContext? })
sessionthreaddelete-threadid-requestcontext-的直接链接

Delete an owned thread. Deleting the active thread also clears the current binding. Pass requestContext when memory is a function that reads the signed-in user.

await session.thread.delete({ threadId: 'thread-8' })

session.thread.getId()
sessionthreadgetid的直接链接

Return the active thread ID, or null when no thread is bound.

const threadId = session.thread.getId()

session.thread.ensureId({ requestContext? })
sessionthreadensureid-requestcontext-的直接链接

Return the active thread ID, creating and binding a new thread when none is bound. Concurrent calls share one thread. Use it with sessions created with createInitialThread: false.

const threadId = await session.thread.ensureId()

session.thread.getOwner({ threadId? })
sessionthreadgetowner-threadid-的直接链接

Return a thread's durable owner. Omit threadId to read the active thread. The method returns undefined when no thread is selected. An owner-less legacy thread returns the Session host's ownerId without writing a backfill.

const ownerId = await session.thread.getOwner()

Returns: Promise<string | undefined>

session.thread.transferOwnership({ toOwnerId, threadId? })
sessionthreadtransferownership-toownerid-threadid-的直接链接

Store toOwnerId in the thread's metadata.ownerId and emit thread_owner_changed. The original metadata.createdBy value doesn't change. Without threadId, this transfers the active thread. With threadId, it transfers that thread of the session's resource and the active thread stays the same.

await session.thread.transferOwnership({
toOwnerId: 'user-456',
threadId: 'thread-123',
})

Returns: Promise<void>

Transfers are serialized within one process. Storage-backed applications that require a durable cross-process compare-and-set should perform it in their own store first, then call this method to mirror the result.

A successful change emits:

const event = {
type: 'thread_owner_changed',
threadId: 'thread-7',
fromOwnerId: 'user-123',
toOwnerId: 'user-456',
}

Runs snapshot ownership when they start. Within agent tools and dynamic runtime functions, AgentControllerRequestContext.threadOwnerId remains unchanged for the run's lifetime. A transfer becomes visible on the next run. AgentControllerRequestContext.session.ownerId continues to identify the process-local Session host.

session.thread.list(options?)
sessionthreadlistoptions的直接链接

List threads from storage. By default only threads for the current resource are returned, and transient forked subagent threads are hidden.

const threads = await session.thread.list()
const allThreads = await session.thread.list({ allResources: true })
const everything = await session.thread.list({ includeForkedSubagents: true })

session.thread.getById({ threadId })
sessionthreadgetbyid-threadid-的直接链接

Return a single thread by ID, or null if it doesn't exist.

const thread = await session.thread.getById({ threadId: 'thread-abc123' })

session.thread.listActiveMessages(options?)
sessionthreadlistactivemessagesoptions的直接链接

Retrieve messages for the active thread. Returns an empty array when no thread is bound.

const messages = await session.thread.listActiveMessages({ limit: 50 })

session.thread.listMessages({ threadId, limit? })
sessionthreadlistmessages-threadid-limit-的直接链接

Retrieve messages for a specific thread.

const messages = await session.thread.listMessages({ threadId: 'thread-abc123' })

The message-reading methods listActiveMessages, listMessages, and firstUserMessage return MastraDBMessage objects, while firstUserMessages returns a Map<string, MastraDBMessage> keyed by thread ID. Each message has a role, an id, a createdAt, and a content object with content.format and a content.parts array. Read text, reasoning, tool calls, and attachments from content.parts. Signals such as system reminders and notifications are returned as separate messages with role: 'signal'.

session.thread.firstUserMessage({ threadId })
sessionthreadfirstusermessage-threadid-的直接链接

Retrieve the first user message for a thread, or null if none.

const firstMsg = await session.thread.firstUserMessage({
threadId: 'thread-abc123',
})

session.thread.firstUserMessages({ threadIds })
sessionthreadfirstusermessages-threadids-的直接链接

Retrieve the first user message for many threads at once, returned as a map.

const firstByThread = await session.thread.firstUserMessages({
threadIds: ['thread-a', 'thread-b'],
})

session.thread.getSetting({ key })
sessionthreadgetsetting-key-的直接链接

Read a setting from the active thread metadata.

const value = await session.thread.getSetting({ key: 'omThreshold' })

session.thread.setSetting({ key, value })
sessionthreadsetsetting-key-value-的直接链接

Write a setting to the active thread metadata.

await session.thread.setSetting({ key: 'omThreshold', value: 0.8 })

session.thread.deleteSetting({ key })
sessionthreaddeletesetting-key-的直接链接

Remove a setting from the active thread metadata.

await session.thread.deleteSetting({ key: 'omThreshold' })

Mode
Mode的直接链接

session.mode owns the active mode selection.

session.mode.get()
sessionmodeget的直接链接

Return the active mode ID.

const modeId = session.mode.get()

session.mode.resolve()
sessionmoderesolve的直接链接

Return the full AgentControllerMode object for the active mode, resolved against the controller's configured modes.

const mode = session.mode.resolve()

session.mode.switch({ modeId })
sessionmodeswitch-modeid-的直接链接

Switch to another mode and persist the mode on the active thread. The session emits mode_changed. Switching modes doesn't change the active model; switch the model separately when your host assigns different models to different modes.

await session.mode.switch({ modeId: 'build' })

Model
Model的直接链接

session.model owns the session's single active model selection.

session.model.get()
sessionmodelget的直接链接

Return the active model ID.

const modelId = session.model.get()

session.model.displayName()
sessionmodeldisplayname的直接链接

Return the last segment of the active model ID as a short display name. Returns 'unknown' when no model is selected.

const name = session.model.displayName()

session.model.hasSelection()
sessionmodelhasselection的直接链接

Check whether a model is currently selected.

if (session.model.hasSelection()) {
// Ready to send messages
}

session.model.set({ modelId })
sessionmodelset-modelid-的直接链接

Set the active model in memory without persisting it, reporting model use, or emitting a model_changed event.

session.model.set({ modelId: 'anthropic/claude-sonnet-4-6' })

session.model.switch(modelId, options?)
sessionmodelswitchmodelid-options的直接链接

Switch the session's active model. The model ID is persisted as the thread's currentModelId and restored with that thread. Reports the selection to the controller's modelUseCountTracker and emits a model_changed event.

Pass { thinkingLevel } as the second argument to switch and persist both values in one operation. The level must be 'off', 'low', 'medium', 'high', 'xhigh', or 'max', and the controller's state schema must accept it. Both live values are updated before events are emitted. Omitting the option preserves the existing thinking-level override. Every model_changed event includes the current { modelId, thinkingLevel }, even when the thinking level is unchanged. The level is undefined when the session has no override.

await session.model.switch('anthropic/claude-sonnet-4-6', {
thinkingLevel: 'high',
})

The client SDK exposes the same operation as session.switchModel(modelId, { thinkingLevel }).

Observational Memory
Observational Memory的直接链接

The observational-memory model selection, grouped by role under session.om.observer and session.om.reflector. Both roles expose the same methods. Selection intent and the effective concrete model ID are separate values.

session.om.observer.model() / session.om.reflector.model()
sessionomobservermodel--sessionomreflectormodel的直接链接

Return the role's configured model.

const observerModel = session.om.observer.model()
const reflectorModel = session.om.reflector.model()

Returns: 'auto' | string | undefined

'auto' follows the active main model. Any other string is an explicit model ID.

session.om.observer.modelId() / session.om.reflector.modelId()
sessionomobservermodelid--sessionomreflectormodelid的直接链接

Return the role's effective concrete model ID. For an automatic selection, the controller calls omConfig.resolveAutoModelId with the role, current main model ID, and read-only session state. If that resolver returns no model or throws, the role uses its concrete omConfig fallback.

const observer = session.om.observer.modelId()
const reflector = session.om.reflector.modelId()

session.om.observer.threshold() / session.om.reflector.threshold()
sessionomobserverthreshold--sessionomreflectorthreshold的直接链接

Return the role's threshold in tokens (observation threshold for the observer, reflection threshold for the reflector), or undefined when unset.

const observationThreshold = session.om.observer.threshold()
const reflectionThreshold = session.om.reflector.threshold()

session.om.observer.switchModel({ modelId }) / session.om.reflector.switchModel({ modelId })
sessionomobserverswitchmodel-modelid---sessionomreflectorswitchmodel-modelid-的直接链接

Switch one role between automatic and explicit selection. Pass 'auto' to follow the active main model, or pass a concrete model ID to pin the role. Automatic selection clears the role's stale concrete model ID. The other role is unchanged. The emitted om_model_changed event continues to contain the effective concrete modelId.

await session.om.observer.switchModel({ modelId: 'auto' })
await session.om.reflector.switchModel({
modelId: 'anthropic/claude-haiku-4-5',
})

session.om.observer.resolvedModel() / session.om.reflector.resolvedModel()
sessionomobserverresolvedmodel--sessionomreflectorresolvedmodel的直接链接

Resolve the role's model ID to a model instance via the configured model gateways, or undefined when no model ID is set or no resolver is configured.

const observerModel = session.om.observer.resolvedModel()
const reflectorModel = session.om.reflector.resolvedModel()

Permissions
Permissions的直接链接

session.permissions owns the tool-approval policy represented in session.state: the per-category and per-tool rules consulted during approval resolution. These are distinct from the in-memory grants documented under Session grants. Grants reset with the live session. Permission rules aren't durable unless the host restores the corresponding session state.

session.permissions.getRules()
sessionpermissionsgetrules的直接链接

Return the current permission rules, or empty rules ({ categories: {}, tools: {} }) when none are set.

const rules = session.permissions.getRules()
// { categories: { execute: 'ask' }, tools: { dangerous_tool: 'deny' } }

session.permissions.setForCategory({ category, policy })
sessionpermissionssetforcategory-category-policy-的直接链接

Set the approval policy ('allow' | 'ask' | 'deny') for a tool category. Resolves once the change is persisted to session state.

await session.permissions.setForCategory({ category: 'execute', policy: 'ask' })

session.permissions.setForTool({ toolName, policy })
sessionpermissionssetfortool-toolname-policy-的直接链接

Set the approval policy for a specific tool. Per-tool policies take precedence over category policies. Resolves once persisted.

await session.permissions.setForTool({ toolName: 'dangerous_tool', policy: 'deny' })

Subagents
Subagents的直接链接

session.subagents owns subagent configuration. It currently exposes the subagent model selection under session.subagents.model.

session.subagents.model.get({ agentType? })
sessionsubagentsmodelget-agenttype-的直接链接

Return the subagent model ID, preferring the per-agentType value when one is given, then the global subagent model, or null when neither is set.

const modelId = session.subagents.model.get({ agentType: 'explore' })

session.subagents.model.set({ modelId, agentType? })
sessionsubagentsmodelset-modelid-agenttype-的直接链接

Set the subagent model ID. Pass an agentType to set a per-type override, or omit it to set the global default. Persists to thread settings and emits a subagent_model_changed event.

// Set the global subagent model
await session.subagents.model.set({ modelId: 'anthropic/claude-sonnet-4-6' })

// Set a per-type override
await session.subagents.model.set({
modelId: 'anthropic/claude-haiku-4-5',
agentType: 'explore',
})

Run
Run的直接链接

session.run owns run and trace identity plus abort state for the in-flight run.

session.run.getRunId() / getTraceId()
sessionrungetrunid--gettraceid的直接链接

Return the stored run ID and trace ID for the current run, or null when idle.

const runId = session.run.getRunId()
const traceId = session.run.getTraceId()

session.run.isRunning()
sessionrunisrunning的直接链接

Return whether a run is currently in progress.

if (session.run.isRunning()) {
// A run is active
}

Stream
Stream的直接链接

session.stream owns the live subscription to the agent thread stream and its dedup key.

session.stream.activeRunId()
sessionstreamactiverunid的直接链接

Return the run ID active on the live stream, or null when no stream is open.

const runId = session.stream.activeRunId()

session.stream.isActive()
sessionstreamisactive的直接链接

Return whether the stream currently has an active run.

if (session.stream.isActive()) {
// The current thread's stream is producing output
}

Suspensions
Suspensions的直接链接

session.suspensions owns parked interactive tool calls (such as ask_user and request_access) awaiting a resume.

session.suspensions.hasPending()
sessionsuspensionshaspending的直接链接

Return whether any tool is currently suspended.

if (session.suspensions.hasPending()) {
// At least one interactive tool is waiting for a response
}

session.suspensions.has({ toolCallId })
sessionsuspensionshas-toolcallid-的直接链接

Return whether a specific tool call is suspended.

const waiting = session.suspensions.has({ toolCallId: event.toolCallId })

Resume a suspended tool with session.respondToToolSuspension().

Follow-ups
Follow-ups的直接链接

session.followUps owns the FIFO queue of messages submitted while a run is in progress.

session.followUps.count()
sessionfollowupscount的直接链接

Return the number of queued follow-ups.

const queued = session.followUps.count()

session.followUps.isEmpty()
sessionfollowupsisempty的直接链接

Return whether the follow-up queue is empty.

if (!session.followUps.isEmpty()) {
// Messages are waiting to be processed
}

Approval
Approval的直接链接

session.approval owns the pending tool-approval gate.

session.approval.isArmed()
sessionapprovalisarmed的直接链接

Return whether a tool is currently awaiting an approval decision.

if (session.approval.isArmed()) {
// Show the approval prompt
}

Respond with session.respondToToolApproval().

Display state
Display state的直接链接

session.displayState owns the canonical AgentControllerDisplayState snapshot a UI renders from, and the reducer that keeps it in sync with every session event.

session.displayState.get()
sessiondisplaystateget的直接链接

Return the current AgentControllerDisplayState snapshot for UI rendering.

const displayState = session.displayState.get()

session.displayState.restoreTasks(tasks)
sessiondisplaystaterestoretaskstasks的直接链接

Restore the task portion of the snapshot after a UI replays persisted task tool history. This is a pure update of the snapshot and doesn't emit an event, so re-render explicitly after calling it.

session.displayState.restoreTasks(replayedTasks)

After every event, the session emits display_state_changed with the latest snapshot. Subscribe with session.subscribe() or read the current value from session.displayState.get().

State
State的直接链接

session.state exposes the active conversation's thread-owned preferences together with host-owned state. Updates are serialized and validated against the stateSchema passed to the AgentController. Visible state changes emit state_changed.

Thread-owned preferences include thinking level, observational memory settings, and subagent model selections. Switching threads leaves host-owned custom fields and permission rules unchanged and doesn't persist them.

A queued write keeps its source thread through asynchronous updates and validation. Switching from A to B while a write is pending doesn't apply A's preferences to B. Host-field updates still affect the host. A retained run and the active view of the same thread share update ordering. A resumed run sees edits made while it was parked. Events without a source address that affect the conversation display are emitted only while that source is active.

Inside a tool's controller request context, state is a snapshot taken when the context was built. Use getState() or session.state.get() to read the latest source-thread state after a write.

session.state.get()
sessionstateget的直接链接

Return a readonly copy of the current state snapshot.

const state = session.state.get()

session.state.set(updates)
sessionstatesetupdates的直接链接

Merge a partial update into the state. The queue applies concurrent calls in order and validates them against the schema before emitting state_changed with the changed keys.

await session.state.set({ yolo: true })

session.state.update(updater)
sessionstateupdateupdater的直接链接

Run an updater against the current snapshot and apply its result atomically within the write queue. Use this for read-modify-write changes that must see the latest state. The updater returns mergeable updates and optional events, plus the result value that update() resolves to.

const added = await session.state.update(current => ({
updates: { count: (current.count ?? 0) + 1 },
result: (current.count ?? 0) + 1,
}))

Persistence boundaries
Persistence boundaries的直接链接

A Session is a live runtime object. Its event bus, arbitrary custom state, permission rules, permission grants, follow-ups, and active stream don't automatically survive controller or process recreation. The host must restore the host-owned state it needs.

Configured storage persists threads and messages along with token usage, while thread settings restore mode, model, and supported preferences. Restored settings can include thinking level, observational memory, and subagent model selections with per-agent-type overrides. Persisted suspended runs can be recovered using their full suspension address and originating agent; this doesn't make arbitrary custom state durable.

While a run retains a source-thread view, successful in-memory preference writes remain available when returning to that thread even if best-effort metadata persistence fails. A cold restart can recover only persisted settings and controller defaults.

A chat channel can map back to stored threads. However, AgentControllerChannels keeps channel-to-session and auto-approval state in memory.