Skip to main content

Session class

alpha

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

A Session owns all the state tied to a single conversation. The Harness is the shared host — agents, storage, config, the thread lock, and the event bus — while the Session holds everything that is per-conversation: identity, the active thread binding and reads, mode and model selection, run and abort state, the live agent stream, tool suspensions, follow-ups, approvals, permission grants, token usage, and the display-state snapshot.

Access the session through harness.session.

For a conceptual introduction, see the Harness overview.

Usage example
Direct link to Usage example

// Read per-conversation state through harness.session
const modeId = harness.session.mode.get()
const modelId = harness.session.model.get()
const threadId = harness.session.thread.getId()
const grants = harness.session.getGrants()

// Render from the coalesced display-state snapshot
harness.subscribe(event => {
if (event.type === 'display_state_changed') {
render(harness.session.displayState.get())
}
})

Properties
Direct link to Properties

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

identity:

SessionIdentity
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.

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 HarnessDisplayState snapshot a UI renders from. See display-state methods below.

state:

SessionState<TState>
The schema-validated, session-owned Harness state. See state methods below.

Methods
Direct link to Methods

Permissions
Direct link to Permissions

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

grantCategory(category)
Direct link to grantcategorycategory

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

harness.session.grantCategory('edit')

grantTool(toolName)
Direct link to granttooltoolname

Grant a specific tool for the current session.

harness.session.grantTool('mastra_workspace_execute_command')

getGrants()
Direct link to getgrants

Return the currently granted categories and tools.

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

Tool approvals
Direct link to Tool approvals

respondToToolApproval({ decision, requestContext? })
Direct link to respondtotoolapproval-decision-requestcontext-

Respond to a pending tool approval request, raised by a tool_approval_required event. Pass always_allow_category to also grant the tool's whole category for the rest of the session.

harness.session.respondToToolApproval({ decision: 'approve' })
harness.session.respondToToolApproval({ decision: 'decline' })
harness.session.respondToToolApproval({ decision: 'always_allow_category' })

Run control
Direct link to Run control

getCurrentRunId()
Direct link to getcurrentrunid

Return the run ID of the in-flight run, or null when idle. Prefers the live stream's active run, falling back to the last stored run ID.

const runId = harness.session.getCurrentRunId()

abortRun()
Direct link to abortrun

Abort the in-flight run: aborts the live stream, requests abort on the run, and clears parked tool suspensions.

harness.session.abortRun()

Token usage
Direct link to Token usage

getTokenUsage()
Direct link to gettokenusage

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

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

Identity
Direct link to Identity

session.identity owns the resource ID for the conversation.

session.identity.getResourceId()
Direct link to sessionidentitygetresourceid

Return the current resource ID.

const resourceId = harness.session.identity.getResourceId()

session.identity.getDefaultResourceId()
Direct link to sessionidentitygetdefaultresourceid

Return the resource ID the session was created with.

const defaultResourceId = harness.session.identity.getDefaultResourceId()

To change the resource ID, use harness.setResourceId(), which also tears down the active thread.

Thread
Direct link to Thread

session.thread owns the active thread binding plus thread and message reads. Thread lifecycle (create, switch, clone, delete, rename) lives on the Harness because it coordinates the shared thread lock and event bus.

session.thread.getId()
Direct link to sessionthreadgetid

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

const threadId = harness.session.thread.getId()

session.thread.list(options?)
Direct link to 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 harness.session.thread.list()
const allThreads = await harness.session.thread.list({ allResources: true })
const everything = await harness.session.thread.list({ includeForkedSubagents: true })

session.thread.getById({ threadId })
Direct link to sessionthreadgetbyid-threadid-

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

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

session.thread.listActiveMessages(options?)
Direct link to sessionthreadlistactivemessagesoptions

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

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

session.thread.listMessages({ threadId, limit? })
Direct link to sessionthreadlistmessages-threadid-limit-

Retrieve messages for a specific thread.

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

session.thread.firstUserMessage({ threadId })
Direct link to sessionthreadfirstusermessage-threadid-

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

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

session.thread.firstUserMessages({ threadIds })
Direct link to sessionthreadfirstusermessages-threadids-

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

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

session.thread.getSetting({ key }) / setSetting({ key, value }) / deleteSetting({ key })
Direct link to sessionthreadgetsetting-key---setsetting-key-value---deletesetting-key-

Read, write, and remove per-thread settings stored on the active thread's metadata.

await harness.session.thread.setSetting({ key: 'omThreshold', value: 0.8 })
const value = await harness.session.thread.getSetting({ key: 'omThreshold' })
await harness.session.thread.deleteSetting({ key: 'omThreshold' })

Mode
Direct link to Mode

session.mode owns the active mode selection. Switching modes is performed on the Harness because it emits events and rebinds the stream.

session.mode.get()
Direct link to sessionmodeget

Return the active mode ID.

const modeId = harness.session.mode.get()

session.mode.resolve()
Direct link to sessionmoderesolve

Return the full HarnessMode object for the active mode, resolved against the harness's configured modes.

const mode = harness.session.mode.resolve()

Model
Direct link to Model

session.model owns the active model selection, including per-mode model memory. Switching models is performed on the Harness.

session.model.get()
Direct link to sessionmodelget

Return the active model ID.

const modelId = harness.session.model.get()

session.model.hasSelection()
Direct link to sessionmodelhasselection

Check whether a model is currently selected.

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

Run
Direct link to Run

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

session.run.getRunId() / getTraceId()
Direct link to sessionrungetrunid--gettraceid

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

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

session.run.isRunning()
Direct link to sessionrunisrunning

Return whether a run is currently in progress.

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

Stream
Direct link to Stream

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

session.stream.activeRunId()
Direct link to sessionstreamactiverunid

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

const runId = harness.session.stream.activeRunId()

session.stream.isActive()
Direct link to sessionstreamisactive

Return whether the stream currently has an active run.

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

Suspensions
Direct link to Suspensions

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

session.suspensions.hasPending()
Direct link to sessionsuspensionshaspending

Return whether any tool is currently suspended.

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

session.suspensions.has({ toolCallId })
Direct link to sessionsuspensionshas-toolcallid-

Return whether a specific tool call is suspended.

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

Resume a suspended tool with harness.respondToToolSuspension().

Follow-ups
Direct link to Follow-ups

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

session.followUps.count()
Direct link to sessionfollowupscount

Return the number of queued follow-ups.

const queued = harness.session.followUps.count()

session.followUps.isEmpty()
Direct link to sessionfollowupsisempty

Return whether the follow-up queue is empty.

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

Approval
Direct link to Approval

session.approval owns the pending tool-approval gate.

session.approval.isArmed()
Direct link to sessionapprovalisarmed

Return whether a tool is currently awaiting an approval decision.

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

Respond with session.respondToToolApproval().

Display state
Direct link to Display state

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

session.displayState.get()
Direct link to sessiondisplaystateget

Return the current HarnessDisplayState snapshot for UI rendering.

const displayState = harness.session.displayState.get()

session.displayState.restoreTasks(tasks)
Direct link to 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 does not emit an event, so re-render explicitly after calling it.

harness.session.displayState.restoreTasks(replayedTasks)

After every event the harness emits display_state_changed, so high-frequency events such as message_update, tool_update, and tool_input_delta are coalesced into the next snapshot. Subscribe with harness.subscribe() and read the latest snapshot from session.displayState.get().

State
Direct link to State

session.state owns the schema-validated Harness state for the conversation. It holds the current snapshot, validates updates against the stateSchema passed to the Harness, serializes concurrent writes, and emits a state_changed event on every change.

session.state.get()
Direct link to sessionstateget

Return a readonly copy of the current state snapshot.

const state = harness.session.state.get()

session.state.set(updates)
Direct link to sessionstatesetupdates

Merge a partial update into the state. Updates are queued so concurrent calls apply in order, validated against the schema, and emit state_changed with the changed keys.

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

session.state.update(updater)
Direct link to 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 updates to merge, optional events to emit, and a result value that update() resolves to.

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